Compare commits

..
Author SHA1 Message Date
stumpylog 4d98be3b47 This is redundant now 2026-10-13 10:04:35 -07:00
stumpylog 6322a0bad3 Adds the given/when/then and some additional cleanups of pytest style 2026-10-13 10:04:35 -07:00
stumpylog 70eab32503 Validates pages ahead of time in build_pdfs rather than during the building 2026-10-13 10:04:35 -07:00
stumpylog 4f91865826 Now that I'm sure it works, re-work these tests to mock pdf_ops, rather than pikepdf 2026-10-13 10:04:35 -07:00
stumpylog a5c3e74b18 Drops tests we don't relly need + adds the typing annotation 2026-10-13 10:04:35 -07:00
Trenton H c06827acdc Refactor: add pure pdf_ops module with real-PDF tests 2026-10-13 10:04:35 -07:00
Trenton H 6e717817c6 Fix: make exporter derived file names unique when originals differ only by extension (#14429) 2026-10-12 15:14:08 -07:00
shamoon 3e0fa73771 Chore: adjust issue auto-triage 2026-10-12 14:16:45 -07:00
github-actions[bot]andshamoon 2b00cf6a95 Documentation: Add v3.3.1 changelog (#14427)
* Changelog v3.3.1 - GHA

* Update changelog for paperless-ngx 3.3.1

Removed MARIADB_HOST documentation from MariaDB compose files.

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2026-10-12 13:22:46 -07:00
shamoon c4d1b0c732 Bump version to 3.3.1 2026-10-12 13:01:03 -07:00
shamoon 333cbf0ae6 Merge branch 'dev' 2026-10-12 13:00:15 -07:00
github-actions[bot]andCrowdin Bot 49a298894e New Crowdin translations by GitHub Action (#14368)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-10-12 12:59:46 -07:00
GitHub Actions 551ebad6f1 Auto translate strings 2026-10-12 19:13:03 +00:00
Trenton H 4171ecf166 Chore: share the authorizing owner rule between the id sets and the trash filter (#14425)
The trash list needs to show a version to whoever owns its root document, but
it deliberately shows only owned and unowned documents and ignores explicit
shares, so it cannot use permitted_object_ids. #14384 gave it its own
filter_queryset with a hand-built condition through the root, which describes
the same rule as the annotations in permitted_object_ids a second time.

Move those annotations into annotate_authorizing_fields. PermittedObjectsFilter
gains a parent_field attribute that its owner-only and granted paths both use,
and the trash filter shrinks to setting include_granted and parent_field.
2026-10-12 12:11:42 -07:00
GitHub Actions 8289dd0bae Auto translate strings 2026-10-12 18:12:14 +00:00
Trenton H b195423f1d Fix: use a root document's effective content for the LLM index and pr… (#14423)
* Fix: use a root document's effective content for the LLM index and prompts

The search index and the document views use the newest version's content as a
root document's content, but everything the LLM side reads used the root's own
content: the text embedded in the LLM index, the content in the classification
prompt, the context blocks from similar documents, and the text that is
searched for similar documents. After a document was replaced by a new
version the LLM kept answering from the old text.

Read get_effective_content() in those four places. The index update annotates
the content in the query like the search index does, and the similar-document
lookup annotates it for the documents it fetches, so no query is made per
document. Entries already in the LLM index keep the old text until the
document next changes or the index is rebuilt.

* Chore: test the LLM query text and bulk annotation use effective content

* Removes the query count testing.  It's flakey from local to CI and workers, doesn't really add much imo
2026-10-12 11:10:50 -07:00
Trenton H 9f2f809131 Fix: index document versions as their root document (#14422)
* Fix: index document versions as their root document

The search index and the LLM index hold root documents only, a root being
indexed with its newest version's content. Every write path therefore had to
remember to hand them the root. Several did not.

WriteBatch.add_or_update, WriteBatch.add_or_update_ids and
llm_index_add_or_update_document now resolve a version to its root themselves.
The reindex command and update_llm_index only walk root documents, and an
incremental LLM update is scoped to the roots of the given ids through
versioning.root_document_ids, which add_or_update_ids shares. more_like_id
returns the root's id for a version. The reprocess task no longer picks the root
for the indexes and only still clears the caches of both documents.

* Fix: rebuild old search indexes and look versions up as their root for similar documents

An index built before versions were indexed as their root can still hold
version entries under their own id and metadata, and only a rebuild clears
them. Bump the schema version so the index is rebuilt on upgrade.

more_like_this_ids looked the given id up in the index as is, so asking for
documents like a version, as the AI suggestions endpoint can, silently found
nothing. It now looks the root up instead.
2026-10-12 11:10:50 -07:00
GitHub Actions 96535991a3 Auto translate strings 2026-10-12 17:18:18 +00:00
Trenton H c2deb69ac5 Fix: authorize document versions by their root in the single-object permission check (#14421)
* Fix: authorize document versions by their root in the single-object permission check

has_perms_owner_aware judged a document by its own owner and grants, so each
endpoint that fetches a document itself had to remember to map a version to
its root document first, and one that forgot, like the more-like-this search
filter, authorized by a stale version owner.

The check now maps a Document to its root before looking at the owner and the
guardian grants, matching what permitted_document_ids does for id sets. The
eight call sites that mapped the document themselves pass it straight through.
The DRF object permission class needs no change because the document viewset
only ever serves root documents.
2026-10-12 17:17:00 +00:00
GitHub Actions 070f614929 Auto translate strings 2026-10-12 17:06:18 +00:00
Trenton H 6f496f741b Chore: New helper for JSON parsing during settings, to provide a better error message to users (#14419) 2026-10-12 17:04:56 +00:00
Sven Lankes 1fb463b55c Chore: silence gotenberg startup warning (#14405) 2026-10-12 16:54:47 +00:00
shamoon 9a136ecb62 Chore: drop PR size labeling 2026-10-12 09:41:48 -07:00
shamoon 192ddbe45e Documentation: tweak demo hosting attribution 2026-10-12 09:18:19 -07:00
shamoon aac5e49034 Merge branch 'main' into dev 2026-10-12 09:13:04 -07:00
shamoon 716d310eb3 Documentation: credit Fyra Stack for hosting the demo (#14399) 2026-10-12 09:10:40 -07:00
dependabot[bot] 71d2edb055 Chore(deps): Bump the npm_and_yarn group across 1 directory with 2 updates (#14417)
Bumps the npm_and_yarn group with 2 updates in the /src/paperless_mail/templates directory: [source-map-js](https://github.com/7rulnik/source-map-js) and [postcss-selector-parser](https://github.com/postcss/postcss-selector-parser).


Updates `source-map-js` from 1.2.1 to 1.2.2
- [Release notes](https://github.com/7rulnik/source-map-js/releases)
- [Changelog](https://github.com/7rulnik/source-map-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/7rulnik/source-map-js/compare/v1.2.1...v1.2.2)

Removes `postcss-selector-parser`

---
updated-dependencies:
- dependency-name: source-map-js
  dependency-version: 1.2.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: postcss-selector-parser
  dependency-version:
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-12 06:52:11 -07:00
shamoon 973af89f61 Chore: update frontend TS libs to ES2022 2026-10-11 15:39:56 -07:00
GitHub Actions 937bf2fa7a Auto translate strings 2026-10-10 22:05:15 +00:00
Trenton HandClaude Sonnet 5.5 2f2381578c Fix: authorize document versions by their root and speed up permission id sets (#14396)
* Fix: authorize document versions by their root and speed up permission id sets

permitted_document_ids judged a version by its own owner and grants, so a
version whose owner had drifted from its root's was visible to the wrong
people and hidden from the right ones. Callers patched this individually by
mapping each document to its root first. The query itself was also slow on
MariaDB: the guardian grants were a UNION cast to integers and tested with
IN inside an OR with the owner checks, which MariaDB cannot materialize, so it
re-scans the user's grants for every document. At 20k documents that took
seconds for a user with a couple of hundred grants.

permitted_object_ids now casts the row key to a string, as guardian stores
object_pk, and tests it against a single uncorrelated UNION ALL of the user's
and groups' grants. No integer cast is needed and the user's groups are
matched with an IN subquery rather than a join through the membership table.
Postgres and SQLite build the grant set once, and MariaDB probes guardian's
unique indexes per row, which is cheap. A correlated EXISTS per grant also
fixed MariaDB but was up to 3x slower than before on Postgres and SQLite.

permitted_object_ids also takes an optional parent_field naming a
self-referencing foreign key whose target authorizes the row, and
permitted_document_ids passes root_document, so a version is visible exactly
when its root is. The helper that mapped documents to their roots at the call
sites is no longer needed, so the email, selection data, share link bundle,
trash, bulk download and bulk edit checks use the id set directly.

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-10 15:03:52 -07:00
shamoon e606dbffcd Chore: update the db options warning (#14398) 2026-10-10 07:17:54 -07:00
shamoon 138160cbda Fix: bulk reprocess latest version for root documents (#14385) 2026-10-08 18:50:44 +00:00
GitHub Actions 6d960c11d5 Auto translate strings 2026-10-08 18:40:22 +00:00
shamoon a35bd6e238 Fix: consistently use root doc for version action permissions (#14384) 2026-10-08 18:39:05 +00:00
Trenton H 474c630aa4 Fix: Don't attempt to index created dates which are not representable inside the FTS index (#14390) 2026-10-08 08:36:35 -07:00
shamoon f8876c55e3 Documentation: remove MARIADB_HOST from MariaDB compose files (#14378) 2026-10-07 07:32:37 -07:00
Trenton H e5b215f9d3 Chore: Updates the troubleshooting with 2 common database collation things (#14370) 2026-10-06 11:02:42 -07:00
github-actions[bot]andshamoon 94f10b8622 Documentation: Add v3.3.0 changelog (#14362)
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2026-10-05 21:14:19 -07:00
shamoon 6f7ed1d4a7 Bump version to 3.3.0 2026-10-05 20:34:24 -07:00
shamoon 0428bf6955 Merge branch 'dev' 2026-10-05 20:33:36 -07:00
shamoon c63afb47b2 Documentation: correct duplicates info (#14243) 2026-09-23 08:27:18 -07:00
130 changed files with 6513 additions and 5804 deletions

No files matched your search

@@ -66,7 +66,7 @@ services:
command: command:
- "gotenberg" - "gotenberg"
- "--chromium-disable-javascript=true" - "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*" - "--chromium-allow-list=^file:///tmp/.*"
tika: tika:
image: docker.io/apache/tika:3.3.1.0 image: docker.io/apache/tika:3.3.1.0
restart: unless-stopped restart: unless-stopped
+11 -1
View File
@@ -31,9 +31,19 @@ body:
value: | value: |
#### 🤖 Use of AI tools #### 🤖 Use of AI tools
If this report was generated, in whole or in part, by an AI tool or agent you **must** clearly say so in the description below. Failing to clearly disclose this is a violation of our [Code of Conduct](https://github.com/paperless-ngx/paperless-ngx/blob/main/CODE_OF_CONDUCT.md). If this report was generated, in whole or in part, by an AI tool or agent you **must** say so using the field below. Failing to clearly disclose this is a violation of our [Code of Conduct](https://github.com/paperless-ngx/paperless-ngx/blob/main/CODE_OF_CONDUCT.md).
Reports written by an AI agent must describe the _observed behavior only_: what happened, how to reproduce it, logs etc. Do **not** include code analysis, speculation about the root cause, or suggest code fixes. Reports written by an AI agent must describe the _observed behavior only_: what happened, how to reproduce it, logs etc. Do **not** include code analysis, speculation about the root cause, or suggest code fixes.
- type: dropdown
id: ai-usage
attributes:
label: AI usage
description: Was any part of this report written by an AI tool or agent?
options:
- No AI tools were used
- Yes, AI tools helped write this report
validations:
required: true
- type: textarea - type: textarea
id: description id: description
attributes: attributes:
+21 -5
View File
@@ -43,6 +43,12 @@ jobs:
const haystack = `${issue.title}\n${issue.body ?? ''}`; const haystack = `${issue.title}\n${issue.body ?? ''}`;
const honeypot = haystack.toUpperCase().includes('ASLOP-PR-VERIFY'); const honeypot = haystack.toUpperCase().includes('ASLOP-PR-VERIFY');
// The form's required "AI usage" dropdown renders as a `### AI usage` section. An agent
// that follows the template trips the honeypot even when it discloses, so only treat
// the honeypot as undisclosed AI use when the reporter did not answer "Yes" here.
const aiUsage = (issue.body ?? '').match(/^###\s+AI usage\s*\n+\s*(.+?)\s*$/m)?.[1] ?? '';
const disclosedAI = aiUsage.toLowerCase().startsWith('yes');
// Issues opened through the form always get the template's default labels. GitHub // Issues opened through the form always get the template's default labels. GitHub
// applies them a second or two *after* creation, so re-read them instead of trusting // applies them a second or two *after* creation, so re-read them instead of trusting
// the webhook payload, and retry before concluding that there are none. // the webhook payload, and retry before concluding that there are none.
@@ -55,19 +61,29 @@ jobs:
if (labels.some((label) => templateLabels.includes(label))) break; if (labels.some((label) => templateLabels.includes(label))) break;
} }
const bypassedTemplate = !labels.some((label) => templateLabels.includes(label)); const bypassedTemplate = !labels.some((label) => templateLabels.includes(label));
core.info(`Labels: [${labels.join(', ')}], honeypot: ${honeypot}, bypassed template: ${bypassedTemplate}`); core.info(
`Labels: [${labels.join(', ')}], honeypot: ${honeypot}, AI usage: "${aiUsage}", ` +
`bypassed template: ${bypassedTemplate}`
);
if (!honeypot && !bypassedTemplate) { if (!bypassedTemplate && (disclosedAI || !honeypot)) {
core.info('No slop signals found'); if (disclosedAI) {
core.info('AI use disclosed, labeling only');
await github.rest.issues.addLabels({ ...common, labels: ['ai'] });
} else {
core.info('No slop signals found');
}
return; return;
} }
// The honeypot is only ever tripped deliberately, so that message can name the cause. // Reaching here with the honeypot means AI usage was not disclosed in the form, so that
// message can name the cause.
// A missing template label only tells us the issue did not come from the form, which // A missing template label only tells us the issue did not come from the form, which
// is reason enough to close it, but not proof of how it was written. // is reason enough to close it, but not proof of how it was written.
const body = honeypot const body = honeypot
? 'This issue was automatically closed because it contains a marker that is only visible to ' + ? 'This issue was automatically closed because it contains a marker that is only visible to ' +
'automated tools, which indicates it was generated by an AI agent without being disclosed as such.\n\n' + 'automated tools, which indicates it was generated by an AI agent, but the "AI usage" field does not ' +
'disclose this.\n\n' +
`Please see our [contributing guidelines](${contributing}) and [Code of Conduct](${codeOfConduct}). ` + `Please see our [contributing guidelines](${contributing}) and [Code of Conduct](${codeOfConduct}). ` +
'You are welcome to open a new issue that describes the problem you observed in your own words.' 'You are welcome to open a new issue that describes the problem you observed in your own words.'
: 'This issue was automatically closed because it was not opened using our bug report form. ' + : 'This issue was automatically closed because it was not opened using our bug report form. ' +
-10
View File
@@ -38,16 +38,6 @@ jobs:
uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0 uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Label by size
uses: Gascon1/pr-size-labeler@deff8ed00a76639a7c0f197525bafa3350ba4c36 # v1.3.0
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
xs_label: 'small-change'
xs_diff: '9'
s_label: 'non-trivial'
s_diff: '99999'
fail_if_xl: 'false'
excluded_files: /\.lock$/ /\.txt$/ ^src-ui/pnpm-lock\.yaml$ ^src-ui/messages\.xlf$ ^src/locale/en_US/LC_MESSAGES/django\.po$
- name: Label by PR title - name: Label by PR title
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with: with:
-1
View File
@@ -38,7 +38,6 @@ src/documents/bulk_edit.py:0: error: Incompatible types in assignment (expressio
src/documents/bulk_edit.py:0: error: Invalid index type "str" for "dict[FieldDataType, str]"; expected type "FieldDataType" [index] src/documents/bulk_edit.py:0: error: Invalid index type "str" for "dict[FieldDataType, str]"; expected type "FieldDataType" [index]
src/documents/bulk_edit.py:0: error: List comprehension has incompatible type List[tuple[int, Any]]; expected List[int] [misc] src/documents/bulk_edit.py:0: error: List comprehension has incompatible type List[tuple[int, Any]]; expected List[int] [misc]
src/documents/bulk_edit.py:0: error: List comprehension has incompatible type List[tuple[int, None]]; expected List[int] [misc] src/documents/bulk_edit.py:0: error: List comprehension has incompatible type List[tuple[int, None]]; expected List[int] [misc]
src/documents/bulk_edit.py:0: error: Missing named argument "p" for "remove" of "PageList" [call-arg]
src/documents/bulk_edit.py:0: error: Missing type arguments for generic type "dict" [type-arg] src/documents/bulk_edit.py:0: error: Missing type arguments for generic type "dict" [type-arg]
src/documents/bulk_edit.py:0: error: Missing type arguments for generic type "dict" [type-arg] src/documents/bulk_edit.py:0: error: Missing type arguments for generic type "dict" [type-arg]
src/documents/bulk_edit.py:0: error: Need type annotation for "to_create" (hint: "to_create: list[<type>] = ...") [var-annotated] src/documents/bulk_edit.py:0: error: Need type annotation for "to_create" (hint: "to_create: list[<type>] = ...") [var-annotated]
-7
View File
@@ -91,13 +91,6 @@
"concise_description": "Argument `list[int]` is not assignable to parameter `args` with type `tuple[Any, ...] | None` in function `celery.app.task.Task.apply_async`", "concise_description": "Argument `list[int]` is not assignable to parameter `args` with type `tuple[Any, ...] | None` in function `celery.app.task.Task.apply_async`",
"severity": "error" "severity": "error"
}, },
{
"column": 33,
"path": "src/documents/bulk_edit.py",
"name": "missing-argument",
"concise_description": "Missing argument `p` in function `pikepdf._core.PageList.remove`",
"severity": "error"
},
{ {
"column": 25, "column": 25,
"path": "src/documents/caching.py", "path": "src/documents/caching.py",
+4 -13
View File
@@ -43,26 +43,17 @@ Once you have submitted a **P**ull **R**equest it will be reviewed, approved, an
Important: Pull requests that implement a new feature or enhancement _should almost always target an existing feature request_ with evidence of community interest and discussion. This is in order to balance the work of implementing and maintaining new features / enhancements. Instead of opening a PR which does not meet this requirement, please open a feature request instead, to gather feedback from both users and the project maintainers. Important: Pull requests that implement a new feature or enhancement _should almost always target an existing feature request_ with evidence of community interest and discussion. This is in order to balance the work of implementing and maintaining new features / enhancements. Instead of opening a PR which does not meet this requirement, please open a feature request instead, to gather feedback from both users and the project maintainers.
## Non-Trivial Requests ## PR Review Process
PRs deemed `non-trivial` will go through a stricter review process before being merged into `dev`. This is to ensure code quality and complete functionality (free of side effects). Our community review process for PRs is the following:
Examples of `non-trivial` PRs might include:
- Additional features
- Large changes to many distinct files
- Breaking or deprecation of existing features
Our community review process for `non-trivial` PRs is the following:
1. Must pass usual automated code tests and formatting checks. 1. Must pass usual automated code tests and formatting checks.
2. The PR will be assigned and pinged to the appropriately experienced team (i.e. @paperless-ngx/backend for backend changes). 2. The PR will be assigned and pinged to the appropriately experienced team (i.e. @paperless-ngx/backend for backend changes).
3. Development team will check and test code manually (possibly over several days). 3. Development team will check and test code manually (possibly over several days).
- You may be asked to make changes or rebase. - You may be asked to make changes or rebase.
- The team may ask for additional testing done by @paperless-ngx/test 4. One or more members of the team will approve and finally merge the request into `dev` 🎉.
4. **At least two** members of the team will approve and finally merge the request into `dev` 🎉.
This process might be slow as community members have different schedules and time to dedicate to the Paperless project. However it ensures community code reviews are as brilliantly thorough as they once were with @jonaswinkler. This process might be slow as community members have different schedules and time to dedicate to the Paperless project. However it ensures community code reviews are thorough and of high quality.
# Use of AI Tools # Use of AI Tools
+4 -8
View File
@@ -21,7 +21,7 @@ Paperless-ngx is a document management system that transforms your physical docu
Paperless-ngx is the official successor to the original [Paperless](https://github.com/the-paperless-project/paperless) & [Paperless-ng](https://github.com/jonaswinkler/paperless-ng) projects and is designed to distribute the responsibility of advancing and supporting the project among a team of people. [Consider joining us!](#community-support) Paperless-ngx is the official successor to the original [Paperless](https://github.com/the-paperless-project/paperless) & [Paperless-ng](https://github.com/jonaswinkler/paperless-ng) projects and is designed to distribute the responsibility of advancing and supporting the project among a team of people. [Consider joining us!](#community-support)
Thanks to the generous folks at [DigitalOcean](https://m.do.co/c/8d70b916d462), a demo is available at [demo.paperless-ngx.com](https://demo.paperless-ngx.com) using login `demo` / `demo`. _Note: demo content is reset frequently and confidential information should not be uploaded._ Thanks to the generous folks at [Fyra Stack](https://fyrastack.com), a demo is available at [demo.paperless-ngx.com](https://demo.paperless-ngx.com) using login `demo` / `demo`. _Note: demo content is reset frequently and confidential information should not be uploaded._
- [Features](#features) - [Features](#features)
- [Getting started](#getting-started) - [Getting started](#getting-started)
@@ -33,13 +33,9 @@ Thanks to the generous folks at [DigitalOcean](https://m.do.co/c/8d70b916d462),
- [Related Projects](#related-projects) - [Related Projects](#related-projects)
- [Important Note](#important-note) - [Important Note](#important-note)
<p align="right">This project is supported by:<br/> <p align="right">Demo hosting provided by:<br/>
<a href="https://m.do.co/c/8d70b916d462" style="padding-top: 4px; display: block;"> <a href="https://fyrastack.com">
<picture> <img src="docs/assets/sponsors/fyra-stack.svg" alt="" width="28px" align="absmiddle"> Fyra Stack
<source media="(prefers-color-scheme: dark)" srcset="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_white.svg" width="140px">
<source media="(prefers-color-scheme: light)" srcset="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="140px">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_black_.svg" width="140px">
</picture>
</a> </a>
</p> </p>
+1 -1
View File
@@ -14,7 +14,7 @@ services:
command: command:
- "gotenberg" - "gotenberg"
- "--chromium-disable-javascript=true" - "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*" - "--chromium-allow-list=^file:///tmp/.*"
- "--log-level=warn" - "--log-level=warn"
- "--log-format=text" - "--log-format=text"
tika: tika:
@@ -40,7 +40,6 @@ services:
volumes: volumes:
- dbdata:/var/lib/mysql - dbdata:/var/lib/mysql
environment: environment:
MARIADB_HOST: paperless
MARIADB_DATABASE: paperless MARIADB_DATABASE: paperless
MARIADB_USER: paperless MARIADB_USER: paperless
MARIADB_PASSWORD: paperless MARIADB_PASSWORD: paperless
@@ -79,7 +78,7 @@ services:
command: command:
- "gotenberg" - "gotenberg"
- "--chromium-disable-javascript=true" - "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*" - "--chromium-allow-list=^file:///tmp/.*"
tika: tika:
image: docker.io/apache/tika:3.3.1.0 image: docker.io/apache/tika:3.3.1.0
restart: unless-stopped restart: unless-stopped
@@ -36,7 +36,6 @@ services:
volumes: volumes:
- dbdata:/var/lib/mysql - dbdata:/var/lib/mysql
environment: environment:
MARIADB_HOST: paperless
MARIADB_DATABASE: paperless MARIADB_DATABASE: paperless
MARIADB_USER: paperless MARIADB_USER: paperless
MARIADB_PASSWORD: paperless MARIADB_PASSWORD: paperless
@@ -74,7 +74,7 @@ services:
command: command:
- "gotenberg" - "gotenberg"
- "--chromium-disable-javascript=true" - "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*" - "--chromium-allow-list=^file:///tmp/.*"
tika: tika:
image: docker.io/apache/tika:3.3.1.0 image: docker.io/apache/tika:3.3.1.0
restart: unless-stopped restart: unless-stopped
@@ -63,7 +63,7 @@ services:
command: command:
- "gotenberg" - "gotenberg"
- "--chromium-disable-javascript=true" - "--chromium-disable-javascript=true"
- "--chromium-allow-list=file:///tmp/.*" - "--chromium-allow-list=^file:///tmp/.*"
tika: tika:
image: docker.io/apache/tika:3.3.1.0 image: docker.io/apache/tika:3.3.1.0
restart: unless-stopped restart: unless-stopped
+29
View File
@@ -101,3 +101,32 @@ h4 code {
border-top: .05rem solid var(--md-default-fg-color--lightest); border-top: .05rem solid var(--md-default-fg-color--lightest);
} }
} }
.md-typeset .demo-sponsor {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: .35rem;
margin-top: -1rem;
margin-bottom: 2rem;
color: var(--md-default-fg-color--light);
font-size: .65rem;
}
.md-typeset .demo-sponsor a {
display: inline-flex;
align-items: center;
gap: .25rem;
color: inherit;
font-weight: 600;
}
.md-typeset .demo-sponsor a:hover {
color: var(--md-accent-fg-color);
}
.md-typeset .demo-sponsor img {
width: 1rem;
height: 1rem;
flex-shrink: 0;
}
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="666.667" height="666.667" viewBox="0 0 666.667 666.667"><g stroke-width="1.333"><path fill="#fef400" d="m332.66668 418.66668h-84.66667v-85.33334-85.33333h84.66667 84.66667v-85.33334-85.333334h85.33333 85.33334v85.333334 85.33334h-85.33334-85.33333v85.33333 85.33334z"/><path fill="#fe6565" d="m248.00001 589.33335h-170.666674v-170.66667-170.66667h85.333334 85.33334v-85.33334-85.333334h84.66667 84.66667v85.333334 85.33334h85.33333 85.33334v85.33333 85.33334h-84.66667-84.66667v85.33334 85.33333zm84.66667-170.66667h84.66667v-85.33334-85.33333h-84.66667-84.66667v85.33333 85.33334z"/></g></svg>

After

Width:  |  Height:  |  Size: 641 B

+174
View File
@@ -1,5 +1,179 @@
# Changelog # Changelog
## paperless-ngx 3.3.1
### Bug Fixes
- Fix: use a root document's effective content for the LLM index and pr… [@stumpylog](https://github.com/stumpylog) ([#14423](https://github.com/paperless-ngx/paperless-ngx/pull/14423))
- Fix: index document versions as their root document [@stumpylog](https://github.com/stumpylog) ([#14422](https://github.com/paperless-ngx/paperless-ngx/pull/14422))
- Fix: authorize document versions by their root in the single-object permission check [@stumpylog](https://github.com/stumpylog) ([#14421](https://github.com/paperless-ngx/paperless-ngx/pull/14421))
- Fix: Provide better error messages to users for invalid JSON [@stumpylog](https://github.com/stumpylog) ([#14419](https://github.com/paperless-ngx/paperless-ngx/pull/14419))
- Fix: authorize document versions by their root and speed up permission id sets [@stumpylog](https://github.com/stumpylog) ([#14396](https://github.com/paperless-ngx/paperless-ngx/pull/14396))
- Chore: update the db options warning [@shamoon](https://github.com/shamoon) ([#14398](https://github.com/paperless-ngx/paperless-ngx/pull/14398))
- Fix: bulk reprocess latest version for root documents [@shamoon](https://github.com/shamoon) ([#14385](https://github.com/paperless-ngx/paperless-ngx/pull/14385))
- Fix: consistently use root doc for version action permissions [@shamoon](https://github.com/shamoon) ([#14384](https://github.com/paperless-ngx/paperless-ngx/pull/14384))
- Fix: Don't attempt to index created dates which are not representable in the FTS index [@stumpylog](https://github.com/stumpylog) ([#14390](https://github.com/paperless-ngx/paperless-ngx/pull/14390))
- Fix: retry a search index rebuild that was interrupted [@stumpylog](https://github.com/stumpylog) ([#14379](https://github.com/paperless-ngx/paperless-ngx/pull/14379))
### Documentation
- Chore: silence gotenberg startup warning [@slankes](https://github.com/slankes) ([#14405](https://github.com/paperless-ngx/paperless-ngx/pull/14405))
- Documentation: remove MARIADB\_HOST from MariaDB compose files [@shamoon](https://github.com/shamoon) ([#14378](https://github.com/paperless-ngx/paperless-ngx/pull/14378))
- Documentation: Updates the troubleshooting with 2 common database collation issues [@stumpylog](https://github.com/stumpylog) ([#14370](https://github.com/paperless-ngx/paperless-ngx/pull/14370))
### Maintenance
- Chore: Upgrade runners to Ubuntu 26.04 [@stumpylog](https://github.com/stumpylog) ([#14347](https://github.com/paperless-ngx/paperless-ngx/pull/14347))
### Dependencies
- Chore(deps): Bump the npm\_and\_yarn group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14417](https://github.com/paperless-ngx/paperless-ngx/pull/14417))
### All App Changes
<details>
<summary>12 changes</summary>
- Chore: share the authorizing owner rule between the id sets and the trash filter [@stumpylog](https://github.com/stumpylog) ([#14425](https://github.com/paperless-ngx/paperless-ngx/pull/14425))
- Fix: use a root document's effective content for the LLM index and pr… [@stumpylog](https://github.com/stumpylog) ([#14423](https://github.com/paperless-ngx/paperless-ngx/pull/14423))
- Fix: index document versions as their root document [@stumpylog](https://github.com/stumpylog) ([#14422](https://github.com/paperless-ngx/paperless-ngx/pull/14422))
- Fix: authorize document versions by their root in the single-object permission check [@stumpylog](https://github.com/stumpylog) ([#14421](https://github.com/paperless-ngx/paperless-ngx/pull/14421))
- Fix: Provide better error messages to users for invalid JSON [@stumpylog](https://github.com/stumpylog) ([#14419](https://github.com/paperless-ngx/paperless-ngx/pull/14419))
- Chore(deps): Bump the npm\_and\_yarn group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14417](https://github.com/paperless-ngx/paperless-ngx/pull/14417))
- Fix: authorize document versions by their root and speed up permission id sets [@stumpylog](https://github.com/stumpylog) ([#14396](https://github.com/paperless-ngx/paperless-ngx/pull/14396))
- Chore: update the db options warning [@shamoon](https://github.com/shamoon) ([#14398](https://github.com/paperless-ngx/paperless-ngx/pull/14398))
- Fix: bulk reprocess latest version for root documents [@shamoon](https://github.com/shamoon) ([#14385](https://github.com/paperless-ngx/paperless-ngx/pull/14385))
- Fix: consistently use root doc for version action permissions [@shamoon](https://github.com/shamoon) ([#14384](https://github.com/paperless-ngx/paperless-ngx/pull/14384))
- Fix: Don't attempt to index created dates which are not representable in the FTS index [@stumpylog](https://github.com/stumpylog) ([#14390](https://github.com/paperless-ngx/paperless-ngx/pull/14390))
- Fix: retry a search index rebuild that was interrupted [@stumpylog](https://github.com/stumpylog) ([#14379](https://github.com/paperless-ngx/paperless-ngx/pull/14379))
</details>
## paperless-ngx 3.3.0
### Features / Enhancements
- Enhancement: more control over suggestion requests [@shamoon](https://github.com/shamoon) ([#14258](https://github.com/paperless-ngx/paperless-ngx/pull/14258))
- Chorehancement: set manifest CORS for credentials [@shamoon](https://github.com/shamoon) ([#14307](https://github.com/paperless-ngx/paperless-ngx/pull/14307))
- Enhancement: include Django admin with 2FA [@shamoon](https://github.com/shamoon) ([#14270](https://github.com/paperless-ngx/paperless-ngx/pull/14270))
- Feature: propagate resolved secrets to interactive container shells [@stumpylog](https://github.com/stumpylog) ([#14254](https://github.com/paperless-ngx/paperless-ngx/pull/14254))
- Enhancement: support separate embedding API key [@furkanural](https://github.com/furkanural) ([#14067](https://github.com/paperless-ngx/paperless-ngx/pull/14067))
- Enhancement: support passthrough extra params for LLMs [@shamoon](https://github.com/shamoon) ([#14202](https://github.com/paperless-ngx/paperless-ngx/pull/14202))
- Feature: store barcode contents, list and search them [@jurassicparkicecream](https://github.com/jurassicparkicecream) ([#14276](https://github.com/paperless-ngx/paperless-ngx/pull/14276))
### Bug Fixes
- Fix: Set the ProcessedMail owner based on the rule owner in all cases [@stumpylog](https://github.com/stumpylog) ([#14356](https://github.com/paperless-ngx/paperless-ngx/pull/14356))
- Fix: Ensure log rotation settings are converted to integers [@stumpylog](https://github.com/stumpylog) ([#14343](https://github.com/paperless-ngx/paperless-ngx/pull/14343))
- Fix: Wrap apt calls into a retry so we can ideally jump a slow mirror [@stumpylog](https://github.com/stumpylog) ([#14344](https://github.com/paperless-ngx/paperless-ngx/pull/14344))
- Fix: ship pdf.js CMaps so CJK documents render in the viewer [@MrOggy85](https://github.com/MrOggy85) ([#14318](https://github.com/paperless-ngx/paperless-ngx/pull/14318))
- Fix: use version page\_count for versioned document [@shamoon](https://github.com/shamoon) ([#14280](https://github.com/paperless-ngx/paperless-ngx/pull/14280))
- Fix: allow pointer events for pdf links in pngx viewer [@shamoon](https://github.com/shamoon) ([#14264](https://github.com/paperless-ngx/paperless-ngx/pull/14264))
- Fix: During a move to the trash directory, only attempt to copy metadata [@stumpylog](https://github.com/stumpylog) ([#14250](https://github.com/paperless-ngx/paperless-ngx/pull/14250))
- Fix: convert file mtime to the configured time zone directly [@stumpylog](https://github.com/stumpylog) ([#14249](https://github.com/paperless-ngx/paperless-ngx/pull/14249))
- Fix: ensure documentDeleted subscription is discarded [@shamoon](https://github.com/shamoon) ([#14247](https://github.com/paperless-ngx/paperless-ngx/pull/14247))
- Chore: Fix bugs in the test suite [@stumpylog](https://github.com/stumpylog) ([#14244](https://github.com/paperless-ngx/paperless-ngx/pull/14244))
- Fix: ensure bulk operations are checked against version root [@shamoon](https://github.com/shamoon) ([#14246](https://github.com/paperless-ngx/paperless-ngx/pull/14246))
- Fix: indexing after document-added workflows signal [@shamoon](https://github.com/shamoon) ([#14242](https://github.com/paperless-ngx/paperless-ngx/pull/14242))
- Fix: Record full tag and custom field lists in bulk edit audit log [@stumpylog](https://github.com/stumpylog) ([#14236](https://github.com/paperless-ngx/paperless-ngx/pull/14236))
- Chore: update pikepdf for ocrmypdf requirement [@shamoon](https://github.com/shamoon) ([#14235](https://github.com/paperless-ngx/paperless-ngx/pull/14235))
- Fix: handle legacy bulk edit split page range with missing page\_count [@shamoon](https://github.com/shamoon) ([#14212](https://github.com/paperless-ngx/paperless-ngx/pull/14212))
- Fix: ignore invalid EXIF orientation when generating image archives [@zhzy0077](https://github.com/zhzy0077) ([#14203](https://github.com/paperless-ngx/paperless-ngx/pull/14203))
### Documentation
- Documentation: correct duplicates info [@shamoon](https://github.com/shamoon) ([#14243](https://github.com/paperless-ngx/paperless-ngx/pull/14243))
### Maintenance
- Chore(deps): Bump the actions group across 1 directory with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14332](https://github.com/paperless-ngx/paperless-ngx/pull/14332))
- Fix: Wrap apt calls into a retry so we can ideally jump a slow mirror [@stumpylog](https://github.com/stumpylog) ([#14344](https://github.com/paperless-ngx/paperless-ngx/pull/14344))
- Chore(deps): Bump the actions group across 1 directory with 10 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14301](https://github.com/paperless-ngx/paperless-ngx/pull/14301))
### Dependencies
<details>
<summary>27 changes</summary>
- Chore(deps): Bump django-filter from 25.2 to 26.1 @[dependabot[bot]](https://github.com/apps/dependabot) ([#14337](https://github.com/paperless-ngx/paperless-ngx/pull/14337))
- Chore(deps): Bump the utilities-patch group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14338](https://github.com/paperless-ngx/paperless-ngx/pull/14338))
- Chore(deps): Bump the pre-commit-dependencies group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14351](https://github.com/paperless-ngx/paperless-ngx/pull/14351))
- Chore(deps-dev): Bump types-channels from 4.3.0.20260408 to 4.3.0.20260518 @[dependabot[bot]](https://github.com/apps/dependabot) ([#14335](https://github.com/paperless-ngx/paperless-ngx/pull/14335))
- docker(deps): Bump astral-sh/uv from 0.12.20-python3.14-trixie-slim to 0.12.23-python3.14-trixie-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#14327](https://github.com/paperless-ngx/paperless-ngx/pull/14327))
- Chore(deps): Bump the actions group across 1 directory with 4 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14332](https://github.com/paperless-ngx/paperless-ngx/pull/14332))
- Chore(deps): Bump the uv group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14325](https://github.com/paperless-ngx/paperless-ngx/pull/14325))
- Chore(deps): Bump the frontend-angular-dependencies group across 1 directory with 13 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14329](https://github.com/paperless-ngx/paperless-ngx/pull/14329))
- Chore(deps-dev): Bump prettier from 3.9.8 to 3.9.9 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14331](https://github.com/paperless-ngx/paperless-ngx/pull/14331))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14330](https://github.com/paperless-ngx/paperless-ngx/pull/14330))
- Chore(deps-dev): Bump zensical from 0.0.64 to 0.0.65 in the development group @[dependabot[bot]](https://github.com/apps/dependabot) ([#14326](https://github.com/paperless-ngx/paperless-ngx/pull/14326))
- Chore(deps): Bump the uv group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14314](https://github.com/paperless-ngx/paperless-ngx/pull/14314))
- Chore(deps): Bump the utilities-minor group across 1 directory with 7 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14305](https://github.com/paperless-ngx/paperless-ngx/pull/14305))
- docker-compose(deps): bump greenmail/standalone from 2.1.13 to 2.1.14 in /docker/compose @[dependabot[bot]](https://github.com/apps/dependabot) ([#14281](https://github.com/paperless-ngx/paperless-ngx/pull/14281))
- docker(deps): Bump astral-sh/uv from 0.12.16-python3.14-trixie-slim to 0.12.20-python3.14-trixie-slim @[dependabot[bot]](https://github.com/apps/dependabot) ([#14282](https://github.com/paperless-ngx/paperless-ngx/pull/14282))
- Chore(deps): Bump the pre-commit-dependencies group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14283](https://github.com/paperless-ngx/paperless-ngx/pull/14283))
- Chore(deps): Bump the utilities-patch group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14297](https://github.com/paperless-ngx/paperless-ngx/pull/14297))
- Chore(deps): Bump the actions group across 1 directory with 10 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14301](https://github.com/paperless-ngx/paperless-ngx/pull/14301))
- Chore(deps-dev): Bump the frontend-jest-dependencies group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14286](https://github.com/paperless-ngx/paperless-ngx/pull/14286))
- Chore(deps-dev): Bump eslint from 10.10.0 to 10.11.0 in /src-ui in the frontend-eslint-dependencies group across 1 directory @[dependabot[bot]](https://github.com/apps/dependabot) ([#14287](https://github.com/paperless-ngx/paperless-ngx/pull/14287))
- Chore(deps-dev): Bump @types/node from 26.5.0 to 26.6.2 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14288](https://github.com/paperless-ngx/paperless-ngx/pull/14288))
- Chore(deps-dev): Bump prettier from 3.9.6 to 3.9.8 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14289](https://github.com/paperless-ngx/paperless-ngx/pull/14289))
- Chore(deps): Bump the frontend-angular-dependencies group across 1 directory with 10 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14285](https://github.com/paperless-ngx/paperless-ngx/pull/14285))
- Chore: replace bleach with turbohtml [@gaborbernat](https://github.com/gaborbernat) ([#14269](https://github.com/paperless-ngx/paperless-ngx/pull/14269))
- Chore(deps): Bump autobahn from 25.12.2 to 26.7.1 in the uv group across 1 directory @[dependabot[bot]](https://github.com/apps/dependabot) ([#14231](https://github.com/paperless-ngx/paperless-ngx/pull/14231))
- Chore: update pikepdf for ocrmypdf requirement [@shamoon](https://github.com/shamoon) ([#14235](https://github.com/paperless-ngx/paperless-ngx/pull/14235))
- Chore(deps): Bump the pre-commit-dependencies group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14133](https://github.com/paperless-ngx/paperless-ngx/pull/14133))
</details>
### All App Changes
<details>
<summary>41 changes</summary>
- Feature: store barcode contents, list and search them [@jurassicparkicecream](https://github.com/jurassicparkicecream) ([#14276](https://github.com/paperless-ngx/paperless-ngx/pull/14276))
- Fix: Set the ProcessedMail owner based on the rule owner in all cases [@stumpylog](https://github.com/stumpylog) ([#14356](https://github.com/paperless-ngx/paperless-ngx/pull/14356))
- Chore(deps): Bump django-filter from 25.2 to 26.1 @[dependabot[bot]](https://github.com/apps/dependabot) ([#14337](https://github.com/paperless-ngx/paperless-ngx/pull/14337))
- Chore(deps): Bump the utilities-patch group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14338](https://github.com/paperless-ngx/paperless-ngx/pull/14338))
- Chore(deps-dev): Bump types-channels from 4.3.0.20260408 to 4.3.0.20260518 @[dependabot[bot]](https://github.com/apps/dependabot) ([#14335](https://github.com/paperless-ngx/paperless-ngx/pull/14335))
- Chore(deps): Bump the uv group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14325](https://github.com/paperless-ngx/paperless-ngx/pull/14325))
- Fix: Ensure log rotation settings are converted to integers [@stumpylog](https://github.com/stumpylog) ([#14343](https://github.com/paperless-ngx/paperless-ngx/pull/14343))
- Chore(deps): Bump the frontend-angular-dependencies group across 1 directory with 13 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14329](https://github.com/paperless-ngx/paperless-ngx/pull/14329))
- Chore(deps-dev): Bump prettier from 3.9.8 to 3.9.9 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14331](https://github.com/paperless-ngx/paperless-ngx/pull/14331))
- Chore(deps-dev): Bump the frontend-eslint-dependencies group across 1 directory with 3 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14330](https://github.com/paperless-ngx/paperless-ngx/pull/14330))
- Fix: ship pdf.js CMaps so CJK documents render in the viewer [@MrOggy85](https://github.com/MrOggy85) ([#14318](https://github.com/paperless-ngx/paperless-ngx/pull/14318))
- Chore(deps-dev): Bump zensical from 0.0.64 to 0.0.65 in the development group @[dependabot[bot]](https://github.com/apps/dependabot) ([#14326](https://github.com/paperless-ngx/paperless-ngx/pull/14326))
- Enhancement: more control over suggestion requests [@shamoon](https://github.com/shamoon) ([#14258](https://github.com/paperless-ngx/paperless-ngx/pull/14258))
- Chore(deps): Bump the uv group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14314](https://github.com/paperless-ngx/paperless-ngx/pull/14314))
- Chore: anchor admin url pattern [@shamoon](https://github.com/shamoon) ([#14316](https://github.com/paperless-ngx/paperless-ngx/pull/14316))
- Chorehancement: set manifest CORS for credentials [@shamoon](https://github.com/shamoon) ([#14307](https://github.com/paperless-ngx/paperless-ngx/pull/14307))
- Chore(deps): Bump the utilities-minor group across 1 directory with 7 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14305](https://github.com/paperless-ngx/paperless-ngx/pull/14305))
- Chore(deps): Bump the utilities-patch group across 1 directory with 6 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14297](https://github.com/paperless-ngx/paperless-ngx/pull/14297))
- Chore(deps-dev): Bump the frontend-jest-dependencies group across 1 directory with 2 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14286](https://github.com/paperless-ngx/paperless-ngx/pull/14286))
- Chore(deps-dev): Bump eslint from 10.10.0 to 10.11.0 in /src-ui in the frontend-eslint-dependencies group across 1 directory @[dependabot[bot]](https://github.com/apps/dependabot) ([#14287](https://github.com/paperless-ngx/paperless-ngx/pull/14287))
- Chore(deps-dev): Bump @types/node from 26.5.0 to 26.6.2 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14288](https://github.com/paperless-ngx/paperless-ngx/pull/14288))
- Chore(deps-dev): Bump prettier from 3.9.6 to 3.9.8 in /src-ui @[dependabot[bot]](https://github.com/apps/dependabot) ([#14289](https://github.com/paperless-ngx/paperless-ngx/pull/14289))
- Chore(deps): Bump the frontend-angular-dependencies group across 1 directory with 10 updates @[dependabot[bot]](https://github.com/apps/dependabot) ([#14285](https://github.com/paperless-ngx/paperless-ngx/pull/14285))
- Fix: use version page\_count for versioned document [@shamoon](https://github.com/shamoon) ([#14280](https://github.com/paperless-ngx/paperless-ngx/pull/14280))
- Chore: replace bleach with turbohtml [@gaborbernat](https://github.com/gaborbernat) ([#14269](https://github.com/paperless-ngx/paperless-ngx/pull/14269))
- Enhancement: include Django admin with 2FA [@shamoon](https://github.com/shamoon) ([#14270](https://github.com/paperless-ngx/paperless-ngx/pull/14270))
- Fix: allow pointer events for pdf links in pngx viewer [@shamoon](https://github.com/shamoon) ([#14264](https://github.com/paperless-ngx/paperless-ngx/pull/14264))
- Feature: propagate resolved secrets to interactive container shells [@stumpylog](https://github.com/stumpylog) ([#14254](https://github.com/paperless-ngx/paperless-ngx/pull/14254))
- Fix: During a move to the trash directory, only attempt to copy metadata [@stumpylog](https://github.com/stumpylog) ([#14250](https://github.com/paperless-ngx/paperless-ngx/pull/14250))
- Fix: convert file mtime to the configured time zone directly [@stumpylog](https://github.com/stumpylog) ([#14249](https://github.com/paperless-ngx/paperless-ngx/pull/14249))
- Fix: ensure documentDeleted subscription is discarded [@shamoon](https://github.com/shamoon) ([#14247](https://github.com/paperless-ngx/paperless-ngx/pull/14247))
- Chore: Fix bugs in the test suite [@stumpylog](https://github.com/stumpylog) ([#14244](https://github.com/paperless-ngx/paperless-ngx/pull/14244))
- Fix: ensure bulk operations are checked against version root [@shamoon](https://github.com/shamoon) ([#14246](https://github.com/paperless-ngx/paperless-ngx/pull/14246))
- Enhancement: support separate embedding API key [@furkanural](https://github.com/furkanural) ([#14067](https://github.com/paperless-ngx/paperless-ngx/pull/14067))
- Enhancement: support passthrough extra params for LLMs [@shamoon](https://github.com/shamoon) ([#14202](https://github.com/paperless-ngx/paperless-ngx/pull/14202))
- Chore(deps): Bump autobahn from 25.12.2 to 26.7.1 in the uv group across 1 directory @[dependabot[bot]](https://github.com/apps/dependabot) ([#14231](https://github.com/paperless-ngx/paperless-ngx/pull/14231))
- Fix: indexing after document-added workflows signal [@shamoon](https://github.com/shamoon) ([#14242](https://github.com/paperless-ngx/paperless-ngx/pull/14242))
- Fix: Record full tag and custom field lists in bulk edit audit log [@stumpylog](https://github.com/stumpylog) ([#14236](https://github.com/paperless-ngx/paperless-ngx/pull/14236))
- Chore: update pikepdf for ocrmypdf requirement [@shamoon](https://github.com/shamoon) ([#14235](https://github.com/paperless-ngx/paperless-ngx/pull/14235))
- Fix: handle legacy bulk edit split page range with missing page\_count [@shamoon](https://github.com/shamoon) ([#14212](https://github.com/paperless-ngx/paperless-ngx/pull/14212))
- Fix: ignore invalid EXIF orientation when generating image archives [@zhzy0077](https://github.com/zhzy0077) ([#14203](https://github.com/paperless-ngx/paperless-ngx/pull/14203))
</details>
## paperless-ngx 3.2.1 ## paperless-ngx 3.2.1
### Bug Fixes ### Bug Fixes
+3 -1
View File
@@ -76,7 +76,9 @@ is not supported by any of the available parsers.
**A:** Not by default. As of v3, a file whose contents match an existing document is still **A:** Not by default. As of v3, a file whose contents match an existing document is still
consumed, and the duplicate is flagged in the UI — open the document and check the consumed, and the duplicate is flagged in the UI — open the document and check the
**Duplicates** tab to review documents that share the same content. If you prefer the old **Duplicates** tab to review documents that share the same content, or filter the document
list by **Duplicates** to find all of them (see
[Duplicate documents](usage.md#duplicate-documents)). If you prefer the old
behavior of rejecting duplicates during consumption, set behavior of rejecting duplicates during consumption, set
[`PAPERLESS_CONSUMER_DELETE_DUPLICATES`](configuration.md#PAPERLESS_CONSUMER_DELETE_DUPLICATES) [`PAPERLESS_CONSUMER_DELETE_DUPLICATES`](configuration.md#PAPERLESS_CONSUMER_DELETE_DUPLICATES)
to `true`. to `true`.
+4 -4
View File
@@ -12,10 +12,10 @@ physical documents into a searchable online archive so you can keep, well, _less
[Get started](setup.md){ .md-button .md-button--primary .index-callout } [Get started](setup.md){ .md-button .md-button--primary .index-callout }
[Demo](https://demo.paperless-ngx.com){ .md-button .md-button--secondary target=\_blank } [Demo](https://demo.paperless-ngx.com){ .md-button .md-button--secondary target=\_blank }
<div style="display: flex; justify-content: end; margin-top: -1.5rem;"> <div class="demo-sponsor">
<a href="https://m.do.co/c/8d70b916d462" target="_blank"> <span>Demo hosting by</span>
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/PoweredByDO/DO_Powered_by_Badge_white.svg#only-dark" class="no-lightbox" width="150px"> <a href="https://fyrastack.com" target="_blank" rel="noopener">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/PoweredByDO/DO_Powered_by_Badge_black.svg#only-light" class="no-lightbox" width="150px"> <img src="assets/sponsors/fyra-stack.svg" alt="" class="no-lightbox" width="20" height="20"> Fyra Stack
</a> </a>
</div> </div>
+60 -1
View File
@@ -129,7 +129,7 @@ change in the `docker-compose.yml` file:
command: command:
- 'gotenberg' - 'gotenberg'
- '--chromium-disable-javascript=true' - '--chromium-disable-javascript=true'
- '--chromium-allow-list=file:///tmp/.*' - '--chromium-allow-list=^file:///tmp/.*'
- '--api-timeout=60s' - '--api-timeout=60s'
``` ```
@@ -272,6 +272,65 @@ This error can occur in installations which have upgraded from a version of Pape
$ python3 manage.py convert_mariadb_uuid $ python3 manage.py convert_mariadb_uuid
``` ```
## MariaDB/MySQL error "Illegal mix of collations"
Consumption or other operations fail with an error like:
```
(1267, "Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and
(utf8mb4_unicode_ci,IMPLICIT) for operation '='")
```
This happens when the tables in your database do not all use the same
collation. It is most often seen on databases that existed before a
MariaDB/MySQL upgrade: older tables keep their original collation, while
tables created afterwards use the new server default.
To work around it, set the collation your existing tables use (the one in the error
that is not `utf8mb4_unicode_ci`) with
[`PAPERLESS_DB_OPTIONS`](configuration.md#PAPERLESS_DB_OPTIONS):
```bash
PAPERLESS_DB_OPTIONS="collation=utf8mb4_general_ci"
```
To fix it permanently, back up your database, then convert the database and
each table to a single collation and remove the override:
```sql
ALTER DATABASE paperless CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
## PostgreSQL warns about a "collation version mismatch"
The PostgreSQL log shows a warning like:
```
WARNING: database "paperless" has a collation version mismatch
DETAIL: The database was created using collation version 2.36, but the operating system provides version 2.41.
HINT: Rebuild all objects in this database that use the default collation and run ALTER DATABASE paperless REFRESH COLLATION VERSION, or build PostgreSQL with the right library version.
```
This comes from PostgreSQL, not Paperless-ngx. The `glibc` version that PostgreSQL
runs against changed, and the existing database was created with an older one. With
Docker, `glibc` comes from the PostgreSQL image's Debian base, not the host, so this
commonly happens when a new image is pulled after its base Debian release changed.
If PostgreSQL is installed directly on a host, it uses the host's `glibc` instead.
The warning is not an error and Paperless-ngx keeps working, but the database's text
indexes may be built with outdated sorting rules. To resolve it, back up your
database, then connect to it (for example with `psql -U paperless -d paperless`
inside the database container) and run:
```sql
REINDEX DATABASE paperless;
ALTER DATABASE paperless REFRESH COLLATION VERSION;
```
To avoid this in the future, pin your PostgreSQL image to a specific Debian release,
for example `postgres:18-trixie`, rather than `postgres:18`.
## Platform-Specific Deployment Troubleshooting ## Platform-Specific Deployment Troubleshooting
A user-maintained wiki page is available to help troubleshoot issues that may arise when trying to deploy Paperless-ngx on specific platforms, for example SELinux. Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Platform%E2%80%90Specific-Troubleshooting). A user-maintained wiki page is available to help troubleshoot issues that may arise when trying to deploy Paperless-ngx on specific platforms, for example SELinux. Please see [the wiki](https://github.com/paperless-ngx/paperless-ngx/wiki/Platform%E2%80%90Specific-Troubleshooting).
+7 -8
View File
@@ -299,19 +299,18 @@ for details.
### Duplicate documents ### Duplicate documents
By default, Paperless-ngx **does not reject duplicates**. If you consume a file whose By default, Paperless-ngx **does not reject duplicates**. If you consume a file whose
contents exactly match an existing document (same checksum), the new copy is still contents match an existing document (same original or archive checksum), the new copy is
consumed and a warning is logged. The task entry for the upload also flags that a still consumed and a warning is logged.
duplicate was detected and links to the existing document(s).
To review duplicates, open a document and switch to the **Duplicates** tab on the When a document has duplicates, a **Duplicates** tab appears on its detail page, listing
document detail page. It lists other documents that share the same content, including any the other documents you can view that share the same content (including any in the trash).
that are in the trash (shown with a badge), and links to each so you can decide which to To find all documents with duplicates, choose **Duplicates** in the document list's text
keep. filter dropdown, or use `has_duplicates=true` in the REST API.
If you would rather reject duplicates at consumption time (the pre-v3 behavior), set If you would rather reject duplicates at consumption time (the pre-v3 behavior), set
[`PAPERLESS_CONSUMER_DELETE_DUPLICATES`](configuration.md#PAPERLESS_CONSUMER_DELETE_DUPLICATES) [`PAPERLESS_CONSUMER_DELETE_DUPLICATES`](configuration.md#PAPERLESS_CONSUMER_DELETE_DUPLICATES)
to `true`. The duplicate file is then deleted instead of consumed, and the task fails with to `true`. The duplicate file is then deleted instead of consumed, and the task fails with
a "document already exists" message. a "Document already exists" message linking to the existing document.
## Document Suggestions ## Document Suggestions
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "paperless-ngx" name = "paperless-ngx"
version = "3.2.1" version = "3.3.1"
description = """\ description = """\
A community-supported supercharged document management system: scan, index and archive all your physical documents\ A community-supported supercharged document management system: scan, index and archive all your physical documents\
""" """
+1 -1
View File
@@ -2,5 +2,5 @@
docker run -p 5432:5432 -e POSTGRES_PASSWORD=password -v paperless_pgdata:/var/lib/postgresql/data -d postgres:15 docker run -p 5432:5432 -e POSTGRES_PASSWORD=password -v paperless_pgdata:/var/lib/postgresql/data -d postgres:15
docker run -d -p 6379:6379 redis:latest docker run -d -p 6379:6379 redis:latest
docker run -p 3000:3000 -d gotenberg/gotenberg:7.8 gotenberg --chromium-disable-javascript=true --chromium-allow-list="file:///tmp/.*" docker run -p 3000:3000 -d gotenberg/gotenberg:7.8 gotenberg --chromium-disable-javascript=true --chromium-allow-list="^file:///tmp/.*"
docker run -p 9998:9998 -d docker.io/apache/tika:latest docker run -p 9998:9998 -d docker.io/apache/tika:latest
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "paperless-ngx-ui", "name": "paperless-ngx-ui",
"version": "3.2.1", "version": "3.3.1",
"scripts": { "scripts": {
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"ng": "ng", "ng": "ng",
@@ -29,13 +29,8 @@
</div> </div>
<div class="search-container flex-grow-1 py-2 pb-3 pb-sm-2 me-sm-auto order-3 order-sm-1" <div class="search-container flex-grow-1 py-2 pb-3 pb-sm-2 me-sm-auto order-3 order-sm-1"
[class.mobile-hidden]="mobileSearchHidden()"> [class.mobile-hidden]="mobileSearchHidden()">
<div class="col-12 header-search mx-auto d-flex align-items-stretch gap-3"> <div class="col-12 header-search mx-auto">
<pngx-global-search class="flex-grow-1"></pngx-global-search> <pngx-global-search></pngx-global-search>
<button *pngxIfPermissions="{ action: PermissionAction.Add, type: PermissionType.Document }"
class="btn btn-sm header-upload border-0 d-flex align-items-center justify-content-center flex-shrink-0 py-0 px-2" type="button"
(click)="fileUpload.click()" aria-label="Upload documents" i18n-aria-label>
<i-bs class="d-flex" width="1.1em" height="1.1em" name="file-earmark-arrow-up"></i-bs><span class="ms-2 d-sm-none d-md-inline" i18n>Upload</span>
</button>
</div> </div>
</div> </div>
<ul ngbNav class="order-sm-3"> <ul ngbNav class="order-sm-3">
@@ -75,9 +70,6 @@
</ul> </ul>
</nav> </nav>
<input type="file" class="visually-hidden" id="navbar-file-upload" (change)="onFileSelected($event)" multiple #fileUpload tabindex="-1" aria-hidden="true">
<label for="navbar-file-upload" class="visually-hidden" i18n>Upload Files</label>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<nav id="sidebarMenu" class="d-md-block bg-light sidebar collapse" <nav id="sidebarMenu" class="d-md-block bg-light sidebar collapse"
@@ -532,23 +532,7 @@ main {
.header-search { .header-search {
width: 100%; width: 100%;
max-width: 50rem; max-width: 55rem;
}
.header-search pngx-global-search {
min-width: 0;
}
// light w shadow to stand out against search
.header-upload {
--bs-btn-bg: rgba(255, 255, 255, .15);
--bs-btn-hover-bg: rgba(255, 255, 255, .22);
--bs-btn-active-bg: rgba(255, 255, 255, .3);
color: var(--pngx-primary-text-contrast) !important; // .navbar .dropdown > .btn in styles.scss
&:not(:focus) {
box-shadow: 0 1px 3px rgba(0, 0, 0, .35), 0 1px 1px rgba(0, 0, 0, .2); // leave room for the .btn:focus ring
}
} }
@media screen and (max-width: 575.98px) { @media screen and (max-width: 575.98px) {
@@ -678,7 +662,3 @@ main {
::ng-deep .navItemDrag .position-absolute i-bs { ::ng-deep .navItemDrag .position-absolute i-bs {
display: none; display: none;
} }
.smaller {
font-size: 0.75rem;
}
@@ -33,7 +33,6 @@ import { SearchService } from 'src/app/services/rest/search.service'
import { SettingsService } from 'src/app/services/settings.service' import { SettingsService } from 'src/app/services/settings.service'
import { TasksService } from 'src/app/services/tasks.service' import { TasksService } from 'src/app/services/tasks.service'
import { ToastService } from 'src/app/services/toast.service' import { ToastService } from 'src/app/services/toast.service'
import { UploadDocumentsService } from 'src/app/services/upload-documents.service'
import { environment } from 'src/environments/environment' import { environment } from 'src/environments/environment'
import { ProfileEditDialogComponent } from '../common/profile-edit-dialog/profile-edit-dialog.component' import { ProfileEditDialogComponent } from '../common/profile-edit-dialog/profile-edit-dialog.component'
import { DocumentDetailComponent } from '../document-detail/document-detail.component' import { DocumentDetailComponent } from '../document-detail/document-detail.component'
@@ -463,29 +462,6 @@ describe('AppFrameComponent', () => {
expect(routerSpy).toHaveBeenCalled() expect(routerSpy).toHaveBeenCalled()
}) })
it('should upload files selected from the navbar upload button', () => {
const uploadDocumentsService = TestBed.inject(UploadDocumentsService)
const uploadSpy = jest
.spyOn(uploadDocumentsService, 'uploadFiles')
.mockImplementation(() => {})
const toastSpy = jest.spyOn(toastService, 'showInfo')
const file = new File(['testing'], 'file.pdf')
const input = window.document.createElement('input')
jest.spyOn(input, 'files', 'get').mockReturnValue([file] as any)
component.onFileSelected({ target: input } as unknown as Event)
expect(toastSpy).toHaveBeenCalled()
expect(uploadSpy).toHaveBeenCalledWith([file])
})
it('should not upload when no files are selected from the navbar', () => {
const uploadDocumentsService = TestBed.inject(UploadDocumentsService)
const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFiles')
const input = window.document.createElement('input')
component.onFileSelected({ target: input } as unknown as Event)
expect(uploadSpy).not.toHaveBeenCalled()
})
it('should close all documents on logout', () => { it('should close all documents on logout', () => {
const closeAllSpy = jest.spyOn(openDocumentsService, 'closeAll') const closeAllSpy = jest.spyOn(openDocumentsService, 'closeAll')
component.onLogout() component.onLogout()
@@ -48,13 +48,12 @@ import { SavedViewService } from 'src/app/services/rest/saved-view.service'
import { SettingsService } from 'src/app/services/settings.service' import { SettingsService } from 'src/app/services/settings.service'
import { TasksService } from 'src/app/services/tasks.service' import { TasksService } from 'src/app/services/tasks.service'
import { ToastService } from 'src/app/services/toast.service' import { ToastService } from 'src/app/services/toast.service'
import { UploadDocumentsService } from 'src/app/services/upload-documents.service'
import { environment } from 'src/environments/environment' import { environment } from 'src/environments/environment'
import { ChatComponent } from '../chat/chat/chat.component' import { ChatComponent } from '../chat/chat/chat.component'
import { SwitchComponent } from '../common/input/switch/switch.component'
import { BrandMarkComponent } from '../common/logo/brand-mark/brand-mark.component' import { BrandMarkComponent } from '../common/logo/brand-mark/brand-mark.component'
import { LogoComponent } from '../common/logo/logo.component' import { LogoComponent } from '../common/logo/logo.component'
import { ProfileEditDialogComponent } from '../common/profile-edit-dialog/profile-edit-dialog.component' import { ProfileEditDialogComponent } from '../common/profile-edit-dialog/profile-edit-dialog.component'
import { SwitchComponent } from '../common/input/switch/switch.component'
import { DocumentDetailComponent } from '../document-detail/document-detail.component' import { DocumentDetailComponent } from '../document-detail/document-detail.component'
import { ComponentWithPermissions } from '../with-permissions/with-permissions.component' import { ComponentWithPermissions } from '../with-permissions/with-permissions.component'
import { GlobalSearchComponent } from './global-search/global-search.component' import { GlobalSearchComponent } from './global-search/global-search.component'
@@ -102,7 +101,6 @@ export class AppFrameComponent
private modalService = inject(NgbModal) private modalService = inject(NgbModal)
permissionsService = inject(PermissionsService) permissionsService = inject(PermissionsService)
private djangoMessagesService = inject(DjangoMessagesService) private djangoMessagesService = inject(DjangoMessagesService)
private uploadDocumentsService = inject(UploadDocumentsService)
readonly appRemoteVersion = signal<AppRemoteVersion>(null) readonly appRemoteVersion = signal<AppRemoteVersion>(null)
readonly isMenuCollapsed = signal(true) readonly isMenuCollapsed = signal(true)
@@ -513,15 +511,6 @@ export class AppFrameComponent
} }
} }
onFileSelected(event: Event) {
const input = event.target as HTMLInputElement
const files = Array.from(input.files ?? [])
input.value = ''
if (!files.length) return
this.toastService.showInfo($localize`Initiating upload...`, 3000)
this.uploadDocumentsService.uploadFiles(files)
}
onLogout() { onLogout() {
this.openDocumentsService.closeAll() this.openDocumentsService.closeAll()
} }
@@ -85,11 +85,15 @@ describe('UploadFileWidgetComponent', () => {
'file.pdf' 'file.pdf'
) )
const fileInput = fixture.debugElement.query(By.css('input')) const fileInput = fixture.debugElement.query(By.css('input'))
jest jest.spyOn(fileInput.nativeElement, 'files', 'get').mockReturnValue({
.spyOn(fileInput.nativeElement, 'files', 'get') item: () => file,
.mockReturnValue([file] as any) length: 1,
[Symbol.iterator]: () => ({
next: () => ({ done: false, value: file }),
}),
} as any)
fileInput.nativeElement.dispatchEvent(new Event('change')) fileInput.nativeElement.dispatchEvent(new Event('change'))
expect(uploadSpy).toHaveBeenCalledWith(file) expect(uploadSpy).toHaveBeenCalled()
}) })
it('should generate stats summary', () => { it('should generate stats summary', () => {
@@ -130,9 +130,11 @@ export class UploadFileWidgetComponent extends ComponentWithPermissions {
} }
public onFileSelected(event: Event) { public onFileSelected(event: Event) {
const input = event.target as HTMLInputElement const files = (event.target as HTMLInputElement).files
this.uploadDocumentsService.uploadFiles(Array.from(input.files ?? [])) for (let i = 0; i < files?.length; i++) {
input.value = '' const file = files.item(i)
file && this.uploadDocumentsService.uploadFile(file)
}
} }
get slimSidebarEnabled(): boolean { get slimSidebarEnabled(): boolean {
@@ -135,7 +135,7 @@ export class FileDropComponent {
.then((results) => { .then((results) => {
files.push(...[].concat(...results)) files.push(...[].concat(...results))
this.toastService.showInfo($localize`Initiating upload...`, 3000) this.toastService.showInfo($localize`Initiating upload...`, 3000)
this.uploadDocumentsService.uploadFiles(files) files.forEach((file) => this.uploadDocumentsService.uploadFile(file))
}) })
.catch((e) => { .catch((e) => {
this.toastService.showError( this.toastService.showError(
@@ -144,7 +144,7 @@ export class FileDropComponent {
}) })
} else if (files.length) { } else if (files.length) {
this.toastService.showInfo($localize`Initiating upload...`, 3000) this.toastService.showInfo($localize`Initiating upload...`, 3000)
this.uploadDocumentsService.uploadFiles(files) files.forEach((file) => this.uploadDocumentsService.uploadFile(file))
} }
this.onDragLeave(event, true) this.onDragLeave(event, true)
@@ -54,18 +54,6 @@ describe('UploadDocumentsService', () => {
req[0].flush('123-456') req[0].flush('123-456')
}) })
it('uploads each file when given multiple files', () => {
const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFile')
const file1 = new File(['one'], 'one.pdf')
const file2 = new File(['two'], 'two.pdf')
uploadDocumentsService.uploadFiles([file1, file2])
expect(uploadSpy).toHaveBeenCalledWith(file1)
expect(uploadSpy).toHaveBeenCalledWith(file2)
httpTestingController
.match(`${environment.apiBaseUrl}documents/post_document/`)
.forEach((req) => req.flush('123-456'))
})
it('updates progress during upload and failure', () => { it('updates progress during upload and failure', () => {
const file = new File( const file = new File(
[new Blob(['testing'], { type: 'application/pdf' })], [new Blob(['testing'], { type: 'application/pdf' })],
@@ -16,12 +16,6 @@ export class UploadDocumentsService {
private uploadSubscriptions: Array<Subscription> = [] private uploadSubscriptions: Array<Subscription> = []
public uploadFiles(files: Iterable<File>) {
for (const file of files) {
this.uploadFile(file)
}
}
public uploadFile(file: File) { public uploadFile(file: File) {
let formData = new FormData() let formData = new FormData()
formData.append('document', file, file.name) formData.append('document', file, file.name)
+1 -1
View File
@@ -8,7 +8,7 @@ export const environment = {
apiVersion: '10', // match src/paperless/settings.py apiVersion: '10', // match src/paperless/settings.py
appTitle: DEFAULT_APP_TITLE, appTitle: DEFAULT_APP_TITLE,
tag: 'prod', tag: 'prod',
version: '3.2.1', version: '3.3.1',
webSocketHost: window.location.host, webSocketHost: window.location.host,
webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:', webSocketProtocol: window.location.protocol == 'https:' ? 'wss:' : 'ws:',
webSocketBaseUrl: base_url.pathname + 'ws/', webSocketBaseUrl: base_url.pathname + 'ws/',
+1 -1
View File
@@ -8525,7 +8525,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">54</context> <context context-type="linenumber">54</context>
</context-group> </context-group>
<target state="needs-translation">{VAR_PLURAL, plural, =1 {1 suggestion available below} other {<x id="INTERPOLATION"/> suggestions available below}}</target> <target state="translated">{VAR_PLURAL, plural, =1 {1 Vorschlag unten verfügbar} other {<x id="INTERPOLATION"/> Vorschläge unten verfügbar}}</target>
</trans-unit> </trans-unit>
<trans-unit id="4169875112244042673" datatype="html" approved="yes"> <trans-unit id="4169875112244042673" datatype="html" approved="yes">
<source>Suggestion options</source> <source>Suggestion options</source>
+4 -4
View File
@@ -8525,7 +8525,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">54</context> <context context-type="linenumber">54</context>
</context-group> </context-group>
<target state="needs-translation">{VAR_PLURAL, plural, =1 {1 suggestion available below} other {<x id="INTERPOLATION"/> suggestions available below}}</target> <target state="translated">{VAR_PLURAL, plural, one {<x id="INTERPOLATION"/> suggerimenti disponibili sotto} =1 {1 suggerimento disponibile sotto} other {<x id="INTERPOLATION"/> suggerimenti disponibili sotto}}</target>
</trans-unit> </trans-unit>
<trans-unit id="4169875112244042673" datatype="html"> <trans-unit id="4169875112244042673" datatype="html">
<source>Suggestion options</source> <source>Suggestion options</source>
@@ -9219,7 +9219,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-barcodes/document-barcodes.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-barcodes/document-barcodes.component.html</context>
<context context-type="linenumber">40,41</context> <context context-type="linenumber">40,41</context>
</context-group> </context-group>
<target state="needs-translation">Copy content</target> <target state="translated">Copia contenuto</target>
</trans-unit> </trans-unit>
<trans-unit id="2266163016683537825" datatype="html"> <trans-unit id="2266163016683537825" datatype="html">
<source>of <x id="INTERPOLATION" equiv-text="{{previewNumPages()}}"/></source> <source>of <x id="INTERPOLATION" equiv-text="{{previewNumPages()}}"/></source>
@@ -9479,7 +9479,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
<context context-type="linenumber">360,361</context> <context context-type="linenumber">360,361</context>
</context-group> </context-group>
<target state="needs-translation">Barcodes</target> <target state="translated">Codici a barre</target>
</trans-unit> </trans-unit>
<trans-unit id="6992781481378431874" datatype="html" approved="yes"> <trans-unit id="6992781481378431874" datatype="html" approved="yes">
<source>Original document metadata</source> <source>Original document metadata</source>
@@ -13376,7 +13376,7 @@
<context context-type="sourcefile">src/app/data/paperless-config.ts</context> <context context-type="sourcefile">src/app/data/paperless-config.ts</context>
<context context-type="linenumber">335</context> <context context-type="linenumber">335</context>
</context-group> </context-group>
<target state="needs-translation">Store Barcode Contents</target> <target state="translated">Contenuti Codice a Barre</target>
</trans-unit> </trans-unit>
<trans-unit id="7011909364081812031" datatype="html"> <trans-unit id="7011909364081812031" datatype="html">
<source>AI Enabled</source> <source>AI Enabled</source>
+18 -18
View File
@@ -17,7 +17,7 @@
<context context-type="linenumber">386,393</context> <context context-type="linenumber">386,393</context>
</context-group> </context-group>
<note priority="1" from="description">Currently selected slide number read by screen reader</note> <note priority="1" from="description">Currently selected slide number read by screen reader</note>
<target state="needs-translation"> Slide <x id="INTERPOLATION" equiv-text="xt(source?: Ngb"/> of <x id="INTERPOLATION_1" equiv-text="this._cycleToSe"/> </target> <target state="translated"> Slajd <x id="INTERPOLATION" equiv-text="xt(source?: Ngb"/> z <x id="INTERPOLATION_1" equiv-text="this._cycleToSe"/> </target>
</trans-unit> </trans-unit>
<trans-unit id="ngb.carousel.previous" datatype="html"> <trans-unit id="ngb.carousel.previous" datatype="html">
<source>Previous</source> <source>Previous</source>
@@ -507,7 +507,7 @@
<context context-type="sourcefile">src/app/app.component.ts</context> <context context-type="sourcefile">src/app/app.component.ts</context>
<context context-type="linenumber">225</context> <context context-type="linenumber">225</context>
</context-group> </context-group>
<target state="needs-translation">Tasks helps you track background work, what needs attention, and what recently completed.</target> <target state="translated">Zadania pomagają śledzić pracę w tle, to, co wymaga uwagi i co zostało ostatnio ukończone.</target>
</trans-unit> </trans-unit>
<trans-unit id="2755033621682206902" datatype="html"> <trans-unit id="2755033621682206902" datatype="html">
<source>Check out the settings for various tweaks to the web app.</source> <source>Check out the settings for various tweaks to the web app.</source>
@@ -571,7 +571,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context> <context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">35</context> <context context-type="linenumber">35</context>
</context-group> </context-group>
<target state="needs-translation">This value overrides <x id="INTERPOLATION" equiv-text="{{option.config_key}}"/>, which is set outside Paperless.</target> <target state="translated">Ta wartość zastępuje <x id="INTERPOLATION" equiv-text="{{option.config_key}}"/>, która jest ustawiona poza Paperless.</target>
</trans-unit> </trans-unit>
<trans-unit id="7221396516204435584" datatype="html"> <trans-unit id="7221396516204435584" datatype="html">
<source><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> is set outside Paperless. Enter a value here to override it.</source> <source><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> is set outside Paperless. Enter a value here to override it.</source>
@@ -579,7 +579,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context> <context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">37</context> <context context-type="linenumber">37</context>
</context-group> </context-group>
<target state="needs-translation"><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> is set outside Paperless. Enter a value here to override it.</target> <target state="translated"><x id="INTERPOLATION" equiv-text="{{option.config_key}}"/> jest ustawiony poza Paperless. Wpisz tutaj wartość, aby go zastąpić.</target>
</trans-unit> </trans-unit>
<trans-unit id="8318849619178340389" datatype="html"> <trans-unit id="8318849619178340389" datatype="html">
<source>Use the externally configured value</source> <source>Use the externally configured value</source>
@@ -587,7 +587,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context> <context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">42</context> <context context-type="linenumber">42</context>
</context-group> </context-group>
<target state="needs-translation">Use the externally configured value</target> <target state="translated">Użyj wartości skonfigurowanej zewnętrznie</target>
</trans-unit> </trans-unit>
<trans-unit id="6032629623003430385" datatype="html"> <trans-unit id="6032629623003430385" datatype="html">
<source>Reset to external</source> <source>Reset to external</source>
@@ -595,7 +595,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context> <context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">43</context> <context context-type="linenumber">43</context>
</context-group> </context-group>
<target state="needs-translation">Reset to external</target> <target state="translated">Zresetuj na zewnętrzny</target>
</trans-unit> </trans-unit>
<trans-unit id="7808756054397155068" datatype="html"> <trans-unit id="7808756054397155068" datatype="html">
<source>Reset</source> <source>Reset</source>
@@ -1411,7 +1411,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">264</context> <context context-type="linenumber">264</context>
</context-group> </context-group>
<target state="needs-translation">Both</target> <target state="translated">Obydwa</target>
</trans-unit> </trans-unit>
<trans-unit id="8967224283122923634" datatype="html"> <trans-unit id="8967224283122923634" datatype="html">
<source>ML only</source> <source>ML only</source>
@@ -1419,7 +1419,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">266</context> <context context-type="linenumber">266</context>
</context-group> </context-group>
<target state="needs-translation">ML only</target> <target state="translated">Tylko ML</target>
</trans-unit> </trans-unit>
<trans-unit id="6418969597665857901" datatype="html"> <trans-unit id="6418969597665857901" datatype="html">
<source>AI only</source> <source>AI only</source>
@@ -1427,7 +1427,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">268</context> <context context-type="linenumber">268</context>
</context-group> </context-group>
<target state="needs-translation">AI only</target> <target state="translated">Tylko SI</target>
</trans-unit> </trans-unit>
<trans-unit id="1043449647727456069" datatype="html"> <trans-unit id="1043449647727456069" datatype="html">
<source>Automatically request suggestions for inbox documents</source> <source>Automatically request suggestions for inbox documents</source>
@@ -2211,7 +2211,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context> <context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">4,5</context> <context context-type="linenumber">4,5</context>
</context-group> </context-group>
<target state="needs-translation">Tasks shows detailed information about document consumption and system tasks.</target> <target state="translated">Zadania zawierają szczegóły dotyczące przetwarzania dokumentów i zadań systemowych.</target>
</trans-unit> </trans-unit>
<trans-unit id="103921551219467537" datatype="html"> <trans-unit id="103921551219467537" datatype="html">
<source>Clear selection</source> <source>Clear selection</source>
@@ -2327,7 +2327,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context> <context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">92,93</context> <context context-type="linenumber">92,93</context>
</context-group> </context-group>
<target state="needs-translation">Search tasks</target> <target state="translated">Szukaj w zadaniach</target>
</trans-unit> </trans-unit>
<trans-unit id="6849725902312323996" datatype="html" approved="yes"> <trans-unit id="6849725902312323996" datatype="html" approved="yes">
<source>Reset filters</source> <source>Reset filters</source>
@@ -2651,7 +2651,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context> <context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
<context context-type="linenumber">207</context> <context context-type="linenumber">207</context>
</context-group> </context-group>
<target state="needs-translation">View task details</target> <target state="translated">Wyświetl szczegóły zadania</target>
</trans-unit> </trans-unit>
<trans-unit id="1536087519743707362" datatype="html"> <trans-unit id="1536087519743707362" datatype="html">
<source>Dismiss</source> <source>Dismiss</source>
@@ -4555,7 +4555,7 @@
<context context-type="sourcefile">src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.html</context> <context context-type="sourcefile">src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.html</context>
<context context-type="linenumber">9</context> <context context-type="linenumber">9</context>
</context-group> </context-group>
<target state="needs-translation">Rotate counterclockwise</target> <target state="translated">Obróć w lewo</target>
</trans-unit> </trans-unit>
<trans-unit id="631338357809243389" datatype="html"> <trans-unit id="631338357809243389" datatype="html">
<source>Rotate clockwise</source> <source>Rotate clockwise</source>
@@ -4563,7 +4563,7 @@
<context context-type="sourcefile">src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.html</context> <context context-type="sourcefile">src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.html</context>
<context context-type="linenumber">19</context> <context context-type="linenumber">19</context>
</context-group> </context-group>
<target state="needs-translation">Rotate clockwise</target> <target state="translated">Obróć w prawo</target>
</trans-unit> </trans-unit>
<trans-unit id="8157388568390631653" datatype="html"> <trans-unit id="8157388568390631653" datatype="html">
<source>Note that only PDFs will be rotated.</source> <source>Note that only PDFs will be rotated.</source>
@@ -7641,7 +7641,7 @@
<context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context> <context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context>
<context context-type="linenumber">17</context> <context context-type="linenumber">17</context>
</context-group> </context-group>
<target state="translated">Obróć wybrane strony przeciwnie do ruchu wskazówek zegara</target> <target state="translated">Obróć zaznaczone strony w lewo</target>
</trans-unit> </trans-unit>
<trans-unit id="4787219034890830544" datatype="html"> <trans-unit id="4787219034890830544" datatype="html">
<source>Rotate selected pages clockwise</source> <source>Rotate selected pages clockwise</source>
@@ -7649,7 +7649,7 @@
<context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context> <context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context>
<context context-type="linenumber">20</context> <context context-type="linenumber">20</context>
</context-group> </context-group>
<target state="translated">Obróć wybrane strony zgodnie z ruchem wskazówek zegara</target> <target state="translated">Obróć zaznaczone strony w prawo</target>
</trans-unit> </trans-unit>
<trans-unit id="3441043765105475130" datatype="html"> <trans-unit id="3441043765105475130" datatype="html">
<source>Delete selected pages</source> <source>Delete selected pages</source>
@@ -7665,7 +7665,7 @@
<context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context> <context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context>
<context context-type="linenumber">33</context> <context context-type="linenumber">33</context>
</context-group> </context-group>
<target state="translated">Obróć stronę przeciwnie do ruchu wskazówek zegara</target> <target state="translated">Obróć stronę w lewo</target>
</trans-unit> </trans-unit>
<trans-unit id="3450236521040548507" datatype="html"> <trans-unit id="3450236521040548507" datatype="html">
<source>Rotate page clockwise</source> <source>Rotate page clockwise</source>
@@ -7673,7 +7673,7 @@
<context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context> <context context-type="sourcefile">src/app/components/common/pdf-editor/pdf-editor.component.html</context>
<context context-type="linenumber">36</context> <context context-type="linenumber">36</context>
</context-group> </context-group>
<target state="translated">Obróć stronę zgodnie z ruchem wskazówek zegara</target> <target state="translated">Obróć stronę w prawo</target>
</trans-unit> </trans-unit>
<trans-unit id="7647925464077975347" datatype="html"> <trans-unit id="7647925464077975347" datatype="html">
<source>Delete page</source> <source>Delete page</source>
+14 -14
View File
@@ -17,7 +17,7 @@
<context context-type="linenumber">386,393</context> <context context-type="linenumber">386,393</context>
</context-group> </context-group>
<note priority="1" from="description">Currently selected slide number read by screen reader</note> <note priority="1" from="description">Currently selected slide number read by screen reader</note>
<target state="needs-translation"> Slide <x id="INTERPOLATION" equiv-text="xt(source?: Ngb"/> of <x id="INTERPOLATION_1" equiv-text="this._cycleToSe"/> </target> <target state="translated"> Слайд <x id="INTERPOLATION" equiv-text="xt(source?: Ngb"/> из <x id="INTERPOLATION_1" equiv-text="this._cycleToSe"/> </target>
</trans-unit> </trans-unit>
<trans-unit id="ngb.carousel.previous" datatype="html"> <trans-unit id="ngb.carousel.previous" datatype="html">
<source>Previous</source> <source>Previous</source>
@@ -1403,7 +1403,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">259,260</context> <context context-type="linenumber">259,260</context>
</context-group> </context-group>
<target state="needs-translation">Suggestions default to</target> <target state="translated">Предложения по умолчанию для</target>
</trans-unit> </trans-unit>
<trans-unit id="6251586038459969294" datatype="html"> <trans-unit id="6251586038459969294" datatype="html">
<source>Both</source> <source>Both</source>
@@ -1411,7 +1411,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">264</context> <context context-type="linenumber">264</context>
</context-group> </context-group>
<target state="needs-translation">Both</target> <target state="translated">Оба</target>
</trans-unit> </trans-unit>
<trans-unit id="8967224283122923634" datatype="html"> <trans-unit id="8967224283122923634" datatype="html">
<source>ML only</source> <source>ML only</source>
@@ -1419,7 +1419,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">266</context> <context context-type="linenumber">266</context>
</context-group> </context-group>
<target state="needs-translation">ML only</target> <target state="translated">Только ML</target>
</trans-unit> </trans-unit>
<trans-unit id="6418969597665857901" datatype="html"> <trans-unit id="6418969597665857901" datatype="html">
<source>AI only</source> <source>AI only</source>
@@ -1427,7 +1427,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">268</context> <context context-type="linenumber">268</context>
</context-group> </context-group>
<target state="needs-translation">AI only</target> <target state="translated">Только ИИ</target>
</trans-unit> </trans-unit>
<trans-unit id="1043449647727456069" datatype="html"> <trans-unit id="1043449647727456069" datatype="html">
<source>Automatically request suggestions for inbox documents</source> <source>Automatically request suggestions for inbox documents</source>
@@ -8525,7 +8525,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">54</context> <context context-type="linenumber">54</context>
</context-group> </context-group>
<target state="needs-translation">{VAR_PLURAL, plural, =1 {1 suggestion available below} other {<x id="INTERPOLATION"/> suggestions available below}}</target> <target state="translated">{VAR_PLURAL, plural, =1 {1 совет} other {<x id="INTERPOLATION"/> советов}}</target>
</trans-unit> </trans-unit>
<trans-unit id="4169875112244042673" datatype="html"> <trans-unit id="4169875112244042673" datatype="html">
<source>Suggestion options</source> <source>Suggestion options</source>
@@ -8533,7 +8533,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">65,66</context> <context context-type="linenumber">65,66</context>
</context-group> </context-group>
<target state="needs-translation">Suggestion options</target> <target state="translated">Настройки советов</target>
</trans-unit> </trans-unit>
<trans-unit id="3145824396228856507" datatype="html"> <trans-unit id="3145824396228856507" datatype="html">
<source>Not using default</source> <source>Not using default</source>
@@ -8541,7 +8541,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">69,70</context> <context context-type="linenumber">69,70</context>
</context-group> </context-group>
<target state="needs-translation">Not using default</target> <target state="translated">Не использует по умолчанию</target>
</trans-unit> </trans-unit>
<trans-unit id="8102623853570619759" datatype="html"> <trans-unit id="8102623853570619759" datatype="html">
<source>Suggest using:</source> <source>Suggest using:</source>
@@ -8549,7 +8549,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">74,75</context> <context context-type="linenumber">74,75</context>
</context-group> </context-group>
<target state="needs-translation">Suggest using:</target> <target state="translated">Предложить используя:</target>
</trans-unit> </trans-unit>
<trans-unit id="5799391967036896978" datatype="html"> <trans-unit id="5799391967036896978" datatype="html">
<source>ML</source> <source>ML</source>
@@ -8557,7 +8557,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">77</context> <context context-type="linenumber">77</context>
</context-group> </context-group>
<target state="needs-translation">ML</target> <target state="translated">ML</target>
</trans-unit> </trans-unit>
<trans-unit id="3484984238852181071" datatype="html"> <trans-unit id="3484984238852181071" datatype="html">
<source>AI</source> <source>AI</source>
@@ -8565,7 +8565,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">81</context> <context context-type="linenumber">81</context>
</context-group> </context-group>
<target state="needs-translation">AI</target> <target state="translated">ИИ</target>
</trans-unit> </trans-unit>
<trans-unit id="9180110319941008393" datatype="html"> <trans-unit id="9180110319941008393" datatype="html">
<source>Environment</source> <source>Environment</source>
@@ -9219,7 +9219,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-barcodes/document-barcodes.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-barcodes/document-barcodes.component.html</context>
<context context-type="linenumber">40,41</context> <context context-type="linenumber">40,41</context>
</context-group> </context-group>
<target state="needs-translation">Copy content</target> <target state="translated">Скопировать содержимое</target>
</trans-unit> </trans-unit>
<trans-unit id="2266163016683537825" datatype="html"> <trans-unit id="2266163016683537825" datatype="html">
<source>of <x id="INTERPOLATION" equiv-text="{{previewNumPages()}}"/></source> <source>of <x id="INTERPOLATION" equiv-text="{{previewNumPages()}}"/></source>
@@ -9479,7 +9479,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
<context context-type="linenumber">360,361</context> <context context-type="linenumber">360,361</context>
</context-group> </context-group>
<target state="needs-translation">Barcodes</target> <target state="translated">Штрихкоды</target>
</trans-unit> </trans-unit>
<trans-unit id="6992781481378431874" datatype="html" approved="yes"> <trans-unit id="6992781481378431874" datatype="html" approved="yes">
<source>Original document metadata</source> <source>Original document metadata</source>
@@ -13375,7 +13375,7 @@
<context context-type="sourcefile">src/app/data/paperless-config.ts</context> <context context-type="sourcefile">src/app/data/paperless-config.ts</context>
<context context-type="linenumber">335</context> <context context-type="linenumber">335</context>
</context-group> </context-group>
<target state="needs-translation">Store Barcode Contents</target> <target state="translated">Сохранить содержимое штрихкодов</target>
</trans-unit> </trans-unit>
<trans-unit id="7011909364081812031" datatype="html"> <trans-unit id="7011909364081812031" datatype="html">
<source>AI Enabled</source> <source>AI Enabled</source>
File diff suppressed because it is too large. Load diff
+58 -58
View File
@@ -595,7 +595,7 @@
<context context-type="sourcefile">src/app/components/admin/config/config.component.html</context> <context context-type="sourcefile">src/app/components/admin/config/config.component.html</context>
<context context-type="linenumber">43</context> <context context-type="linenumber">43</context>
</context-group> </context-group>
<target state="needs-translation">Reset to external</target> <target state="translated">重置为外部</target>
</trans-unit> </trans-unit>
<trans-unit id="7808756054397155068" datatype="html"> <trans-unit id="7808756054397155068" datatype="html">
<source>Reset</source> <source>Reset</source>
@@ -1135,7 +1135,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">115,116</context> <context context-type="linenumber">115,116</context>
</context-group> </context-group>
<target state="needs-translation">Sidebar items to show:</target> <target state="translated">侧边栏显示项:</target>
</trans-unit> </trans-unit>
<trans-unit id="1356890996281769972" datatype="html"> <trans-unit id="1356890996281769972" datatype="html">
<source>Dark mode</source> <source>Dark mode</source>
@@ -1419,7 +1419,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">266</context> <context context-type="linenumber">266</context>
</context-group> </context-group>
<target state="needs-translation">ML only</target> <target state="translated">仅限 ML</target>
</trans-unit> </trans-unit>
<trans-unit id="6418969597665857901" datatype="html"> <trans-unit id="6418969597665857901" datatype="html">
<source>AI only</source> <source>AI only</source>
@@ -1427,7 +1427,7 @@
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context> <context context-type="sourcefile">src/app/components/admin/settings/settings.component.html</context>
<context context-type="linenumber">268</context> <context context-type="linenumber">268</context>
</context-group> </context-group>
<target state="needs-translation">AI only</target> <target state="translated">仅限 AI</target>
</trans-unit> </trans-unit>
<trans-unit id="1043449647727456069" datatype="html"> <trans-unit id="1043449647727456069" datatype="html">
<source>Automatically request suggestions for inbox documents</source> <source>Automatically request suggestions for inbox documents</source>
@@ -2887,7 +2887,7 @@
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context> <context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.ts</context>
<context context-type="linenumber">104</context> <context context-type="linenumber">104</context>
</context-group> </context-group>
<target state="needs-translation">Apply AI Suggestions</target> <target state="translated">应用 AI 建议</target>
</trans-unit> </trans-unit>
<trans-unit id="9172233176401579786" datatype="html"> <trans-unit id="9172233176401579786" datatype="html">
<source>Scheduled</source> <source>Scheduled</source>
@@ -3939,7 +3939,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-links.component.html</context> <context context-type="sourcefile">src/app/components/manage/share-links/share-links.component.html</context>
<context context-type="linenumber">2,3</context> <context context-type="linenumber">2,3</context>
</context-group> </context-group>
<target state="needs-translation">Share links</target> <target state="translated">分享链接</target>
</trans-unit> </trans-unit>
<trans-unit id="7844706011418789951" datatype="html"> <trans-unit id="7844706011418789951" datatype="html">
<source>Administration</source> <source>Administration</source>
@@ -4259,7 +4259,7 @@
<context context-type="sourcefile">src/app/data/saved-view-icons.ts</context> <context context-type="sourcefile">src/app/data/saved-view-icons.ts</context>
<context context-type="linenumber">21</context> <context context-type="linenumber">21</context>
</context-group> </context-group>
<target state="needs-translation">Chat</target> <target state="translated">聊天</target>
</trans-unit> </trans-unit>
<trans-unit id="8066608938393600549" datatype="html"> <trans-unit id="8066608938393600549" datatype="html">
<source>Message</source> <source>Message</source>
@@ -4539,7 +4539,7 @@
<context context-type="sourcefile">src/app/components/common/confirm-dialog/reprocess-confirm-dialog/reprocess-confirm-dialog.component.html</context> <context context-type="sourcefile">src/app/components/common/confirm-dialog/reprocess-confirm-dialog/reprocess-confirm-dialog.component.html</context>
<context context-type="linenumber">16,17</context> <context context-type="linenumber">16,17</context>
</context-group> </context-group>
<target state="needs-translation">Use remote OCR</target> <target state="translated">使用远程 OCR</target>
</trans-unit> </trans-unit>
<trans-unit id="6733578401091585663" datatype="html"> <trans-unit id="6733578401091585663" datatype="html">
<source>Sends the document to the configured remote OCR service, which may incur costs.</source> <source>Sends the document to the configured remote OCR service, which may incur costs.</source>
@@ -4547,7 +4547,7 @@
<context context-type="sourcefile">src/app/components/common/confirm-dialog/reprocess-confirm-dialog/reprocess-confirm-dialog.component.html</context> <context context-type="sourcefile">src/app/components/common/confirm-dialog/reprocess-confirm-dialog/reprocess-confirm-dialog.component.html</context>
<context context-type="linenumber">17,18</context> <context context-type="linenumber">17,18</context>
</context-group> </context-group>
<target state="needs-translation">Sends the document to the configured remote OCR service, which may incur costs.</target> <target state="translated">将文档发送到已配置的远程 OCR 服务,可能会产生费用。</target>
</trans-unit> </trans-unit>
<trans-unit id="3883144805282469432" datatype="html"> <trans-unit id="3883144805282469432" datatype="html">
<source>Rotate counterclockwise</source> <source>Rotate counterclockwise</source>
@@ -4655,7 +4655,7 @@
<context context-type="sourcefile">src/app/components/common/input/date/date.component.html</context> <context context-type="sourcefile">src/app/components/common/input/date/date.component.html</context>
<context context-type="linenumber">16</context> <context context-type="linenumber">16</context>
</context-group> </context-group>
<target state="needs-translation">Open date picker</target> <target state="translated">打开日期选择器</target>
</trans-unit> </trans-unit>
<trans-unit id="6048892649018070225" datatype="html"> <trans-unit id="6048892649018070225" datatype="html">
<source>Today</source> <source>Today</source>
@@ -4779,7 +4779,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">154</context> <context context-type="linenumber">154</context>
</context-group> </context-group>
<target state="needs-translation">Remove query</target> <target state="translated">移除查询</target>
</trans-unit> </trans-unit>
<trans-unit id="3184700926171002527" datatype="html"> <trans-unit id="3184700926171002527" datatype="html">
<source>Any</source> <source>Any</source>
@@ -4823,7 +4823,7 @@
<context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html</context>
<context context-type="linenumber">196</context> <context context-type="linenumber">196</context>
</context-group> </context-group>
<target state="needs-translation">Remove expression</target> <target state="translated">移除表达式</target>
</trans-unit> </trans-unit>
<trans-unit id="6630533861307510614" datatype="html"> <trans-unit id="6630533861307510614" datatype="html">
<source>Clear created relative date</source> <source>Clear created relative date</source>
@@ -6931,7 +6931,7 @@
<context context-type="sourcefile">src/app/data/paperless-config.ts</context> <context context-type="sourcefile">src/app/data/paperless-config.ts</context>
<context context-type="linenumber">58</context> <context context-type="linenumber">58</context>
</context-group> </context-group>
<target state="needs-translation">Remote OCR</target> <target state="translated">远程 OCR</target>
</trans-unit> </trans-unit>
<trans-unit id="4101630920511881483" datatype="html"> <trans-unit id="4101630920511881483" datatype="html">
<source>Apply AI suggestions</source> <source>Apply AI suggestions</source>
@@ -6939,7 +6939,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context> <context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
<context context-type="linenumber">159</context> <context context-type="linenumber">159</context>
</context-group> </context-group>
<target state="needs-translation">Apply AI suggestions</target> <target state="translated">应用 AI 建议</target>
</trans-unit> </trans-unit>
<trans-unit id="5701618810648052610" datatype="html"> <trans-unit id="5701618810648052610" datatype="html">
<source>Title</source> <source>Title</source>
@@ -6975,7 +6975,7 @@
<context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context> <context context-type="sourcefile">src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts</context>
<context context-type="linenumber">186</context> <context context-type="linenumber">186</context>
</context-group> </context-group>
<target state="needs-translation">Created date</target> <target state="translated">创建日期</target>
</trans-unit> </trans-unit>
<trans-unit id="4522609911791833187" datatype="html"> <trans-unit id="4522609911791833187" datatype="html">
<source>Has any of these tags</source> <source>Has any of these tags</source>
@@ -7396,7 +7396,7 @@
<context context-type="sourcefile">src/app/components/common/input/document-link/document-link.component.html</context> <context context-type="sourcefile">src/app/components/common/input/document-link/document-link.component.html</context>
<context context-type="linenumber">52,53</context> <context context-type="linenumber">52,53</context>
</context-group> </context-group>
<target state="needs-translation">Unavailable</target> <target state="translated">不可用</target>
</trans-unit> </trans-unit>
<trans-unit id="5676637575587497817" datatype="html"> <trans-unit id="5676637575587497817" datatype="html">
<source>Search for documents</source> <source>Search for documents</source>
@@ -7440,7 +7440,7 @@
<context context-type="sourcefile">src/app/components/common/input/entries/entries.component.html</context> <context context-type="sourcefile">src/app/components/common/input/entries/entries.component.html</context>
<context context-type="linenumber">16</context> <context context-type="linenumber">16</context>
</context-group> </context-group>
<target state="needs-translation">Remove entry</target> <target state="translated">移除条目</target>
</trans-unit> </trans-unit>
<trans-unit id="6932865105766151309" datatype="html"> <trans-unit id="6932865105766151309" datatype="html">
<source>Upload</source> <source>Upload</source>
@@ -7493,7 +7493,7 @@
<context context-type="sourcefile">src/app/components/common/input/select/select.component.html</context> <context context-type="sourcefile">src/app/components/common/input/select/select.component.html</context>
<context context-type="linenumber">41</context> <context context-type="linenumber">41</context>
</context-group> </context-group>
<target state="needs-translation">Remove item</target> <target state="translated">移除项目</target>
</trans-unit> </trans-unit>
<trans-unit id="3686284950598311784" datatype="html"> <trans-unit id="3686284950598311784" datatype="html">
<source>Private</source> <source>Private</source>
@@ -7769,7 +7769,7 @@
<context context-type="sourcefile">src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.html</context>
<context context-type="linenumber">61</context> <context context-type="linenumber">61</context>
</context-group> </context-group>
<target state="needs-translation">Clear selected users</target> <target state="translated">清除选中的用户</target>
</trans-unit> </trans-unit>
<trans-unit id="8999708063434507268" datatype="html"> <trans-unit id="8999708063434507268" datatype="html">
<source>Hide unowned</source> <source>Hide unowned</source>
@@ -8481,7 +8481,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">12,13</context> <context context-type="linenumber">12,13</context>
</context-group> </context-group>
<target state="needs-translation">No suggestions</target> <target state="translated">无建议</target>
</trans-unit> </trans-unit>
<trans-unit id="5320136382998259826" datatype="html"> <trans-unit id="5320136382998259826" datatype="html">
<source>Suggest</source> <source>Suggest</source>
@@ -8533,7 +8533,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">65,66</context> <context context-type="linenumber">65,66</context>
</context-group> </context-group>
<target state="needs-translation">Suggestion options</target> <target state="translated">建议选项</target>
</trans-unit> </trans-unit>
<trans-unit id="3145824396228856507" datatype="html"> <trans-unit id="3145824396228856507" datatype="html">
<source>Not using default</source> <source>Not using default</source>
@@ -8541,7 +8541,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">69,70</context> <context context-type="linenumber">69,70</context>
</context-group> </context-group>
<target state="needs-translation">Not using default</target> <target state="translated">不使用默认值</target>
</trans-unit> </trans-unit>
<trans-unit id="8102623853570619759" datatype="html"> <trans-unit id="8102623853570619759" datatype="html">
<source>Suggest using:</source> <source>Suggest using:</source>
@@ -8549,7 +8549,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">74,75</context> <context context-type="linenumber">74,75</context>
</context-group> </context-group>
<target state="needs-translation">Suggest using:</target> <target state="translated">建议使用:</target>
</trans-unit> </trans-unit>
<trans-unit id="5799391967036896978" datatype="html"> <trans-unit id="5799391967036896978" datatype="html">
<source>ML</source> <source>ML</source>
@@ -8557,7 +8557,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">77</context> <context context-type="linenumber">77</context>
</context-group> </context-group>
<target state="needs-translation">ML</target> <target state="translated">ML</target>
</trans-unit> </trans-unit>
<trans-unit id="3484984238852181071" datatype="html"> <trans-unit id="3484984238852181071" datatype="html">
<source>AI</source> <source>AI</source>
@@ -8565,7 +8565,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">81</context> <context context-type="linenumber">81</context>
</context-group> </context-group>
<target state="needs-translation">AI</target> <target state="translated">AI</target>
</trans-unit> </trans-unit>
<trans-unit id="9180110319941008393" datatype="html"> <trans-unit id="9180110319941008393" datatype="html">
<source>Environment</source> <source>Environment</source>
@@ -9219,7 +9219,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-barcodes/document-barcodes.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-barcodes/document-barcodes.component.html</context>
<context context-type="linenumber">40,41</context> <context context-type="linenumber">40,41</context>
</context-group> </context-group>
<target state="needs-translation">Copy content</target> <target state="translated">复制内容</target>
</trans-unit> </trans-unit>
<trans-unit id="2266163016683537825" datatype="html"> <trans-unit id="2266163016683537825" datatype="html">
<source>of <x id="INTERPOLATION" equiv-text="{{previewNumPages()}}"/></source> <source>of <x id="INTERPOLATION" equiv-text="{{previewNumPages()}}"/></source>
@@ -9255,7 +9255,7 @@
<context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context> <context context-type="sourcefile">src/app/components/document-list/bulk-editor/bulk-editor.component.html</context>
<context context-type="linenumber">154</context> <context context-type="linenumber">154</context>
</context-group> </context-group>
<target state="needs-translation">Download options</target> <target state="translated">下载选项</target>
</trans-unit> </trans-unit>
<trans-unit id="8659635229098859487" datatype="html"> <trans-unit id="8659635229098859487" datatype="html">
<source>Download original</source> <source>Download original</source>
@@ -9479,7 +9479,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
<context context-type="linenumber">360,361</context> <context context-type="linenumber">360,361</context>
</context-group> </context-group>
<target state="needs-translation">Barcodes</target> <target state="translated">条形码</target>
</trans-unit> </trans-unit>
<trans-unit id="6992781481378431874" datatype="html"> <trans-unit id="6992781481378431874" datatype="html">
<source>Original document metadata</source> <source>Original document metadata</source>
@@ -9727,7 +9727,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1074</context> <context context-type="linenumber">1074</context>
</context-group> </context-group>
<target state="needs-translation">You have unsaved changes to the content of this version.</target> <target state="translated">此版本的内容有未保存的更改。</target>
</trans-unit> </trans-unit>
<trans-unit id="85184271222513014" datatype="html"> <trans-unit id="85184271222513014" datatype="html">
<source>Switching versions will discard them.</source> <source>Switching versions will discard them.</source>
@@ -9735,7 +9735,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1075</context> <context context-type="linenumber">1075</context>
</context-group> </context-group>
<target state="needs-translation">Switching versions will discard them.</target> <target state="translated">切换版本将丢弃这些更改。</target>
</trans-unit> </trans-unit>
<trans-unit id="2565707334844767610" datatype="html"> <trans-unit id="2565707334844767610" datatype="html">
<source>Discard and switch</source> <source>Discard and switch</source>
@@ -9743,7 +9743,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1077</context> <context context-type="linenumber">1077</context>
</context-group> </context-group>
<target state="needs-translation">Discard and switch</target> <target state="translated">丢弃并切换</target>
</trans-unit> </trans-unit>
<trans-unit id="2109314380040637387" datatype="html"> <trans-unit id="2109314380040637387" datatype="html">
<source>Save and switch</source> <source>Save and switch</source>
@@ -9751,7 +9751,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1079</context> <context context-type="linenumber">1079</context>
</context-group> </context-group>
<target state="needs-translation">Save and switch</target> <target state="translated">保存并切换</target>
</trans-unit> </trans-unit>
<trans-unit id="3456881259945295697" datatype="html"> <trans-unit id="3456881259945295697" datatype="html">
<source>Error retrieving suggestions.</source> <source>Error retrieving suggestions.</source>
@@ -9967,7 +9967,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-version-dropdown/add-existing-document-version-dialog/add-existing-document-version-dialog.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-version-dropdown/add-existing-document-version-dialog/add-existing-document-version-dialog.component.html</context>
<context context-type="linenumber">13,14</context> <context context-type="linenumber">13,14</context>
</context-group> </context-group>
<target state="needs-translation">Select one document to add as a version.</target> <target state="translated">请选择一份文档,将其添加为新版本。</target>
</trans-unit> </trans-unit>
<trans-unit id="337558081609002420" datatype="html"> <trans-unit id="337558081609002420" datatype="html">
<source>Add version</source> <source>Add version</source>
@@ -10011,7 +10011,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-version-dropdown/document-version-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-version-dropdown/document-version-dropdown.component.html</context>
<context context-type="linenumber">35,36</context> <context context-type="linenumber">35,36</context>
</context-group> </context-group>
<target state="needs-translation">Upload a new version</target> <target state="translated">上传新版本</target>
</trans-unit> </trans-unit>
<trans-unit id="278763911843514709" datatype="html"> <trans-unit id="278763911843514709" datatype="html">
<source>Use an existing document</source> <source>Use an existing document</source>
@@ -10019,7 +10019,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-version-dropdown/document-version-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-version-dropdown/document-version-dropdown.component.html</context>
<context context-type="linenumber">44,45</context> <context context-type="linenumber">44,45</context>
</context-group> </context-group>
<target state="needs-translation">Use an existing document</target> <target state="translated">使用现有文档</target>
</trans-unit> </trans-unit>
<trans-unit id="1906507263389090131" datatype="html"> <trans-unit id="1906507263389090131" datatype="html">
<source>Existing</source> <source>Existing</source>
@@ -10027,7 +10027,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-version-dropdown/document-version-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-version-dropdown/document-version-dropdown.component.html</context>
<context context-type="linenumber">47,48</context> <context context-type="linenumber">47,48</context>
</context-group> </context-group>
<target state="needs-translation">Existing</target> <target state="translated">已有的</target>
</trans-unit> </trans-unit>
<trans-unit id="3119565892291077820" datatype="html"> <trans-unit id="3119565892291077820" datatype="html">
<source>Uploading version...</source> <source>Uploading version...</source>
@@ -11234,7 +11234,7 @@
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">208</context> <context context-type="linenumber">208</context>
</context-group> </context-group>
<target state="needs-translation">Duplicates</target> <target state="translated">创建副本</target>
</trans-unit> </trans-unit>
<trans-unit id="8492379284173601938" datatype="html"> <trans-unit id="8492379284173601938" datatype="html">
<source>Custom fields (Deprecated)</source> <source>Custom fields (Deprecated)</source>
@@ -11298,7 +11298,7 @@
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">252</context> <context context-type="linenumber">252</context>
</context-group> </context-group>
<target state="needs-translation">exist</target> <target state="translated">存在</target>
</trans-unit> </trans-unit>
<trans-unit id="4528435301189613620" datatype="html"> <trans-unit id="4528435301189613620" datatype="html">
<source>do not exist</source> <source>do not exist</source>
@@ -11306,7 +11306,7 @@
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">256</context> <context context-type="linenumber">256</context>
</context-group> </context-group>
<target state="needs-translation">do not exist</target> <target state="translated">不存在</target>
</trans-unit> </trans-unit>
<trans-unit id="5195932016807797291" datatype="html"> <trans-unit id="5195932016807797291" datatype="html">
<source>Correspondent: <x id="PH" equiv-text="this.correspondentSelectionModel.items.find( (c) =&gt; c.id == +rule.value )?.name"/></source> <source>Correspondent: <x id="PH" equiv-text="this.correspondentSelectionModel.items.find( (c) =&gt; c.id == +rule.value )?.name"/></source>
@@ -11378,7 +11378,7 @@
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">343</context> <context context-type="linenumber">343</context>
</context-group> </context-group>
<target state="needs-translation">Without duplicates</target> <target state="translated">无重复项</target>
</trans-unit> </trans-unit>
<trans-unit id="8448347828886552444" datatype="html"> <trans-unit id="8448347828886552444" datatype="html">
<source>With duplicates</source> <source>With duplicates</source>
@@ -11386,7 +11386,7 @@
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context> <context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
<context context-type="linenumber">344</context> <context context-type="linenumber">344</context>
</context-group> </context-group>
<target state="needs-translation">With duplicates</target> <target state="translated">带重复项</target>
</trans-unit> </trans-unit>
<trans-unit id="8644099678903817943" datatype="html"> <trans-unit id="8644099678903817943" datatype="html">
<source>Custom fields query</source> <source>Custom fields query</source>
@@ -11462,7 +11462,7 @@
<context context-type="sourcefile">src/app/components/manage/saved-views/saved-views.component.html</context> <context context-type="sourcefile">src/app/components/manage/saved-views/saved-views.component.html</context>
<context context-type="linenumber">21</context> <context context-type="linenumber">21</context>
</context-group> </context-group>
<target state="needs-translation">Icon</target> <target state="translated">图标</target>
</trans-unit> </trans-unit>
<trans-unit id="8689274715612276035" datatype="html"> <trans-unit id="8689274715612276035" datatype="html">
<source>Show in sidebar</source> <source>Show in sidebar</source>
@@ -12515,7 +12515,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.html</context> <context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.html</context>
<context context-type="linenumber">40,41</context> <context context-type="linenumber">40,41</context>
</context-group> </context-group>
<target state="needs-translation">Expired</target> <target state="translated">已过期</target>
</trans-unit> </trans-unit>
<trans-unit id="2057574872309338088" datatype="html"> <trans-unit id="2057574872309338088" datatype="html">
<source>Copy share link</source> <source>Copy share link</source>
@@ -12559,7 +12559,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.html</context> <context context-type="sourcefile">src/app/components/manage/share-links/share-link-bundle-list/share-link-bundle-list.component.html</context>
<context context-type="linenumber">164,165</context> <context context-type="linenumber">164,165</context>
</context-group> </context-group>
<target state="needs-translation">Share link bundles pagination</target> <target state="translated">分享链接合集分页</target>
</trans-unit> </trans-unit>
<trans-unit id="1541995860059283227" datatype="html"> <trans-unit id="1541995860059283227" datatype="html">
<source>Failed to load share link bundles.</source> <source>Failed to load share link bundles.</source>
@@ -12623,7 +12623,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.html</context> <context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.html</context>
<context context-type="linenumber">15,16</context> <context context-type="linenumber">15,16</context>
</context-group> </context-group>
<target state="needs-translation">Document</target> <target state="translated">文档</target>
</trans-unit> </trans-unit>
<trans-unit id="7420713064779772535" datatype="html"> <trans-unit id="7420713064779772535" datatype="html">
<source>Delete share link</source> <source>Delete share link</source>
@@ -12631,7 +12631,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.html</context> <context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.html</context>
<context context-type="linenumber">75,76</context> <context context-type="linenumber">75,76</context>
</context-group> </context-group>
<target state="needs-translation">Delete share link</target> <target state="translated">删除分享链接</target>
</trans-unit> </trans-unit>
<trans-unit id="5198271726831144068" datatype="html"> <trans-unit id="5198271726831144068" datatype="html">
<source>Share links pagination</source> <source>Share links pagination</source>
@@ -12647,7 +12647,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.ts</context> <context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.ts</context>
<context context-type="linenumber">105</context> <context context-type="linenumber">105</context>
</context-group> </context-group>
<target state="needs-translation">Failed to load share links.</target> <target state="translated">加载分享链接失败。</target>
</trans-unit> </trans-unit>
<trans-unit id="4100863040928608880" datatype="html"> <trans-unit id="4100863040928608880" datatype="html">
<source>Error retrieving share links.</source> <source>Error retrieving share links.</source>
@@ -12655,7 +12655,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.ts</context> <context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.ts</context>
<context context-type="linenumber">107</context> <context context-type="linenumber">107</context>
</context-group> </context-group>
<target state="needs-translation">Error retrieving share links.</target> <target state="translated">获取分享链接失败。</target>
</trans-unit> </trans-unit>
<trans-unit id="326120283142363299" datatype="html"> <trans-unit id="326120283142363299" datatype="html">
<source>Share link deleted.</source> <source>Share link deleted.</source>
@@ -12663,7 +12663,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.ts</context> <context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.ts</context>
<context context-type="linenumber">154</context> <context context-type="linenumber">154</context>
</context-group> </context-group>
<target state="needs-translation">Share link deleted.</target> <target state="translated">分享链接已删除。</target>
</trans-unit> </trans-unit>
<trans-unit id="4964931417181298145" datatype="html"> <trans-unit id="4964931417181298145" datatype="html">
<source>Error deleting share link.</source> <source>Error deleting share link.</source>
@@ -12671,7 +12671,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.ts</context> <context context-type="sourcefile">src/app/components/manage/share-links/share-link-list/share-link-list.component.ts</context>
<context context-type="linenumber">159</context> <context context-type="linenumber">159</context>
</context-group> </context-group>
<target state="needs-translation">Error deleting share link.</target> <target state="translated">删除分享链接失败。</target>
</trans-unit> </trans-unit>
<trans-unit id="3439104787814858626" datatype="html"> <trans-unit id="3439104787814858626" datatype="html">
<source>Manage public links to individual documents and document bundles.</source> <source>Manage public links to individual documents and document bundles.</source>
@@ -12679,7 +12679,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-links.component.html</context> <context context-type="sourcefile">src/app/components/manage/share-links/share-links.component.html</context>
<context context-type="linenumber">4,5</context> <context context-type="linenumber">4,5</context>
</context-group> </context-group>
<target state="needs-translation">Manage public links to individual documents and document bundles.</target> <target state="translated">管理单个文档和文档合集的公开分享链接。</target>
</trans-unit> </trans-unit>
<trans-unit id="3740311348735113099" datatype="html"> <trans-unit id="3740311348735113099" datatype="html">
<source>Document links</source> <source>Document links</source>
@@ -12687,7 +12687,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-links.component.html</context> <context context-type="sourcefile">src/app/components/manage/share-links/share-links.component.html</context>
<context context-type="linenumber">18,19</context> <context context-type="linenumber">18,19</context>
</context-group> </context-group>
<target state="needs-translation">Document links</target> <target state="translated">文档链接</target>
</trans-unit> </trans-unit>
<trans-unit id="8216780576869093031" datatype="html"> <trans-unit id="8216780576869093031" datatype="html">
<source>Bundles</source> <source>Bundles</source>
@@ -12695,7 +12695,7 @@
<context context-type="sourcefile">src/app/components/manage/share-links/share-links.component.html</context> <context context-type="sourcefile">src/app/components/manage/share-links/share-links.component.html</context>
<context context-type="linenumber">26,27</context> <context context-type="linenumber">26,27</context>
</context-group> </context-group>
<target state="needs-translation">Bundles</target> <target state="translated">捆绑包</target>
</trans-unit> </trans-unit>
<trans-unit id="1229748338333965418" datatype="html"> <trans-unit id="1229748338333965418" datatype="html">
<source>Use workflows to customize the behavior of Paperless-ngx when events &apos;trigger&apos; a workflow.</source> <source>Use workflows to customize the behavior of Paperless-ngx when events &apos;trigger&apos; a workflow.</source>
@@ -13208,7 +13208,7 @@
<context context-type="sourcefile">src/app/data/paperless-config.ts</context> <context context-type="sourcefile">src/app/data/paperless-config.ts</context>
<context context-type="linenumber">200</context> <context context-type="linenumber">200</context>
</context-group> </context-group>
<target state="needs-translation">Remote OCR Engine</target> <target state="translated">远程 OCR 引擎</target>
</trans-unit> </trans-unit>
<trans-unit id="2892372808177591734" datatype="html"> <trans-unit id="2892372808177591734" datatype="html">
<source>Enabling remote OCR sends documents to a third-party service for processing. Consider the privacy implications as well as potential costs before enabling.</source> <source>Enabling remote OCR sends documents to a third-party service for processing. Consider the privacy implications as well as potential costs before enabling.</source>
@@ -13224,7 +13224,7 @@
<context context-type="sourcefile">src/app/data/paperless-config.ts</context> <context context-type="sourcefile">src/app/data/paperless-config.ts</context>
<context context-type="linenumber">210</context> <context context-type="linenumber">210</context>
</context-group> </context-group>
<target state="needs-translation">Remote OCR API Key</target> <target state="translated">远程 OCR API 密钥</target>
</trans-unit> </trans-unit>
<trans-unit id="8592973963881683840" datatype="html"> <trans-unit id="8592973963881683840" datatype="html">
<source>Remote OCR Endpoint</source> <source>Remote OCR Endpoint</source>
@@ -13232,7 +13232,7 @@
<context context-type="sourcefile">src/app/data/paperless-config.ts</context> <context context-type="sourcefile">src/app/data/paperless-config.ts</context>
<context context-type="linenumber">218</context> <context context-type="linenumber">218</context>
</context-group> </context-group>
<target state="needs-translation">Remote OCR Endpoint</target> <target state="translated">远程 OCR 端点</target>
</trans-unit> </trans-unit>
<trans-unit id="2477780225973884032" datatype="html"> <trans-unit id="2477780225973884032" datatype="html">
<source>Required when using the Azure AI engine.</source> <source>Required when using the Azure AI engine.</source>
@@ -13240,7 +13240,7 @@
<context context-type="sourcefile">src/app/data/paperless-config.ts</context> <context context-type="sourcefile">src/app/data/paperless-config.ts</context>
<context context-type="linenumber">223</context> <context context-type="linenumber">223</context>
</context-group> </context-group>
<target state="needs-translation">Required when using the Azure AI engine.</target> <target state="translated">使用 Azure AI 引擎时必填。</target>
</trans-unit> </trans-unit>
<trans-unit id="2908398258708722139" datatype="html"> <trans-unit id="2908398258708722139" datatype="html">
<source>Remote OCR Mode</source> <source>Remote OCR Mode</source>
@@ -13248,7 +13248,7 @@
<context context-type="sourcefile">src/app/data/paperless-config.ts</context> <context context-type="sourcefile">src/app/data/paperless-config.ts</context>
<context context-type="linenumber">227</context> <context context-type="linenumber">227</context>
</context-group> </context-group>
<target state="needs-translation">Remote OCR Mode</target> <target state="translated">远程 OCR 模式</target>
</trans-unit> </trans-unit>
<trans-unit id="4022045371405916558" datatype="html"> <trans-unit id="4022045371405916558" datatype="html">
<source>Which documents are sent to the remote engine. Use &apos;workflow_only&apos; to keep remote OCR off unless a workflow enables it for a document.</source> <source>Which documents are sent to the remote engine. Use &apos;workflow_only&apos; to keep remote OCR off unless a workflow enables it for a document.</source>
+6 -6
View File
@@ -479,7 +479,7 @@
<context context-type="sourcefile">src/app/app.component.ts</context> <context context-type="sourcefile">src/app/app.component.ts</context>
<context context-type="linenumber">201</context> <context context-type="linenumber">201</context>
</context-group> </context-group>
<target state="translated">標籤、關聯方、文件類型、儲存路徑與自訂欄位等屬性皆可在此處管理,亦可在「文件編輯」檢視中建立。</target> <target state="translated">標籤、關聯方、文件類型、儲存路徑與自訂欄位等文件屬性皆可在此處管理,亦可在「文件編輯」檢視中建立。</target>
</trans-unit> </trans-unit>
<trans-unit id="7851939076947092983" datatype="html"> <trans-unit id="7851939076947092983" datatype="html">
<source>Manage e-mail accounts and rules for automatically importing documents.</source> <source>Manage e-mail accounts and rules for automatically importing documents.</source>
@@ -3859,7 +3859,7 @@
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context> <context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">195</context> <context context-type="linenumber">195</context>
</context-group> </context-group>
<target state="translated">屬性</target> <target state="translated">文件屬性</target>
</trans-unit> </trans-unit>
<trans-unit id="7437910965833684826" datatype="html"> <trans-unit id="7437910965833684826" datatype="html">
<source>Correspondents</source> <source>Correspondents</source>
@@ -8525,7 +8525,7 @@
<context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context> <context context-type="sourcefile">src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html</context>
<context context-type="linenumber">54</context> <context context-type="linenumber">54</context>
</context-group> </context-group>
<target state="needs-translation">{VAR_PLURAL, plural, =1 {1 suggestion available below} other {<x id="INTERPOLATION"/> suggestions available below}}</target> <target state="translated">{VAR_PLURAL, plural, =1 {以下有 1 個可用建議} other {以下有 <x id="INTERPOLATION"/> 個可用建議}}</target>
</trans-unit> </trans-unit>
<trans-unit id="4169875112244042673" datatype="html"> <trans-unit id="4169875112244042673" datatype="html">
<source>Suggestion options</source> <source>Suggestion options</source>
@@ -9219,7 +9219,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-barcodes/document-barcodes.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-barcodes/document-barcodes.component.html</context>
<context context-type="linenumber">40,41</context> <context context-type="linenumber">40,41</context>
</context-group> </context-group>
<target state="needs-translation">Copy content</target> <target state="translated">拷貝內容</target>
</trans-unit> </trans-unit>
<trans-unit id="2266163016683537825" datatype="html"> <trans-unit id="2266163016683537825" datatype="html">
<source>of <x id="INTERPOLATION" equiv-text="{{previewNumPages()}}"/></source> <source>of <x id="INTERPOLATION" equiv-text="{{previewNumPages()}}"/></source>
@@ -9479,7 +9479,7 @@
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context> <context context-type="sourcefile">src/app/components/document-detail/document-detail.component.html</context>
<context context-type="linenumber">360,361</context> <context context-type="linenumber">360,361</context>
</context-group> </context-group>
<target state="needs-translation">Barcodes</target> <target state="translated">條碼</target>
</trans-unit> </trans-unit>
<trans-unit id="6992781481378431874" datatype="html"> <trans-unit id="6992781481378431874" datatype="html">
<source>Original document metadata</source> <source>Original document metadata</source>
@@ -13376,7 +13376,7 @@
<context context-type="sourcefile">src/app/data/paperless-config.ts</context> <context context-type="sourcefile">src/app/data/paperless-config.ts</context>
<context context-type="linenumber">335</context> <context context-type="linenumber">335</context>
</context-group> </context-group>
<target state="needs-translation">Store Barcode Contents</target> <target state="translated">儲存條碼內容</target>
</trans-unit> </trans-unit>
<trans-unit id="7011909364081812031" datatype="html"> <trans-unit id="7011909364081812031" datatype="html">
<source>AI Enabled</source> <source>AI Enabled</source>
-2
View File
@@ -90,7 +90,6 @@ import {
exclamationTriangleFill, exclamationTriangleFill,
eye, eye,
fileEarmark, fileEarmark,
fileEarmarkArrowUp,
fileEarmarkCheck, fileEarmarkCheck,
fileEarmarkDiff, fileEarmarkDiff,
fileEarmarkFill, fileEarmarkFill,
@@ -342,7 +341,6 @@ const icons = {
exclamationTriangleFill, exclamationTriangleFill,
eye, eye,
fileEarmark, fileEarmark,
fileEarmarkArrowUp,
fileEarmarkCheck, fileEarmarkCheck,
fileEarmarkDiff, fileEarmarkDiff,
fileEarmarkFill, fileEarmarkFill,
+1 -1
View File
@@ -15,7 +15,7 @@
"target": "ES2022", "target": "ES2022",
"module": "es2020", "module": "es2020",
"lib": [ "lib": [
"es2020", "es2022",
"dom" "dom"
], ],
"useDefineForClassFields": false "useDefineForClassFields": false
+211 -221
View File
@@ -3,6 +3,7 @@ from __future__ import annotations
import logging import logging
import tempfile import tempfile
import uuid import uuid
from functools import partial
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Literal from typing import Literal
@@ -13,10 +14,17 @@ from celery import group
from celery import shared_task from celery import shared_task
from django.conf import settings from django.conf import settings
from django.db import transaction from django.db import transaction
from django.db.models import Case
from django.db.models import F
from django.db.models import Max from django.db.models import Max
from django.db.models import OuterRef
from django.db.models import Q from django.db.models import Q
from django.db.models import Subquery
from django.db.models import When
from django.db.models.functions import Coalesce
from django.utils import timezone from django.utils import timezone
from documents import pdf_ops
from documents.data_models import ConsumableDocument from documents.data_models import ConsumableDocument
from documents.data_models import DocumentMetadataOverrides from documents.data_models import DocumentMetadataOverrides
from documents.data_models import DocumentSource from documents.data_models import DocumentSource
@@ -36,6 +44,7 @@ from documents.tasks import remove_document_from_index
from documents.tasks import update_document_content_maybe_archive_file from documents.tasks import update_document_content_maybe_archive_file
from documents.versioning import get_latest_version_for_root from documents.versioning import get_latest_version_for_root
from documents.versioning import get_root_document from documents.versioning import get_root_document
from documents.versioning import versions_newest_first
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Mapping from collections.abc import Mapping
@@ -116,6 +125,11 @@ def _resolve_root_and_source_doc(
) )
def _scratch_path(name: str) -> Path:
"""A path inside a fresh directory under SCRATCH_DIR."""
return Path(tempfile.mkdtemp(dir=settings.SCRATCH_DIR)) / name
def set_correspondent( def set_correspondent(
doc_ids: list[int], doc_ids: list[int],
correspondent: Correspondent, correspondent: Correspondent,
@@ -408,10 +422,28 @@ def reprocess(doc_ids: list[int], *, remote_ocr: bool = False) -> Literal["OK"]:
Consumption workflows do not run here, so ``remote_ocr`` is how the user Consumption workflows do not run here, so ``remote_ocr`` is how the user
asks for the remote engine when it is not configured to handle everything. asks for the remote engine when it is not configured to handle everything.
A root document with versions reprocesses its latest version, which is the
file whose content, archive and thumbnail are shown for it.
""" """
for document_id in doc_ids: latest_version = versions_newest_first(
Document.objects.filter(root_document=OuterRef("pk")),
).values("id")[:1]
source_ids = (
Document.objects.filter(id__in=doc_ids)
.annotate(
source_id=Case(
When(root_document__isnull=False, then=F("id")),
default=Coalesce(Subquery(latest_version), F("id")),
),
)
.order_by()
.values_list("source_id", flat=True)
.distinct()
)
for source_id in source_ids:
update_document_content_maybe_archive_file.apply_async( update_document_content_maybe_archive_file.apply_async(
kwargs={"document_id": document_id, "remote_ocr": remote_ocr}, kwargs={"document_id": source_id, "remote_ocr": remote_ocr},
headers={"trigger_source": PaperlessTask.TriggerSource.MANUAL}, headers={"trigger_source": PaperlessTask.TriggerSource.MANUAL},
) )
@@ -474,8 +506,6 @@ def rotate(
pair = _resolve_root_and_source_doc(doc, source_mode=source_mode) pair = _resolve_root_and_source_doc(doc, source_mode=source_mode)
docs_by_root_id.setdefault(pair.root_doc.id, pair) docs_by_root_id.setdefault(pair.root_doc.id, pair)
import pikepdf
for pair in docs_by_root_id.values(): for pair in docs_by_root_id.values():
if pair.source_doc.mime_type != "application/pdf": if pair.source_doc.mime_type != "application/pdf":
logger.warning( logger.warning(
@@ -488,11 +518,7 @@ def rotate(
Path(tempfile.mkdtemp(dir=settings.SCRATCH_DIR)) Path(tempfile.mkdtemp(dir=settings.SCRATCH_DIR))
/ f"{pair.root_doc.id}_rotated.pdf" / f"{pair.root_doc.id}_rotated.pdf"
) )
with pikepdf.open(pair.source_doc.source_path) as pdf: pdf_ops.rotate_pdf(pair.source_doc.source_path, filepath, degrees)
for page in pdf.pages:
page.rotate(degrees, relative=True)
pdf.remove_unreferenced_resources()
pdf.save(filepath)
# Preserve metadata/permissions via overrides; mark as new version # Preserve metadata/permissions via overrides; mark as new version
overrides = DocumentMetadataOverrides().from_document(pair.root_doc) overrides = DocumentMetadataOverrides().from_document(pair.root_doc)
@@ -535,48 +561,45 @@ def merge(
qs = Document.objects.select_related("root_document").filter(id__in=doc_ids) qs = Document.objects.select_related("root_document").filter(id__in=doc_ids)
docs_by_id = {doc.id: doc for doc in qs} docs_by_id = {doc.id: doc for doc in qs}
affected_docs: list[int] = [] affected_docs: list[int] = []
import pikepdf
merged_pdf = pikepdf.new()
version: str = merged_pdf.pdf_version
handoff_asn: int | None = None handoff_asn: int | None = None
# use doc_ids to preserve order with pdf_ops.PdfMerger() as merger:
for doc_id in doc_ids: # use doc_ids to preserve order
doc = docs_by_id.get(doc_id) for doc_id in doc_ids:
if doc is None: doc = docs_by_id.get(doc_id)
continue if doc is None:
pair = _resolve_root_and_source_doc(doc, source_mode=source_mode) continue
try: pair = _resolve_root_and_source_doc(doc, source_mode=source_mode)
doc_path = ( try:
pair.source_doc.archive_path # archive_path is None when there is no archive version
if archive_fallback archive_path = (
and pair.source_doc.mime_type != "application/pdf" pair.source_doc.archive_path
and pair.source_doc.has_archive_version if archive_fallback
else pair.source_doc.source_path and pair.source_doc.mime_type != "application/pdf"
) else None
with pikepdf.open(str(doc_path)) as pdf: )
version = max(version, pdf.pdf_version) merger.add(
merged_pdf.pages.extend(pdf.pages) archive_path
affected_docs.append(doc.id) if archive_path is not None
if handoff_asn is None and doc.archive_serial_number is not None: else pair.source_doc.source_path,
handoff_asn = doc.archive_serial_number )
except Exception as e: affected_docs.append(doc.id)
logger.exception( if handoff_asn is None and doc.archive_serial_number is not None:
f"Error merging document {doc.id}, it will not be included in the merge: {e}", handoff_asn = doc.archive_serial_number
) except Exception as e:
if len(affected_docs) == 0: logger.exception(
logger.warning("No documents were merged") f"Error merging document {doc.id}, it will not be included in the merge: {e}",
return "OK" )
if len(affected_docs) == 0:
logger.warning("No documents were merged")
return "OK"
filepath = ( filepath = (
Path( Path(
tempfile.mkdtemp(dir=settings.SCRATCH_DIR), tempfile.mkdtemp(dir=settings.SCRATCH_DIR),
)
/ f"{'_'.join([str(doc_id) for doc_id in affected_docs])[:100]}_merged.pdf"
) )
/ f"{'_'.join([str(doc_id) for doc_id in affected_docs])[:100]}_merged.pdf" merger.save(filepath)
)
merged_pdf.remove_unreferenced_resources()
merged_pdf.save(filepath, min_version=version)
merged_pdf.close()
if metadata_document_id: if metadata_document_id:
metadata_document = qs.get(id=metadata_document_id) metadata_document = qs.get(id=metadata_document_id)
@@ -752,64 +775,60 @@ def split(
) )
doc = Document.objects.select_related("root_document").get(id=doc_ids[0]) doc = Document.objects.select_related("root_document").get(id=doc_ids[0])
pair = _resolve_root_and_source_doc(doc, source_mode=source_mode) pair = _resolve_root_and_source_doc(doc, source_mode=source_mode)
import pikepdf
consume_tasks = [] consume_tasks = []
try: try:
with pikepdf.open(pair.source_doc.source_path) as pdf: outputs = [
for idx, split_doc in enumerate(pages): (
dst: pikepdf.Pdf = pikepdf.new() [pdf_ops.PageSpec(page) for page in split_doc],
for page in split_doc: partial(_scratch_path, f"{doc.id}_{split_doc[0]}-{split_doc[-1]}.pdf"),
dst.pages.append(pdf.pages[page - 1]) )
filepath: Path = ( for split_doc in pages
Path( ]
tempfile.mkdtemp(dir=settings.SCRATCH_DIR), filepaths = pdf_ops.build_pdfs(pair.source_doc.source_path, outputs)
)
/ f"{doc.id}_{split_doc[0]}-{split_doc[-1]}.pdf"
)
dst.remove_unreferenced_resources()
dst.save(filepath)
dst.close()
overrides: DocumentMetadataOverrides = ( for idx, (split_doc, filepath) in enumerate(
DocumentMetadataOverrides().from_document(doc) zip(pages, filepaths, strict=True),
) ):
overrides.title = f"{doc.title} (split {idx + 1})" overrides: DocumentMetadataOverrides = (
if user is not None: DocumentMetadataOverrides().from_document(doc)
overrides.owner_id = user.id )
if not delete_originals: overrides.title = f"{doc.title} (split {idx + 1})"
overrides.skip_asn_if_exists = True if user is not None:
logger.info( overrides.owner_id = user.id
f"Adding split document with pages {split_doc} to the task queue.", if not delete_originals:
) overrides.skip_asn_if_exists = True
consume_tasks.append( logger.info(
consume_file.s( f"Adding split document with pages {split_doc} to the task queue.",
input_doc=ConsumableDocument( )
source=DocumentSource.ConsumeFolder, consume_tasks.append(
original_file=filepath, consume_file.s(
), input_doc=ConsumableDocument(
overrides=overrides, source=DocumentSource.ConsumeFolder,
).set(headers={"trigger_source": trigger_source}), original_file=filepath,
) ),
overrides=overrides,
).set(headers={"trigger_source": trigger_source}),
)
if delete_originals: if delete_originals:
backup = release_archive_serial_numbers([doc.id]) backup = release_archive_serial_numbers([doc.id])
logger.info( logger.info(
"Queueing removal of original document after consumption of the split documents", "Queueing removal of original document after consumption of the split documents",
) )
try: try:
chord( chord(
header=consume_tasks, header=consume_tasks,
body=delete.si([doc.id]), body=delete.si([doc.id]),
).on_error( ).on_error(
restore_archive_serial_numbers_task.s(backup), restore_archive_serial_numbers_task.s(backup),
).apply_async() ).apply_async()
except Exception: except Exception:
restore_archive_serial_numbers(backup) restore_archive_serial_numbers(backup)
raise raise
else: else:
group(consume_tasks).delay() group(consume_tasks).delay()
except Exception as e: except Exception as e:
logger.exception(f"Error splitting document {doc.id}: {e}") logger.exception(f"Error splitting document {doc.id}: {e}")
@@ -830,8 +849,6 @@ def delete_pages(
) )
doc = Document.objects.select_related("root_document").get(id=doc_ids[0]) doc = Document.objects.select_related("root_document").get(id=doc_ids[0])
pair = _resolve_root_and_source_doc(doc, source_mode=source_mode) pair = _resolve_root_and_source_doc(doc, source_mode=source_mode)
pages = sorted(pages) # sort pages to avoid index issues
import pikepdf
try: try:
# Produce edited PDF to a temp file and create a new version # Produce edited PDF to a temp file and create a new version
@@ -839,13 +856,7 @@ def delete_pages(
Path(tempfile.mkdtemp(dir=settings.SCRATCH_DIR)) Path(tempfile.mkdtemp(dir=settings.SCRATCH_DIR))
/ f"{pair.root_doc.id}_pages_deleted.pdf" / f"{pair.root_doc.id}_pages_deleted.pdf"
) )
with pikepdf.open(pair.source_doc.source_path) as pdf: pdf_ops.remove_pages(pair.source_doc.source_path, filepath, pages)
offset = 1 # pages are 1-indexed
for page_num in pages:
pdf.pages.remove(pdf.pages[page_num - offset])
offset += 1 # remove() changes the index of the pages
pdf.remove_unreferenced_resources()
pdf.save(filepath)
overrides = DocumentMetadataOverrides().from_document(pair.root_doc) overrides = DocumentMetadataOverrides().from_document(pair.root_doc)
if user is not None: if user is not None:
@@ -894,47 +905,28 @@ def edit_pdf(
) )
doc = Document.objects.select_related("root_document").get(id=doc_ids[0]) doc = Document.objects.select_related("root_document").get(id=doc_ids[0])
pair = _resolve_root_and_source_doc(doc, source_mode=source_mode) pair = _resolve_root_and_source_doc(doc, source_mode=source_mode)
import pikepdf
pdf_docs: list[pikepdf.Pdf] = []
try: try:
if not operations: output_count = pdf_ops.validate_page_operations(
raise ValueError("Output document index is out of bounds") operations,
single_output=update_document,
max_idx = max(op.get("doc", 0) for op in operations) )
if update_document and max_idx > 0: page_specs: list[list[pdf_ops.PageSpec]] = [[] for _ in range(output_count)]
logger.error( for op in operations:
"Update requested but multiple output documents specified", page_specs[op.get("doc", 0)].append(
pdf_ops.PageSpec(op["page"], op.get("rotate", 0)),
) )
raise ValueError("Multiple output documents specified")
if any(
op.get("doc", 0) < 0 or op.get("doc", 0) >= len(operations)
for op in operations
):
raise ValueError("Output document index is out of bounds")
with pikepdf.open(pair.source_doc.source_path) as src:
# prepare output documents
pdf_docs = [pikepdf.new() for _ in range(max_idx + 1)]
for op in operations:
dst = pdf_docs[op.get("doc", 0)]
page = src.pages[op["page"] - 1]
dst.pages.append(page)
if op.get("rotate"):
dst.pages[-1].rotate(op["rotate"], relative=True)
if update_document: if update_document:
# Create a new version from the edited PDF rather than replacing in-place # Create a new version from the edited PDF rather than replacing in-place
pdf = pdf_docs[0] (filepath,) = pdf_ops.build_pdfs(
pdf.remove_unreferenced_resources() pair.source_doc.source_path,
filepath: Path = ( [
Path(tempfile.mkdtemp(dir=settings.SCRATCH_DIR)) (
/ f"{pair.root_doc.id}_edited.pdf" page_specs[0],
partial(_scratch_path, f"{pair.root_doc.id}_edited.pdf"),
),
],
) )
pdf.save(filepath)
overrides = ( overrides = (
DocumentMetadataOverrides().from_document(pair.root_doc) DocumentMetadataOverrides().from_document(pair.root_doc)
if include_metadata if include_metadata
@@ -955,6 +947,19 @@ def edit_pdf(
headers={"trigger_source": trigger_source}, headers={"trigger_source": trigger_source},
) )
else: else:
version_filepaths = pdf_ops.build_pdfs(
pair.source_doc.source_path,
[
(
specs,
partial(
_scratch_path,
f"{pair.root_doc.id}_edit_{idx}.pdf",
),
)
for idx, specs in enumerate(page_specs, start=1)
],
)
consume_tasks = [] consume_tasks = []
overrides = ( overrides = (
DocumentMetadataOverrides().from_document(pair.root_doc) DocumentMetadataOverrides().from_document(pair.root_doc)
@@ -966,15 +971,9 @@ def edit_pdf(
overrides.actor_id = user.id overrides.actor_id = user.id
if not delete_original: if not delete_original:
overrides.skip_asn_if_exists = True overrides.skip_asn_if_exists = True
if delete_original and len(pdf_docs) == 1: if delete_original and output_count == 1:
overrides.asn = pair.root_doc.archive_serial_number overrides.asn = pair.root_doc.archive_serial_number
for idx, pdf in enumerate(pdf_docs, start=1): for version_filepath in version_filepaths:
version_filepath: Path = (
Path(tempfile.mkdtemp(dir=settings.SCRATCH_DIR))
/ f"{pair.root_doc.id}_edit_{idx}.pdf"
)
pdf.remove_unreferenced_resources()
pdf.save(version_filepath)
consume_tasks.append( consume_tasks.append(
consume_file.s( consume_file.s(
input_doc=ConsumableDocument( input_doc=ConsumableDocument(
@@ -1024,8 +1023,6 @@ def remove_password(
""" """
Remove password protection from PDF documents. Remove password protection from PDF documents.
""" """
import pikepdf
for doc_id in doc_ids: for doc_id in doc_ids:
doc = Document.objects.select_related("root_document").get(id=doc_id) doc = Document.objects.select_related("root_document").get(id=doc_id)
pair = _resolve_root_and_source_doc(doc, source_mode=source_mode) pair = _resolve_root_and_source_doc(doc, source_mode=source_mode)
@@ -1039,76 +1036,69 @@ def remove_password(
doc.id, doc.id,
pair.source_doc.source_path, pair.source_doc.source_path,
) )
try: if not pdf_ops.needs_decrypt(source_path):
with pikepdf.open(source_path) as pdf: logger.info(
if not pdf.is_encrypted: "Skipping password removal for document %s because the "
logger.info( "source PDF is not encrypted",
"Skipping password removal for document %s because the " pair.root_doc.id,
"source PDF is not encrypted",
pair.root_doc.id,
)
continue
except pikepdf.PasswordError:
# Password-protected PDFs need the supplied password below.
pass
with pikepdf.open(source_path, password=password) as pdf:
filepath: Path = (
Path(tempfile.mkdtemp(dir=settings.SCRATCH_DIR))
/ f"{pair.root_doc.id}_unprotected.pdf"
) )
pdf.remove_unreferenced_resources() continue
pdf.save(filepath)
if update_document: filepath = pdf_ops.decrypt_pdf(
# Create a new version rather than modifying the root/original in place. source_path,
overrides = ( partial(_scratch_path, f"{pair.root_doc.id}_unprotected.pdf"),
DocumentMetadataOverrides().from_document(pair.root_doc) password,
if include_metadata )
else DocumentMetadataOverrides()
) if update_document:
if user is not None: # Create a new version rather than modifying the root/original in place.
overrides.owner_id = user.id overrides = (
overrides.actor_id = user.id DocumentMetadataOverrides().from_document(pair.root_doc)
consume_file.apply_async( if include_metadata
kwargs={ else DocumentMetadataOverrides()
"input_doc": ConsumableDocument( )
source=DocumentSource.ConsumeFolder, if user is not None:
original_file=filepath, overrides.owner_id = user.id
root_document_id=pair.root_doc.id, overrides.actor_id = user.id
), consume_file.apply_async(
"overrides": overrides, kwargs={
}, "input_doc": ConsumableDocument(
headers={"trigger_source": trigger_source}, source=DocumentSource.ConsumeFolder,
) original_file=filepath,
root_document_id=pair.root_doc.id,
),
"overrides": overrides,
},
headers={"trigger_source": trigger_source},
)
else:
consume_tasks = []
overrides = (
DocumentMetadataOverrides().from_document(pair.root_doc)
if include_metadata
else DocumentMetadataOverrides()
)
if user is not None:
overrides.owner_id = user.id
overrides.actor_id = user.id
consume_tasks.append(
consume_file.s(
input_doc=ConsumableDocument(
source=DocumentSource.ConsumeFolder,
original_file=filepath,
),
overrides=overrides,
).set(headers={"trigger_source": trigger_source}),
)
if delete_original:
chord(
header=consume_tasks,
body=delete.si([doc.id]),
).delay()
else: else:
consume_tasks = [] group(consume_tasks).delay()
overrides = (
DocumentMetadataOverrides().from_document(pair.root_doc)
if include_metadata
else DocumentMetadataOverrides()
)
if user is not None:
overrides.owner_id = user.id
overrides.actor_id = user.id
consume_tasks.append(
consume_file.s(
input_doc=ConsumableDocument(
source=DocumentSource.ConsumeFolder,
original_file=filepath,
),
overrides=overrides,
).set(headers={"trigger_source": trigger_source}),
)
if delete_original:
chord(
header=consume_tasks,
body=delete.si([doc.id]),
).delay()
else:
group(consume_tasks).delay()
except Exception as e: except Exception as e:
logger.exception( logger.exception(
+18 -2
View File
@@ -50,6 +50,7 @@ from documents.models import ShareLink
from documents.models import ShareLinkBundle from documents.models import ShareLinkBundle
from documents.models import StoragePath from documents.models import StoragePath
from documents.models import Tag from documents.models import Tag
from documents.permissions import annotate_authorizing_fields
from documents.permissions import permitted_document_ids from documents.permissions import permitted_document_ids
from documents.permissions import permitted_object_ids from documents.permissions import permitted_object_ids
from documents.versioning import annotate_effective_content from documents.versioning import annotate_effective_content
@@ -1075,6 +1076,9 @@ class PermittedObjectsFilter(BaseFilterBackend):
include_granted: bool = True include_granted: bool = True
perm_codename: str | None = None perm_codename: str | None = None
# A self-referencing foreign key whose target authorizes a row, so a row is
# judged by its parent's owner and grants (``Document.root_document``).
parent_field: str | None = None
def filter_queryset(self, request, queryset, view): def filter_queryset(self, request, queryset, view):
# Before the superuser and owner-only paths, neither of which consults # Before the superuser and owner-only paths, neither of which consults
@@ -1086,11 +1090,23 @@ class PermittedObjectsFilter(BaseFilterBackend):
if request.user.is_superuser: if request.user.is_superuser:
return queryset return queryset
if not self.include_granted: if not self.include_granted:
return queryset.filter(Q(owner=request.user) | Q(owner__isnull=True)) owner_field = "owner"
if self.parent_field is not None:
queryset = annotate_authorizing_fields(queryset, self.parent_field)
owner_field = "authorizing_owner"
return queryset.filter(
Q(**{owner_field: request.user.pk})
| Q(**{f"{owner_field}__isnull": True}),
)
model = queryset.model model = queryset.model
perm = self.perm_codename or f"view_{model._meta.model_name}" perm = self.perm_codename or f"view_{model._meta.model_name}"
return queryset.filter( return queryset.filter(
id__in=permitted_object_ids(request.user, model, perm), id__in=permitted_object_ids(
request.user,
model,
perm,
parent_field=self.parent_field,
),
) )
@@ -514,8 +514,11 @@ class Command(CryptMixin, PaperlessCommand):
else: else:
base_name = document.get_public_filename(counter=filename_counter) base_name = document.get_public_filename(counter=filename_counter)
if base_name not in self.exported_files: # The thumbnail and archive names are derived from the stem, so
self.exported_files.add(base_name) # uniqueness must hold without the original's extension
unique_key = str(Path(base_name).with_suffix(""))
if unique_key not in self.exported_files:
self.exported_files.add(unique_key)
break break
else: else:
filename_counter += 1 filename_counter += 1
@@ -67,18 +67,22 @@ class Command(PaperlessCommand):
if options.get("recreate"): if options.get("recreate"):
wipe_index(settings.INDEX_DIR) wipe_index(settings.INDEX_DIR)
documents = Document.objects.select_related( documents = (
"correspondent", Document.objects.filter(root_document__isnull=True)
"document_type", .select_related(
"storage_path", "correspondent",
"owner", "document_type",
).prefetch_related( "storage_path",
"tags", "owner",
"notes__user", )
"custom_fields__field", .prefetch_related(
"versions", "tags",
"barcodes", "notes__user",
"versions__barcodes", "custom_fields__field",
"versions",
"barcodes",
"versions__barcodes",
)
) )
total = documents.count() total = documents.count()
rebuild_kwargs = {} rebuild_kwargs = {}
+194
View File
@@ -0,0 +1,194 @@
"""
Pure PDF page operations used by documents.bulk_edit.
This module deliberately knows nothing about Django, Celery or the documents
app: callers resolve documents, choose output paths and queue work. Every
function that writes a PDF removes unreferenced resources before saving.
pikepdf is always called as ``pikepdf.open(...)`` / ``pikepdf.new()`` (never
``from pikepdf import open``) so tests can patch those module attributes.
"""
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import NamedTuple
import pikepdf
if TYPE_CHECKING:
from collections.abc import Callable
from collections.abc import Iterable
from collections.abc import Mapping
from collections.abc import Sequence
from pathlib import Path
from types import TracebackType
class PageSpec(NamedTuple):
"""One page of an output PDF: a 1-indexed source page, optionally rotated."""
page: int
rotate: int = 0 # relative degrees, 0 leaves the page alone
def _require_positive(pages: Iterable[int]) -> None:
for page in pages:
if page < 1:
raise ValueError(f"Page numbers start at 1, got {page}")
def rotate_pdf(src: Path, dst: Path, degrees: int) -> None:
"""
Rotate every page relatively on the opened document, not a rebuild, so Info,
XMP and outlines are kept. ``src`` is not modified.
"""
with pikepdf.open(src) as pdf:
for page in pdf.pages:
page.rotate(degrees, relative=True)
pdf.remove_unreferenced_resources()
pdf.save(dst)
def remove_pages(src: Path, dst: Path, pages: Iterable[int]) -> None:
"""
Remove 1-indexed pages from the opened document, not a rebuild, so Info, XMP
and outlines are kept. ``src`` is not modified.
Duplicates are ignored. Pages are removed highest first so earlier removals
never shift the index of later ones.
"""
unique = sorted(set(pages))
_require_positive(unique)
with pikepdf.open(src) as pdf:
for page_num in reversed(unique):
del pdf.pages[page_num - 1]
pdf.remove_unreferenced_resources()
pdf.save(dst)
def build_pdfs(
src: Path,
outputs: Sequence[tuple[Sequence[PageSpec], Callable[[], Path]]],
) -> list[Path]:
"""
Build one new PDF per output from pages of ``src``, opening ``src`` once.
Each output is ``(page_specs, make_dst)``. Every page number is checked against
``src`` before any output is built, and ``make_dst`` is called after that
output's pages are copied and immediately before it is saved, so a bad page
number in any output never leaves a destination behind. Document-level data
(Info, XMP, outlines) is not carried over. Returns the written paths in output
order.
"""
for specs, _ in outputs:
_require_positive(spec.page for spec in specs)
written: list[Path] = []
with pikepdf.open(src) as source:
page_count = len(source.pages)
for specs, _ in outputs:
for spec in specs:
if spec.page > page_count:
raise IndexError(
f"Page {spec.page} is out of range, the PDF has "
f"{page_count} pages",
)
for specs, make_dst in outputs:
dst = pikepdf.new()
for spec in specs:
dst.pages.append(source.pages[spec.page - 1])
if spec.rotate:
dst.pages[-1].rotate(spec.rotate, relative=True)
dst.remove_unreferenced_resources()
path = make_dst()
dst.save(path)
dst.close()
written.append(path)
return written
def validate_page_operations(
operations: Sequence[Mapping[str, int]],
*,
single_output: bool,
) -> int:
"""
Validate ``edit_pdf`` style operations and return the output document count.
Each operation has ``page`` and optionally ``rotate`` and ``doc`` (the output
document index, default 0). The bounds rule is kept as it was: a ``doc`` index
must be below the number of operations.
"""
if not operations:
raise ValueError("Output document index is out of bounds")
max_idx = max(op.get("doc", 0) for op in operations)
if single_output and max_idx > 0:
raise ValueError("Multiple output documents specified")
if any(
op.get("doc", 0) < 0 or op.get("doc", 0) >= len(operations) for op in operations
):
raise ValueError("Output document index is out of bounds")
return max_idx + 1
def needs_decrypt(src: Path) -> bool:
"""
True if ``src`` is encrypted. A PDF that needs a password to open at all
counts as encrypted.
"""
try:
with pikepdf.open(src) as pdf:
return bool(pdf.is_encrypted)
except pikepdf.PasswordError:
return True
def decrypt_pdf(src: Path, make_dst: Callable[[], Path], password: str) -> Path:
"""
Write an unencrypted copy of ``src`` and return its path.
``make_dst`` is only called once the password has been accepted, so a wrong
password never leaves a destination behind.
"""
with pikepdf.open(src, password=password) as pdf:
pdf.remove_unreferenced_resources()
dst = make_dst()
pdf.save(dst)
return dst
class PdfMerger:
"""
Accumulates the pages of several PDFs into one new PDF.
``add`` raises if a source cannot be read; deciding whether to skip it is the
caller's policy. Use as a context manager so the merged PDF is closed.
"""
def __init__(self) -> None:
self._pdf = pikepdf.new()
self._version: str = self._pdf.pdf_version
def __enter__(self) -> PdfMerger:
return self
def __exit__(
self,
exc_type: type[BaseException] | None,
exc: BaseException | None,
tb: TracebackType | None,
) -> None:
self._pdf.close()
def add(self, path: Path) -> None:
with pikepdf.open(str(path)) as pdf:
self._version = max(self._version, pdf.pdf_version)
self._pdf.pages.extend(pdf.pages)
def save(self, dst: Path) -> None:
self._pdf.remove_unreferenced_resources()
self._pdf.save(dst, min_version=self._version)
+86 -16
View File
@@ -6,7 +6,9 @@ from django.contrib.auth.models import Permission
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import Case from django.db.models import Case
from django.db.models import CharField
from django.db.models import Count from django.db.models import Count
from django.db.models import F
from django.db.models import IntegerField from django.db.models import IntegerField
from django.db.models import Model from django.db.models import Model
from django.db.models import Q from django.db.models import Q
@@ -14,6 +16,7 @@ from django.db.models import QuerySet
from django.db.models import Value from django.db.models import Value
from django.db.models import When from django.db.models import When
from django.db.models.functions import Cast from django.db.models.functions import Cast
from django.db.models.functions import Coalesce
from guardian.core import ObjectPermissionChecker from guardian.core import ObjectPermissionChecker
from guardian.models import GroupObjectPermission from guardian.models import GroupObjectPermission
from guardian.models import UserObjectPermission from guardian.models import UserObjectPermission
@@ -25,6 +28,7 @@ from rest_framework.permissions import BasePermission
from rest_framework.permissions import DjangoObjectPermissions from rest_framework.permissions import DjangoObjectPermissions
from documents.models import Document from documents.models import Document
from documents.versioning import get_root_document
class PaperlessObjectPermissions(DjangoObjectPermissions): class PaperlessObjectPermissions(DjangoObjectPermissions):
@@ -348,6 +352,7 @@ def permitted_object_ids(
perm: str, perm: str,
*, *,
include_deleted: bool = False, include_deleted: bool = False,
parent_field: str | None = None,
) -> QuerySet[int]: ) -> QuerySet[int]:
""" """
Generic version of ``permitted_document_ids`` for any model with an Generic version of ``permitted_document_ids`` for any model with an
@@ -356,6 +361,24 @@ def permitted_object_ids(
soft-delete pattern (currently only ``Document``); for every other model soft-delete pattern (currently only ``Document``); for every other model
it is accepted but has no effect, since those models have no soft-delete it is accepted but has no effect, since those models have no soft-delete
concept. concept.
``parent_field`` names a self-referencing foreign key whose target
authorizes the row (``Document.root_document``). A row with a parent is
visible exactly when its parent is, judged by the parent's owner and
grants, so the row's own owner and grants are ignored.
Guardian stores ``object_pk`` as a string, so the row key is cast to a
string and tested against the user's and groups' grants with a single
uncorrelated ``IN``. Postgres and SQLite build that set once. MariaDB
evaluates it as an index probe per row, which is cheap because the
lookups use guardian's unique indexes. Casting every ``object_pk`` to an
integer instead cannot use an index, and MariaDB cannot materialize it
inside the owner ``OR``, so it re-scans the user's grants for every row.
A correlated ``EXISTS`` per grant fixes MariaDB too, but Postgres and
SQLite re-run it for every row and end up slower than the original. The
user's groups are matched with an ``IN`` subquery rather than a join
through the membership table, which SQLite plans badly once the grant
tables grow.
""" """
has_soft_delete = hasattr(model, "global_objects") has_soft_delete = hasattr(model, "global_objects")
manager = ( manager = (
@@ -363,8 +386,14 @@ def permitted_object_ids(
) )
base_qs = manager.all().only("id", "owner") base_qs = manager.all().only("id", "owner")
owner_field, key_field = "owner", "pk"
if parent_field is not None:
owner_field, key_field = "authorizing_owner", "authorizing_id"
base_qs = annotate_authorizing_fields(base_qs, parent_field)
unowned = Q(**{f"{owner_field}__isnull": True})
if user is None or not getattr(user, "is_authenticated", False): if user is None or not getattr(user, "is_authenticated", False):
return base_qs.filter(owner__isnull=True).values_list("id", flat=True) return base_qs.filter(unowned).values_list("id", flat=True)
# Deactivated users get nothing, deactivated superusers included, so this # Deactivated users get nothing, deactivated superusers included, so this
# has to come before the superuser shortcut. guardian's # has to come before the superuser shortcut. guardian's
@@ -388,26 +417,51 @@ def permitted_object_ids(
"permission__content_type": content_type, "permission__content_type": content_type,
} }
user_perm_ids = ( # Both grant sets are compared to the row key as strings, exactly as
UserObjectPermission.objects.filter(user=user, **perm_filter) # guardian stores them, and are uncorrelated, so each engine can build the
.annotate(object_pk_int=Cast("object_pk", IntegerField())) # set once instead of probing per row.
.values_list("object_pk_int", flat=True) user_keys = UserObjectPermission.objects.filter(
) user=user,
group_perm_ids = ( **perm_filter,
GroupObjectPermission.objects.filter(group__user=user, **perm_filter) ).values_list("object_pk", flat=True)
.annotate(object_pk_int=Cast("object_pk", IntegerField())) group_keys = GroupObjectPermission.objects.filter(
.values_list("object_pk_int", flat=True) group_id__in=user.groups.values("id"),
) **perm_filter,
permitted_ids = user_perm_ids.union(group_perm_ids) ).values_list("object_pk", flat=True)
permitted_keys = user_keys.union(group_keys, all=True)
return base_qs.filter( return (
Q(owner=user) | Q(owner__isnull=True) | Q(id__in=permitted_ids), base_qs.annotate(permitted_key=Cast(key_field, CharField(max_length=64)))
).values_list("id", flat=True) .filter(
Q(**{owner_field: user.pk}) | unowned | Q(permitted_key__in=permitted_keys),
)
.values_list("id", flat=True)
)
ModelT = TypeVar("ModelT", bound=Model) ModelT = TypeVar("ModelT", bound=Model)
def annotate_authorizing_fields(
queryset: QuerySet[ModelT],
parent_field: str,
) -> QuerySet[ModelT]:
"""
Annotate each row with ``authorizing_id`` and ``authorizing_owner``: the id
and owner of the row that authorizes it. A row with a parent (the
self-referencing foreign key ``parent_field``) is authorized by its parent,
any other row by itself.
"""
return queryset.annotate(
authorizing_id=Coalesce(f"{parent_field}_id", "id"),
authorizing_owner=Case(
When(**{f"{parent_field}_id__isnull": True}, then=F("owner_id")),
default=F(f"{parent_field}__owner_id"),
output_field=IntegerField(),
),
)
def user_is_unrestricted(user: User | None) -> bool: def user_is_unrestricted(user: User | None) -> bool:
""" """
True when ``user`` means "no restriction at all" (an absent user, or an True when ``user`` means "no restriction at all" (an absent user, or an
@@ -470,8 +524,17 @@ def permitted_document_ids(
``include_deleted=True`` for callers that need to check permission on ``include_deleted=True`` for callers that need to check permission on
soft-deleted documents (e.g. trash restore). This intentionally avoids soft-deleted documents (e.g. trash restore). This intentionally avoids
``get_objects_for_user`` to keep the subquery small and index-friendly. ``get_objects_for_user`` to keep the subquery small and index-friendly.
A version is authorized by its root document, so a version's own owner and
grants never matter.
""" """
return permitted_object_ids(user, Document, perm, include_deleted=include_deleted) return permitted_object_ids(
user,
Document,
perm,
include_deleted=include_deleted,
parent_field="root_document",
)
def get_document_count_filter_for_user(user, related_name: str = "documents"): def get_document_count_filter_for_user(user, related_name: str = "documents"):
@@ -630,7 +693,14 @@ def has_perms_owner_aware(user, perms, obj):
single-object check still has many production callers. Several callers single-object check still has many production callers. Several callers
remain across ``documents/``, ``paperless_mail/``, and ``paperless_ai/`` remain across ``documents/``, ``paperless_mail/``, and ``paperless_ai/``
-- grep for this function name before removing it. -- grep for this function name before removing it.
A document version is authorized by its root document, like in
``permitted_document_ids``, so a version's own owner and grants never
matter. Fetch the root with ``select_related("root_document__owner")`` to
avoid extra queries.
""" """
if isinstance(obj, Document):
obj = get_root_document(obj)
checker = ObjectPermissionChecker(user) checker = ObjectPermissionChecker(user)
return obj.owner is None or obj.owner == user or checker.has_perm(perms, obj) return obj.owner is None or obj.owner == user or checker.has_perm(perms, obj)
+38 -4
View File
@@ -7,6 +7,7 @@ import threading
import time import time
from datetime import UTC from datetime import UTC
from datetime import datetime from datetime import datetime
from datetime import timedelta
from enum import StrEnum from enum import StrEnum
from itertools import islice from itertools import islice
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
@@ -55,6 +56,19 @@ if TYPE_CHECKING:
logger = logging.getLogger("paperless.search") logger = logging.getLogger("paperless.search")
# tantivy stores dates as signed 64-bit nanoseconds since the Unix epoch, which
# covers 1677-09-21T00:12:43 to 2262-04-11T23:47:16 UTC
_INDEX_DATE_NANOS_MIN: Final[int] = -(2**63)
_INDEX_DATE_NANOS_MAX: Final[int] = 2**63 - 1
_UNIX_EPOCH: Final[datetime] = datetime(1970, 1, 1, tzinfo=UTC)
def _is_indexable_date(value: datetime) -> bool:
"""Whether value, at whole-second precision, fits tantivy's date range."""
nanos = ((value - _UNIX_EPOCH) // timedelta(seconds=1)) * 1_000_000_000
return _INDEX_DATE_NANOS_MIN <= nanos <= _INDEX_DATE_NANOS_MAX
_LOCK_TIMEOUT_SECONDS: Final[float] = 10.0 # per-attempt acquire timeout _LOCK_TIMEOUT_SECONDS: Final[float] = 10.0 # per-attempt acquire timeout
_LOCK_RETRY_ATTEMPTS: Final[int] = 4 # total attempts (1 initial + 3 retries) _LOCK_RETRY_ATTEMPTS: Final[int] = 4 # total attempts (1 initial + 3 retries)
_LOCK_BACKOFF_BASE: Final[float] = 1.0 # seconds _LOCK_BACKOFF_BASE: Final[float] = 1.0 # seconds
@@ -270,9 +284,14 @@ class WriteBatch:
and adding the new version. This ensures stale document data (e.g., after and adding the new version. This ensures stale document data (e.g., after
permission changes) doesn't persist in the index. permission changes) doesn't persist in the index.
Only root documents are indexed, with their effective content, so a
version is indexed as its root document.
Args: Args:
document: Django Document instance to index document: Django Document instance to index
""" """
if document.root_document_id is not None:
document = document.root_document
self.remove(document.pk) self.remove(document.pk)
doc = self._backend._build_tantivy_doc(document) doc = self._backend._build_tantivy_doc(document)
self._writer.add_document(doc) self._writer.add_document(doc)
@@ -297,20 +316,22 @@ class WriteBatch:
An id with no matching document (e.g. deleted between the caller An id with no matching document (e.g. deleted between the caller
collecting ids and the batch running) is silently skipped, matching collecting ids and the batch running) is silently skipped, matching
``add_or_update()``'s existing single-document deferred-task behavior ``add_or_update()``'s existing single-document deferred-task behavior
rather than erroring or leaving a stale index entry. rather than erroring or leaving a stale index entry. The id of a
version stands for its root document.
Args: Args:
ids: Primary keys of Document instances to index ids: Primary keys of Document instances to index
""" """
from documents.models import Document from documents.models import Document
from documents.versioning import annotate_effective_content from documents.versioning import annotate_effective_content
from documents.versioning import root_document_ids
ids = list(ids) ids = list(ids)
if not ids: if not ids:
return return
queryset = annotate_effective_content( queryset = annotate_effective_content(
Document.objects.filter(pk__in=ids) Document.objects.filter(pk__in=root_document_ids(ids))
.select_related("correspondent", "document_type", "storage_path", "owner") .select_related("correspondent", "document_type", "storage_path", "owner")
.prefetch_related( .prefetch_related(
"tags", "tags",
@@ -628,7 +649,15 @@ class TantivyBackend:
document.created.day, document.created.day,
tzinfo=UTC, tzinfo=UTC,
) )
doc.add_date("created", created_date) if _is_indexable_date(created_date):
doc.add_date("created", created_date)
else:
logger.warning(
"Document %s has a created date (%s) outside the range the search "
"index can store; it will be indexed without a created date",
document.pk,
document.created,
)
doc.add_date("modified", document.modified) doc.add_date("modified", document.modified)
doc.add_date("added", document.added) doc.add_date("added", document.added)
@@ -1014,16 +1043,21 @@ class TantivyBackend:
excluded from results. excluded from results.
Args: Args:
doc_id: Primary key of the reference document doc_id: Primary key of the reference document, or of one of its
versions
user: User for permission filtering (None for no filtering) user: User for permission filtering (None for no filtering)
limit: Maximum number of IDs to return (None = all matching docs) limit: Maximum number of IDs to return (None = all matching docs)
Returns: Returns:
List of similar document IDs (excluding the original) List of similar document IDs (excluding the original)
""" """
from documents.versioning import root_document_ids
self._ensure_open() self._ensure_open()
searcher = self._index.searcher() searcher = self._index.searcher()
# Only root documents are indexed, so a version stands for its root
doc_id = next(iter(root_document_ids([doc_id])), doc_id)
id_query = tantivy.Query.term_query(self._schema, "id", doc_id) id_query = tantivy.Query.term_query(self._schema, "id", doc_id)
results = searcher.search(id_query, limit=1) results = searcher.search(id_query, limit=1)
+3 -1
View File
@@ -28,7 +28,9 @@ logger = logging.getLogger("paperless.search")
# columns dropped. tantivy compares schemas by ordered field list, so an # columns dropped. tantivy compares schemas by ordered field list, so an
# index built by v1 rejects every write against the v2 schema. # index built by v1 rejects every write against the v2 schema.
# v3 - barcodes JSON field for stored barcode contents # v3 - barcodes JSON field for stored barcode contents
SCHEMA_VERSION: Final[int] = 3 # v4 - root documents only. Earlier indexes may hold document versions under
# their own id and metadata, so they are rebuilt without them.
SCHEMA_VERSION: Final[int] = 4
# Present in the index directory from the moment a full rebuild starts until it # Present in the index directory from the moment a full rebuild starts until it
# finishes. If a rebuild is interrupted it is left behind, so the half-built # finishes. If a rebuild is interrupted it is left behind, so the half-built
+2
View File
@@ -2144,6 +2144,8 @@ class BulkEditSerializer(
raise serializers.ValidationError("pages must be a list") raise serializers.ValidationError("pages must be a list")
if not all(isinstance(i, int) for i in parameters["pages"]): if not all(isinstance(i, int) for i in parameters["pages"]):
raise serializers.ValidationError("pages must be a list of integers") raise serializers.ValidationError("pages must be a list of integers")
if any(i < 1 for i in parameters["pages"]):
raise serializers.ValidationError("pages must be positive integers")
def _validate_parameters_merge(self, parameters) -> None: def _validate_parameters_merge(self, parameters) -> None:
if "delete_originals" in parameters: if "delete_originals" in parameters:
+2
View File
@@ -502,6 +502,8 @@ def update_document_content_maybe_archive_file(
llm_index_add_or_update_document(document) llm_index_add_or_update_document(document)
clear_document_caches(document.pk) clear_document_caches(document.pk)
if document.root_document_id is not None:
clear_document_caches(document.root_document_id)
except Exception: except Exception:
logger.exception( logger.exception(
+177
View File
@@ -1,4 +1,6 @@
import json import json
import logging
from datetime import date
from pathlib import Path from pathlib import Path
import pytest import pytest
@@ -291,6 +293,80 @@ class TestAddOrUpdateIds:
assert backend.search_ids("updated", user=None) == [doc.pk] assert backend.search_ids("updated", user=None) == [doc.pk]
class TestVersionsAreIndexedAsTheirRoot:
"""Only root documents are indexed, with their effective content, so
every write path that is handed a version indexes its root instead."""
@staticmethod
def _root_with_version() -> tuple[Document, Document]:
root = DocumentFactory(title="Statement", content="stale text")
version = DocumentFactory(
title="Statement",
content="latest text",
root_document=root,
version_index=1,
)
return root, version
def test_add_or_update_indexes_the_root_of_a_version(
self,
backend: TantivyBackend,
) -> None:
"""
GIVEN:
- A root document with a version
WHEN:
- The version is passed to add_or_update
THEN:
- The root is indexed with the version's text, and the version is not
"""
root, version = self._root_with_version()
backend.add_or_update(version)
assert backend.search_ids("latest", user=None) == [root.pk]
assert backend.search_ids("stale", user=None) == []
def test_add_or_update_ids_indexes_each_root_once(
self,
backend: TantivyBackend,
) -> None:
"""
GIVEN:
- A root document with a version
WHEN:
- Both ids are passed to add_or_update_ids
THEN:
- The root is indexed once and the version is not indexed
"""
root, version = self._root_with_version()
with backend.batch_update() as batch:
batch.add_or_update_ids([version.pk, root.pk])
assert backend.search_ids("Statement", user=None) == [root.pk]
assert backend.search_ids("latest", user=None) == [root.pk]
def test_add_or_update_ids_resolves_a_lone_version(
self,
backend: TantivyBackend,
) -> None:
"""
GIVEN:
- A root document with a version
WHEN:
- Only the version's id is passed to add_or_update_ids
THEN:
- The root is indexed
"""
root, version = self._root_with_version()
with backend.batch_update() as batch:
batch.add_or_update_ids([version.pk])
assert backend.search_ids("latest", user=None) == [root.pk]
class TestSearch: class TestSearch:
"""Test search query parsing and matching via search_ids.""" """Test search query parsing and matching via search_ids."""
@@ -904,6 +980,79 @@ class TestRebuild:
assert ids == [doc.pk] assert ids == [doc.pk]
class TestCreatedDateOutOfRange:
"""The index stores dates as nanosecond i64 values (1677-09-22 to 2262-04-11).
A document whose created date falls outside that window must not abort
indexing: it is indexed without a created value and a warning names it.
"""
@pytest.mark.parametrize(
("created", "expected_warnings"),
[
pytest.param(date(1677, 9, 22), 0, id="first-representable-day"),
pytest.param(date(2262, 4, 11), 0, id="last-representable-day"),
pytest.param(date(1677, 9, 21), 1, id="day-before-first"),
pytest.param(date(2262, 4, 12), 1, id="day-after-last"),
pytest.param(date(16, 8, 30), 1, id="two-digit-year-read-as-year-16"),
pytest.param(date(9999, 12, 31), 1, id="max-python-date"),
],
)
def test_add_or_update_indexes_document_and_warns_when_out_of_range(
self,
backend: TantivyBackend,
caplog: pytest.LogCaptureFixture,
created: date,
expected_warnings: int,
) -> None:
"""
GIVEN:
- A document with a created date at or beyond the index date limits
WHEN:
- The document is added to the index
THEN:
- The document is indexed and searchable either way
- A warning naming the document is logged only for out-of-range dates
"""
doc = DocumentFactory(created=created, content="boundarycontent")
with caplog.at_level(logging.WARNING, logger="paperless.search"):
backend.add_or_update(doc)
assert backend.search_ids("boundarycontent", user=None) == [doc.pk]
warnings = [r for r in caplog.records if r.levelno == logging.WARNING]
assert len(warnings) == expected_warnings
if expected_warnings:
assert f"Document {doc.pk}" in warnings[0].getMessage()
def test_rebuild_continues_past_out_of_range_document(
self,
backend: TantivyBackend,
caplog: pytest.LogCaptureFixture,
) -> None:
"""
GIVEN:
- A document with an unrepresentable created date among valid ones
WHEN:
- The index is rebuilt
THEN:
- Rebuild completes and every document is searchable
- A warning names the offending document
"""
good = DocumentFactory(created=date(2016, 8, 30), content="rebuildcontent")
bad = DocumentFactory(created=date(16, 8, 30), content="rebuildcontent")
with caplog.at_level(logging.WARNING, logger="paperless.search"):
backend.rebuild(Document.objects.all())
assert sorted(backend.search_ids("rebuildcontent", user=None)) == sorted(
[good.pk, bad.pk],
)
warnings = [r for r in caplog.records if r.levelno == logging.WARNING]
assert len(warnings) == 1
assert f"Document {bad.pk}" in warnings[0].getMessage()
class TestAutocomplete: class TestAutocomplete:
"""Test autocomplete functionality.""" """Test autocomplete functionality."""
@@ -987,6 +1136,34 @@ class TestMoreLikeThis:
assert 150 not in ids assert 150 not in ids
assert 151 in ids assert 151 in ids
def test_more_like_this_ids_seeded_by_version_uses_root(
self,
backend: TantivyBackend,
) -> None:
"""A version is not indexed, so it must be looked up as its root."""
root = DocumentFactory.create(
title="Important document",
content="financial information report",
)
version = DocumentFactory.create(
title="Important document",
content="financial information report",
root_document=root,
version_index=1,
)
other = DocumentFactory.create(
title="Another document",
content="financial information report",
)
backend.add_or_update(root)
backend.add_or_update(other)
ids = backend.more_like_this_ids(doc_id=version.pk, user=None)
assert other.pk in ids
assert root.pk not in ids
assert version.pk not in ids
class TestSingleton: class TestSingleton:
"""Test get_backend() and reset_backend() singleton lifecycle.""" """Test get_backend() and reset_backend() singleton lifecycle."""
+30
View File
@@ -1843,6 +1843,36 @@ class TestBulkEditAPI(DirectoriesMixin, APITestCase):
m.assert_called_once() m.assert_called_once()
self.assertEqual(m.call_args.kwargs["pages"], [[1], [2, 3, 4], [5]]) self.assertEqual(m.call_args.kwargs["pages"], [[1], [2, 3, 4], [5]])
@mock.patch("documents.serialisers.bulk_edit.delete_pages")
def test_bulk_edit_delete_pages_rejects_pages_below_one(self, m) -> None:
"""
GIVEN:
- A legacy delete_pages bulk edit
WHEN:
- API to bulk edit is called with a page number below 1
THEN:
- API returns HTTP 400
- delete_pages is not called
"""
self.setup_mock(m, "delete_pages")
for pages in ([0], [-1], [1, 0]):
with self.subTest(pages=pages):
response = self.client.post(
"/api/documents/bulk_edit/",
json.dumps(
{
"documents": [self.doc2.id],
"method": "delete_pages",
"parameters": {"pages": pages},
},
),
content_type="application/json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(b"pages must be positive integers", response.content)
m.assert_not_called()
@mock.patch("documents.views.bulk_edit.rotate") @mock.patch("documents.views.bulk_edit.rotate")
def test_rotate_insufficient_permissions(self, m) -> None: def test_rotate_insufficient_permissions(self, m) -> None:
self.doc1.owner = User.objects.get(username="temp_admin") self.doc1.owner = User.objects.get(username="temp_admin")
@@ -8,6 +8,7 @@ from auditlog.models import LogEntry # type: ignore[import-untyped]
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase as DjangoTestCase from django.test import TestCase as DjangoTestCase
from django.test import override_settings
from django.utils import timezone from django.utils import timezone
from rest_framework import status from rest_framework import status
from rest_framework.test import APITestCase from rest_framework.test import APITestCase
@@ -16,13 +17,17 @@ from documents.data_models import DocumentSource
from documents.filters import EffectiveContentFilter from documents.filters import EffectiveContentFilter
from documents.filters import TitleContentFilter from documents.filters import TitleContentFilter
from documents.models import Document from documents.models import Document
from documents.models import Note
from documents.models import ShareLink
from documents.versioning import annotate_effective_content from documents.versioning import annotate_effective_content
from documents.views import DocumentSelectionMixin from documents.views import DocumentSelectionMixin
from paperless_testing.dirs import DirectoriesMixin from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory from paperless_testing.factories import DocumentFactory
from paperless_testing.factories import UserFactory from paperless_testing.factories import UserFactory
from paperless_testing.http import read_streaming_response from paperless_testing.http import read_streaming_response
from paperless_testing.permissions import grant_all_global
from paperless_testing.permissions import grant_global from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object
if TYPE_CHECKING: if TYPE_CHECKING:
from pathlib import Path from pathlib import Path
@@ -1043,3 +1048,152 @@ class TestBulkSelectionExcludesVersions(DjangoTestCase):
) )
self.assertEqual(selected, [root.id]) self.assertEqual(selected, [root.id])
class TestVersionActionPermissions(DirectoriesMixin, APITestCase):
def setUp(self):
super().setUp()
self.user = UserFactory()
grant_all_global(self.user)
self.client.force_authenticate(self.user)
self.root = DocumentFactory(owner=UserFactory())
self.version = DocumentFactory(root_document=self.root, owner=None)
@override_settings(AUDIT_LOG_ENABLED=True)
def test_actions_reject_stale_version_ownership(self):
note = Note.objects.create(document=self.version, note="Version note")
for owner in (None, self.user):
self.version.owner = owner
self.version.save(update_fields=["owner"])
for action in (
"notes",
"suggestions",
"ai_suggestions",
"history",
"share_links",
):
with self.subTest(owner=owner, action=action):
response = self.client.get(
f"/api/documents/{self.version.pk}/{action}/",
)
self.assertEqual(response.status_code, 403)
response = self.client.post(
f"/api/documents/{self.version.pk}/notes/",
{"note": "New note"},
)
self.assertEqual(response.status_code, 403)
response = self.client.delete(
f"/api/documents/{self.version.pk}/notes/?id={note.pk}",
)
self.assertEqual(response.status_code, 403)
response = self.client.post(
"/api/share_links/",
{"document": self.version.pk, "file_version": "original"},
)
self.assertEqual(response.status_code, 403)
response = self.client.post(
"/api/share_link_bundles/",
{"document_ids": [self.version.pk], "file_version": "original"},
format="json",
)
self.assertEqual(response.status_code, 400)
response = self.client.post(
"/api/documents/email/",
{
"documents": [self.version.pk],
"addresses": "recipient@example.com",
"subject": "Version",
"message": "Version",
},
format="json",
)
self.assertEqual(response.status_code, 403)
with (
mock.patch("documents.views.AIConfig") as ai_config,
mock.patch("documents.views.stream_chat_with_documents") as chat,
):
ai_config.return_value.ai_enabled = True
response = self.client.post(
"/api/documents/chat/",
{"q": "Version?", "document_id": self.version.pk},
format="json",
)
self.assertEqual(response.status_code, 403)
chat.assert_not_called()
self.assertTrue(Note.objects.filter(pk=note.pk).exists())
self.assertFalse(ShareLink.objects.exists())
@mock.patch("documents.views.build_share_link_bundle.apply_async")
def test_root_permissions_allow_sharing_a_private_version(self, build_mock):
self.version.owner = UserFactory()
self.version.save(update_fields=["owner"])
grant_object(self.user, self.root, "view_document", "change_document")
note = Note.objects.create(document=self.version, note="Version note")
response = self.client.get(f"/api/documents/{self.version.pk}/notes/")
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data[0]["id"], note.pk)
response = self.client.post(
"/api/share_links/",
{"document": self.version.pk, "file_version": "original"},
)
self.assertEqual(response.status_code, 201)
self.assertEqual(ShareLink.objects.get().document_id, self.version.pk)
response = self.client.get(f"/api/documents/{self.version.pk}/share_links/")
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 1)
response = self.client.post(
"/api/share_link_bundles/",
{"document_ids": [self.version.pk], "file_version": "original"},
format="json",
)
self.assertEqual(response.status_code, 201)
build_mock.assert_called_once()
def test_root_view_permission_does_not_allow_note_changes(self):
grant_object(self.user, self.root, "view_document")
note = Note.objects.create(document=self.version, note="Version note")
response = self.client.get(f"/api/documents/{self.version.pk}/notes/")
self.assertEqual(response.status_code, 200)
response = self.client.post(
f"/api/documents/{self.version.pk}/notes/",
{"note": "New note"},
)
self.assertEqual(response.status_code, 403)
response = self.client.delete(
f"/api/documents/{self.version.pk}/notes/?id={note.pk}",
)
self.assertEqual(response.status_code, 403)
self.assertTrue(Note.objects.filter(pk=note.pk).exists())
@override_settings(AUDIT_LOG_ENABLED=True)
def test_history_uses_root_ownership(self):
self.root.owner = self.user
self.root.save(update_fields=["owner"])
self.version.owner = UserFactory()
self.version.save(update_fields=["owner"])
response = self.client.get(f"/api/documents/{self.version.pk}/history/")
self.assertEqual(response.status_code, 200)
def test_selection_data_rejects_stale_version_ownership(self):
for owner in (None, self.user):
self.version.owner = owner
self.version.save(update_fields=["owner"])
with self.subTest(owner=owner):
response = self.client.post(
"/api/documents/selection_data/",
{"documents": [self.version.pk]},
format="json",
)
self.assertEqual(response.status_code, 403)
def test_selection_data_allows_private_version_of_permitted_root(self):
self.version.owner = UserFactory()
self.version.save(update_fields=["owner"])
grant_object(self.user, self.root, "view_document")
other = DocumentFactory(owner=self.user)
response = self.client.post(
"/api/documents/selection_data/",
{"documents": [self.version.pk, other.pk]},
format="json",
)
self.assertEqual(response.status_code, 200)
+86
View File
@@ -1196,6 +1196,59 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
self.assertIn(d3.id, result_ids) self.assertIn(d3.id, result_ids)
self.assertNotIn(d4.id, result_ids) self.assertNotIn(d4.id, result_ids)
def test_search_more_like_version_uses_its_root(self) -> None:
"""
GIVEN:
- A document similar in content to a root document, and one that is not
- A version of the root document, which is never indexed
WHEN:
- API request for more like the version
THEN:
- The documents similar to the root are returned, not the version
"""
indexed = {}
for name, title, content, day in (
("root", "bank statement 1", "things i paid for in august", (2019, 3, 4)),
(
"similar",
"bank statement 3",
"things i paid for in september",
(2020, 7, 9),
),
(
"other",
"Quarterly Report",
"quarterly revenue profit margin",
(2021, 11, 30),
),
):
with time_machine.travel(
timezone.make_aware(datetime.datetime(*day)),
tick=False,
):
indexed[name] = DocumentFactory(
title=title,
content=content,
created=datetime.date(*day),
added=timezone.make_aware(datetime.datetime(*day)),
)
version = DocumentFactory(
root_document=indexed["root"],
version_index=1,
content="things i paid for in august",
)
backend = get_backend()
for document in indexed.values():
backend.add_or_update(document)
response = self.client.get(f"/api/documents/?more_like_id={version.id}")
self.assertEqual(response.status_code, status.HTTP_200_OK)
result_ids = [r["id"] for r in response.data["results"]]
self.assertIn(indexed["similar"].id, result_ids)
self.assertNotIn(indexed["other"].id, result_ids)
self.assertNotIn(version.id, result_ids)
def test_more_like_requires_id_of_existing_document(self) -> None: def test_more_like_requires_id_of_existing_document(self) -> None:
""" """
GIVEN: GIVEN:
@@ -1261,6 +1314,39 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(response.content, b"Insufficient permissions.") self.assertEqual(response.content, b"Insufficient permissions.")
def test_search_more_like_version_requires_view_permission_on_its_root(
self,
) -> None:
"""
GIVEN:
- Another user's private root document with an unowned version
WHEN:
- A user without access requests more-like-this for the version
THEN:
- The request is rejected, the version's missing owner grants nothing
"""
owner = UserFactory(username="owner")
attacker = UserFactory(username="attacker")
grant_global(attacker, "view_document")
private_root = DocumentFactory(
title="private bank statement",
content="quarterly treasury bank statement wire transfer",
owner=owner,
)
version = DocumentFactory(
root_document=private_root,
version_index=1,
owner=None,
)
get_backend().add_or_update(private_root)
self.client.force_authenticate(user=attacker)
response = self.client.get(f"/api/documents/?more_like_id={version.id}")
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(response.content, b"Insufficient permissions.")
def test_search_filtering(self) -> None: def test_search_filtering(self) -> None:
t = Tag.objects.create(name="tag") t = Tag.objects.create(name="tag")
t2 = Tag.objects.create(name="tag2") t2 = Tag.objects.create(name="tag2")
+51
View File
@@ -6,6 +6,7 @@ from rest_framework.test import APITestCase
from documents.models import Document from documents.models import Document
from paperless_testing.dirs import DirectoriesMixin from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory
from paperless_testing.factories import UserFactory from paperless_testing.factories import UserFactory
from paperless_testing.permissions import grant_all_global from paperless_testing.permissions import grant_all_global
@@ -279,3 +280,53 @@ class TestTrashAPI(DirectoriesMixin, APITestCase):
Document.objects.filter(root_document=root).values_list("id", flat=True), Document.objects.filter(root_document=root).values_list("id", flat=True),
[version.pk for version in versions], [version.pk for version in versions],
) )
def test_api_trash_version_follows_root_owner(self) -> None:
"""
GIVEN:
- A deleted version of user2's document, owned by nobody
- A deleted version of the user's document, owned by user2
WHEN:
- The user lists the trash and tries to restore or empty the versions
THEN:
- Only the version of the user's own document is listed
- The other version can't be restored or emptied
- The version of the user's own document can be restored
"""
user2 = UserFactory(username="user2")
other_version = DocumentFactory(
root_document=DocumentFactory(owner=user2),
version_index=1,
)
other_version.delete()
own_version = DocumentFactory(
owner=user2,
root_document=DocumentFactory(owner=self.user),
version_index=1,
)
own_version.delete()
resp = self.client.get("/api/trash/")
self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.assertEqual(
[doc["id"] for doc in resp.data["results"]],
[own_version.pk],
)
for action in ("restore", "empty"):
with self.subTest(action=action):
resp = self.client.post(
"/api/trash/",
{"action": action, "documents": [other_version.pk]},
)
self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)
self.assertTrue(
Document.deleted_objects.filter(pk=other_version.pk).exists(),
)
resp = self.client.post(
"/api/trash/",
{"action": "restore", "documents": [own_version.pk]},
)
self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.assertTrue(Document.objects.filter(pk=own_version.pk).exists())
+243 -258
View File
@@ -1,9 +1,10 @@
import shutil import shutil
from collections.abc import Callable
from datetime import date from datetime import date
from pathlib import Path from pathlib import Path
from unittest import mock from unittest import mock
import pikepdf import pytest
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.contrib.auth.models import User from django.contrib.auth.models import User
@@ -12,6 +13,7 @@ from django.test import TestCase
from django.test.utils import CaptureQueriesContext from django.test.utils import CaptureQueriesContext
from guardian.shortcuts import get_groups_with_perms from guardian.shortcuts import get_groups_with_perms
from guardian.shortcuts import get_users_with_perms from guardian.shortcuts import get_users_with_perms
from pytest_mock import MockerFixture
from documents import bulk_edit from documents import bulk_edit
from documents.models import Correspondent from documents.models import Correspondent
@@ -21,8 +23,10 @@ from documents.models import Document
from documents.models import DocumentType from documents.models import DocumentType
from documents.models import StoragePath from documents.models import StoragePath
from documents.models import Tag from documents.models import Tag
from documents.pdf_ops import PageSpec
from documents.permissions import set_permissions_for_objects from documents.permissions import set_permissions_for_objects
from paperless_testing.dirs import DirectoriesMixin from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory
from paperless_testing.permissions import grant_object from paperless_testing.permissions import grant_object
@@ -793,16 +797,14 @@ class TestPDFActions(DirectoriesMixin, TestCase):
self.img_doc.save() self.img_doc.save()
@staticmethod @staticmethod
def mock_password_required_pdf( def fake_decrypt(
mock_open: mock.Mock, src: Path,
fake_pdf: mock.Mock, make_dst: Callable[[], Path],
) -> None: password: str,
password_context = mock.MagicMock() ) -> Path:
password_context.__enter__.return_value = fake_pdf dst = make_dst()
mock_open.side_effect = [ dst.write_bytes(b"password removed")
pikepdf.PasswordError("password required"), return dst
password_context,
]
@mock.patch("documents.tasks.consume_file.s") @mock.patch("documents.tasks.consume_file.s")
def test_merge(self, mock_consume_file) -> None: def test_merge(self, mock_consume_file) -> None:
@@ -847,12 +849,12 @@ class TestPDFActions(DirectoriesMixin, TestCase):
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
@mock.patch("pikepdf.open") @mock.patch("documents.pdf_ops.PdfMerger")
@mock.patch("documents.tasks.consume_file.s") @mock.patch("documents.tasks.consume_file.s")
def test_merge_uses_latest_version_source_for_root_selection( def test_merge_uses_latest_version_source_for_root_selection(
self, self,
mock_consume_file, mock_consume_file,
mock_open_pdf, mock_merger,
) -> None: ) -> None:
version_file = self.dirs.scratch_dir / "sample2_version_merge.pdf" version_file = self.dirs.scratch_dir / "sample2_version_merge.pdf"
shutil.copy(self.doc2.source_path, version_file) shutil.copy(self.doc2.source_path, version_file)
@@ -863,16 +865,14 @@ class TestPDFActions(DirectoriesMixin, TestCase):
filename=version_file, filename=version_file,
mime_type="application/pdf", mime_type="application/pdf",
) )
fake_pdf = mock.MagicMock() merger = mock_merger.return_value.__enter__.return_value
fake_pdf.pdf_version = "1.7" merger.save.side_effect = lambda dst: shutil.copy(version.source_path, dst)
fake_pdf.pages = [mock.Mock()]
mock_open_pdf.return_value.__enter__.return_value = fake_pdf
result = bulk_edit.merge([self.doc2.id]) result = bulk_edit.merge([self.doc2.id])
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
mock_open_pdf.assert_called_once_with(str(version.source_path)) merger.add.assert_called_once_with(version.source_path)
mock_consume_file.assert_not_called() mock_consume_file.assert_called_once()
@mock.patch("documents.bulk_edit.delete.si") @mock.patch("documents.bulk_edit.delete.si")
@mock.patch("documents.tasks.consume_file.s") @mock.patch("documents.tasks.consume_file.s")
@@ -1034,18 +1034,18 @@ class TestPDFActions(DirectoriesMixin, TestCase):
) )
@mock.patch("documents.tasks.consume_file.delay") @mock.patch("documents.tasks.consume_file.delay")
@mock.patch("pikepdf.open") @mock.patch("documents.pdf_ops.PdfMerger.add")
def test_merge_with_errors(self, mock_open_pdf, mock_consume_file) -> None: def test_merge_with_errors(self, mock_add, mock_consume_file) -> None:
""" """
GIVEN: GIVEN:
- Existing documents - Existing documents
WHEN: WHEN:
- Merge action is called with 2 documents - Merge action is called with 2 documents
- Error occurs when opening both files - Error occurs when adding both files
THEN: THEN:
- Consume file should not be called - Consume file should not be called
""" """
mock_open_pdf.side_effect = Exception("Error opening PDF") mock_add.side_effect = Exception("Error opening PDF")
doc_ids = [self.doc2.id, self.doc3.id] doc_ids = [self.doc2.id, self.doc3.id]
with self.assertLogs("paperless.bulk_edit", level="ERROR") as cm: with self.assertLogs("paperless.bulk_edit", level="ERROR") as cm:
@@ -1082,12 +1082,12 @@ class TestPDFActions(DirectoriesMixin, TestCase):
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
@mock.patch("documents.bulk_edit.group") @mock.patch("documents.bulk_edit.group")
@mock.patch("pikepdf.open") @mock.patch("documents.pdf_ops.build_pdfs")
@mock.patch("documents.tasks.consume_file.s") @mock.patch("documents.tasks.consume_file.s")
def test_split_uses_latest_version_source_for_root_selection( def test_split_uses_latest_version_source_for_root_selection(
self, self,
mock_consume_file, mock_consume_file,
mock_open_pdf, mock_build_pdfs,
mock_group, mock_group,
) -> None: ) -> None:
version_file = self.dirs.scratch_dir / "sample2_version_split.pdf" version_file = self.dirs.scratch_dir / "sample2_version_split.pdf"
@@ -1099,17 +1099,15 @@ class TestPDFActions(DirectoriesMixin, TestCase):
filename=version_file, filename=version_file,
mime_type="application/pdf", mime_type="application/pdf",
) )
fake_pdf = mock.MagicMock() mock_build_pdfs.return_value = [version.source_path, version.source_path]
fake_pdf.pages = [mock.Mock(), mock.Mock()]
mock_open_pdf.return_value.__enter__.return_value = fake_pdf
mock_group.return_value.delay.return_value = None mock_group.return_value.delay.return_value = None
result = bulk_edit.split([self.doc2.id], [[1], [2]]) result = bulk_edit.split([self.doc2.id], [[1], [2]])
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
mock_open_pdf.assert_called_once_with(version.source_path) self.assertEqual(mock_build_pdfs.call_args.args[0], version.source_path)
mock_consume_file.assert_not_called() self.assertEqual(mock_consume_file.call_count, 2)
mock_group.return_value.delay.assert_not_called() mock_group.return_value.delay.assert_called_once()
@mock.patch("documents.bulk_edit.delete.si") @mock.patch("documents.bulk_edit.delete.si")
@mock.patch("documents.tasks.consume_file.s") @mock.patch("documents.tasks.consume_file.s")
@@ -1197,18 +1195,18 @@ class TestPDFActions(DirectoriesMixin, TestCase):
self.assertEqual(self.doc2.archive_serial_number, 222) self.assertEqual(self.doc2.archive_serial_number, 222)
@mock.patch("documents.tasks.consume_file.apply_async") @mock.patch("documents.tasks.consume_file.apply_async")
@mock.patch("pikepdf.Pdf.save") @mock.patch("documents.pdf_ops.build_pdfs")
def test_split_with_errors(self, mock_save_pdf, mock_consume_file) -> None: def test_split_with_errors(self, mock_build_pdfs, mock_consume_file) -> None:
""" """
GIVEN: GIVEN:
- Existing documents - Existing documents
WHEN: WHEN:
- Split action is called with 1 document and 2 page groups - Split action is called with 1 document and 2 page groups
- Error occurs when saving the files - Error occurs when building the files
THEN: THEN:
- Consume file should not be called - Consume file should not be called
""" """
mock_save_pdf.side_effect = Exception("Error saving PDF") mock_build_pdfs.side_effect = Exception("Error building PDFs")
doc_ids = [self.doc2.id] doc_ids = [self.doc2.id]
pages = [[1, 2], [3]] pages = [[1, 2], [3]]
@@ -1243,10 +1241,10 @@ class TestPDFActions(DirectoriesMixin, TestCase):
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
@mock.patch("documents.tasks.consume_file.apply_async") @mock.patch("documents.tasks.consume_file.apply_async")
@mock.patch("pikepdf.Pdf.save") @mock.patch("documents.pdf_ops.rotate_pdf")
def test_rotate_with_error( def test_rotate_with_error(
self, self,
mock_pdf_save, mock_rotate_pdf,
mock_consume_delay, mock_consume_delay,
) -> None: ) -> None:
""" """
@@ -1254,11 +1252,11 @@ class TestPDFActions(DirectoriesMixin, TestCase):
- Existing documents - Existing documents
WHEN: WHEN:
- Rotate action is called with 2 documents - Rotate action is called with 2 documents
- PikePDF raises an error - Rotating the PDF raises an error
THEN: THEN:
- Rotate action should be called 0 times - Rotate action should be called 0 times
""" """
mock_pdf_save.side_effect = Exception("Error saving PDF") mock_rotate_pdf.side_effect = Exception("Error rotating PDF")
doc_ids = [self.doc2.id, self.doc3.id] doc_ids = [self.doc2.id, self.doc3.id]
with self.assertLogs("paperless.bulk_edit", level="ERROR") as cm: with self.assertLogs("paperless.bulk_edit", level="ERROR") as cm:
@@ -1293,10 +1291,10 @@ class TestPDFActions(DirectoriesMixin, TestCase):
@mock.patch("documents.data_models.magic.from_file", return_value="application/pdf") @mock.patch("documents.data_models.magic.from_file", return_value="application/pdf")
@mock.patch("documents.tasks.consume_file.apply_async") @mock.patch("documents.tasks.consume_file.apply_async")
@mock.patch("pikepdf.open") @mock.patch("documents.pdf_ops.rotate_pdf")
def test_rotate_explicit_selection_uses_root_source_when_root_selected( def test_rotate_explicit_selection_uses_root_source_when_root_selected(
self, self,
mock_open, mock_rotate_pdf,
mock_consume_delay, mock_consume_delay,
mock_magic, mock_magic,
) -> None: ) -> None:
@@ -1305,9 +1303,6 @@ class TestPDFActions(DirectoriesMixin, TestCase):
title="B version 1", title="B version 1",
root_document=self.doc2, root_document=self.doc2,
) )
fake_pdf = mock.MagicMock()
fake_pdf.pages = [mock.Mock()]
mock_open.return_value.__enter__.return_value = fake_pdf
result = bulk_edit.rotate( result = bulk_edit.rotate(
[self.doc2.id], [self.doc2.id],
@@ -1316,26 +1311,35 @@ class TestPDFActions(DirectoriesMixin, TestCase):
) )
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
mock_open.assert_called_once_with(self.doc2.source_path) self.assertEqual(mock_rotate_pdf.call_args.args[0], self.doc2.source_path)
mock_consume_delay.assert_called_once() mock_consume_delay.assert_called_once()
@mock.patch("documents.tasks.consume_file.apply_async") @mock.patch("documents.tasks.consume_file.apply_async")
@mock.patch("pikepdf.Pdf.save") @mock.patch("documents.pdf_ops.remove_pages")
@mock.patch("documents.data_models.magic.from_file", return_value="application/pdf") @mock.patch("documents.data_models.magic.from_file", return_value="application/pdf")
def test_delete_pages(self, mock_magic, mock_pdf_save, mock_consume_delay) -> None: def test_delete_pages(
self,
mock_magic,
mock_remove_pages,
mock_consume_delay,
) -> None:
""" """
GIVEN: GIVEN:
- Existing documents - Existing documents
WHEN: WHEN:
- Delete pages action is called with 1 document and 2 pages - Delete pages action is called with 1 document and 2 pages
THEN: THEN:
- Save should be called once - The pages are removed from the document's source PDF
- A new version should be enqueued via consume_file - A new version should be enqueued via consume_file
""" """
doc_ids = [self.doc2.id] doc_ids = [self.doc2.id]
pages = [1, 3] pages = [1, 3]
result = bulk_edit.delete_pages(doc_ids, pages) result = bulk_edit.delete_pages(doc_ids, pages)
mock_pdf_save.assert_called_once() mock_remove_pages.assert_called_once_with(
self.doc2.source_path,
mock.ANY,
[1, 3],
)
mock_consume_delay.assert_called_once() mock_consume_delay.assert_called_once()
task_kwargs = mock_consume_delay.call_args.kwargs["kwargs"] task_kwargs = mock_consume_delay.call_args.kwargs["kwargs"]
self.assertEqual(task_kwargs["input_doc"].root_document_id, self.doc2.id) self.assertEqual(task_kwargs["input_doc"].root_document_id, self.doc2.id)
@@ -1347,10 +1351,10 @@ class TestPDFActions(DirectoriesMixin, TestCase):
@mock.patch("documents.data_models.magic.from_file", return_value="application/pdf") @mock.patch("documents.data_models.magic.from_file", return_value="application/pdf")
@mock.patch("documents.tasks.consume_file.apply_async") @mock.patch("documents.tasks.consume_file.apply_async")
@mock.patch("pikepdf.open") @mock.patch("documents.pdf_ops.remove_pages")
def test_delete_pages_explicit_selection_uses_root_source_when_root_selected( def test_delete_pages_explicit_selection_uses_root_source_when_root_selected(
self, self,
mock_open, mock_remove_pages,
mock_consume_delay, mock_consume_delay,
mock_magic, mock_magic,
) -> None: ) -> None:
@@ -1359,9 +1363,6 @@ class TestPDFActions(DirectoriesMixin, TestCase):
title="B version 1", title="B version 1",
root_document=self.doc2, root_document=self.doc2,
) )
fake_pdf = mock.MagicMock()
fake_pdf.pages = [mock.Mock(), mock.Mock()]
mock_open.return_value.__enter__.return_value = fake_pdf
result = bulk_edit.delete_pages( result = bulk_edit.delete_pages(
[self.doc2.id], [self.doc2.id],
@@ -1370,23 +1371,26 @@ class TestPDFActions(DirectoriesMixin, TestCase):
) )
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
mock_open.assert_called_once_with(self.doc2.source_path) self.assertEqual(mock_remove_pages.call_args.args[0], self.doc2.source_path)
mock_consume_delay.assert_called_once() mock_consume_delay.assert_called_once()
@mock.patch("documents.tasks.consume_file.apply_async") @mock.patch("documents.tasks.consume_file.apply_async")
@mock.patch("pikepdf.Pdf.save") @mock.patch("documents.pdf_ops.remove_pages")
def test_delete_pages_with_error(self, mock_pdf_save, mock_consume_delay) -> None: def test_delete_pages_with_error(
self,
mock_remove_pages,
mock_consume_delay,
) -> None:
""" """
GIVEN: GIVEN:
- Existing documents - Existing documents
WHEN: WHEN:
- Delete pages action is called with 1 document and 2 pages - Delete pages action is called with 1 document and 2 pages
- PikePDF raises an error - Removing the pages raises an error
THEN: THEN:
- Save should be called once
- No new version should be enqueued - No new version should be enqueued
""" """
mock_pdf_save.side_effect = Exception("Error saving PDF") mock_remove_pages.side_effect = Exception("Error removing pages")
doc_ids = [self.doc2.id] doc_ids = [self.doc2.id]
pages = [1, 3] pages = [1, 3]
@@ -1416,6 +1420,41 @@ class TestPDFActions(DirectoriesMixin, TestCase):
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
mock_group.return_value.delay.assert_called_once() mock_group.return_value.delay.assert_called_once()
@mock.patch("documents.bulk_edit.group")
@mock.patch("documents.pdf_ops.build_pdfs")
@mock.patch("documents.tasks.consume_file.s")
def test_edit_pdf_maps_operations_to_outputs(
self,
mock_consume_file: mock.Mock,
mock_build_pdfs: mock.Mock,
mock_group: mock.Mock,
) -> None:
"""
GIVEN:
- Existing document
WHEN:
- edit_pdf is called with operations interleaved across two outputs,
some of them rotated
THEN:
- Each output is built from its own operations, in operation order,
with the requested rotation
"""
mock_build_pdfs.return_value = [self.doc2.source_path, self.doc2.source_path]
mock_group.return_value.delay.return_value = None
operations = [
{"page": 3, "doc": 1},
{"page": 1, "doc": 0, "rotate": 90},
{"page": 2, "doc": 1, "rotate": 180},
]
bulk_edit.edit_pdf([self.doc2.id], operations)
outputs = mock_build_pdfs.call_args.args[1]
self.assertEqual(
[specs for specs, _ in outputs],
[[PageSpec(1, 90)], [PageSpec(3), PageSpec(2, 180)]],
)
@mock.patch("documents.bulk_edit.group") @mock.patch("documents.bulk_edit.group")
@mock.patch("documents.tasks.consume_file.s") @mock.patch("documents.tasks.consume_file.s")
def test_edit_pdf_with_user_override(self, mock_consume_file, mock_group) -> None: def test_edit_pdf_with_user_override(self, mock_consume_file, mock_group) -> None:
@@ -1531,12 +1570,10 @@ class TestPDFActions(DirectoriesMixin, TestCase):
@mock.patch("documents.data_models.magic.from_file", return_value="application/pdf") @mock.patch("documents.data_models.magic.from_file", return_value="application/pdf")
@mock.patch("documents.tasks.consume_file.apply_async") @mock.patch("documents.tasks.consume_file.apply_async")
@mock.patch("pikepdf.new") @mock.patch("documents.pdf_ops.build_pdfs")
@mock.patch("pikepdf.open")
def test_edit_pdf_explicit_selection_uses_root_source_when_root_selected( def test_edit_pdf_explicit_selection_uses_root_source_when_root_selected(
self, self,
mock_open, mock_build_pdfs,
mock_new,
mock_consume_delay, mock_consume_delay,
mock_magic, mock_magic,
) -> None: ) -> None:
@@ -1545,12 +1582,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
title="B version 1", title="B version 1",
root_document=self.doc2, root_document=self.doc2,
) )
fake_pdf = mock.MagicMock() mock_build_pdfs.return_value = [Path("edited.pdf")]
fake_pdf.pages = [mock.Mock()]
mock_open.return_value.__enter__.return_value = fake_pdf
output_pdf = mock.MagicMock()
output_pdf.pages = []
mock_new.return_value = output_pdf
result = bulk_edit.edit_pdf( result = bulk_edit.edit_pdf(
[self.doc2.id], [self.doc2.id],
@@ -1560,7 +1592,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
) )
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
mock_open.assert_called_once_with(self.doc2.source_path) self.assertEqual(mock_build_pdfs.call_args.args[0], self.doc2.source_path)
mock_consume_delay.assert_called_once() mock_consume_delay.assert_called_once()
@mock.patch("documents.bulk_edit.group") @mock.patch("documents.bulk_edit.group")
@@ -1586,31 +1618,6 @@ class TestPDFActions(DirectoriesMixin, TestCase):
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
mock_group.return_value.delay.assert_called_once() mock_group.return_value.delay.assert_called_once()
@mock.patch("documents.bulk_edit.group")
@mock.patch("documents.tasks.consume_file.s")
def test_edit_pdf_open_failure(
self,
mock_consume_file: mock.Mock,
mock_group: mock.Mock,
) -> None:
"""
GIVEN:
- Existing document
WHEN:
- edit_pdf fails to open PDF
THEN:
- Task group is not called
"""
doc_ids = [self.doc2.id]
operations = [
{"page": 9999}, # invalid page, forces error during PDF load
]
with self.assertLogs("paperless.bulk_edit", level="ERROR"):
with self.assertRaises(Exception):
bulk_edit.edit_pdf(doc_ids, operations)
mock_group.assert_not_called()
mock_consume_file.assert_not_called()
@mock.patch("documents.bulk_edit.group") @mock.patch("documents.bulk_edit.group")
@mock.patch("documents.tasks.consume_file.s") @mock.patch("documents.tasks.consume_file.s")
def test_edit_pdf_multiple_outputs_with_update_flag_errors( def test_edit_pdf_multiple_outputs_with_update_flag_errors(
@@ -1637,23 +1644,15 @@ class TestPDFActions(DirectoriesMixin, TestCase):
mock_group.assert_not_called() mock_group.assert_not_called()
mock_consume_file.assert_not_called() mock_consume_file.assert_not_called()
@mock.patch("pikepdf.open")
def test_edit_pdf_rejects_invalid_operations(self, mock_open) -> None:
for operations in ([], [{"page": 1, "doc": 2**32}]):
with self.subTest(operations=operations):
with self.assertLogs("paperless.bulk_edit", level="ERROR"):
with self.assertRaisesRegex(ValueError, "index is out of bounds"):
bulk_edit.edit_pdf([self.doc2.id], operations)
mock_open.assert_not_called()
@mock.patch("documents.bulk_edit.update_document_content_maybe_archive_file.delay") @mock.patch("documents.bulk_edit.update_document_content_maybe_archive_file.delay")
@mock.patch("documents.tasks.consume_file.apply_async") @mock.patch("documents.tasks.consume_file.apply_async")
@mock.patch("documents.bulk_edit.tempfile.mkdtemp") @mock.patch("documents.bulk_edit.tempfile.mkdtemp")
@mock.patch("pikepdf.open") @mock.patch("documents.pdf_ops.decrypt_pdf")
@mock.patch("documents.pdf_ops.needs_decrypt", return_value=True)
def test_remove_password_update_document( def test_remove_password_update_document(
self, self,
mock_open, mock_needs_decrypt,
mock_decrypt,
mock_mkdtemp, mock_mkdtemp,
mock_consume_delay, mock_consume_delay,
mock_update_document, mock_update_document,
@@ -1662,16 +1661,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
temp_dir = self.dirs.scratch_dir / "remove-password-update" temp_dir = self.dirs.scratch_dir / "remove-password-update"
temp_dir.mkdir(parents=True, exist_ok=True) temp_dir.mkdir(parents=True, exist_ok=True)
mock_mkdtemp.return_value = str(temp_dir) mock_mkdtemp.return_value = str(temp_dir)
mock_decrypt.side_effect = self.fake_decrypt
fake_pdf = mock.MagicMock()
fake_pdf.pages = [mock.Mock(), mock.Mock(), mock.Mock()]
fake_pdf.is_encrypted = True
def save_side_effect(target_path):
Path(target_path).write_bytes(b"new pdf content")
fake_pdf.save.side_effect = save_side_effect
mock_open.return_value.__enter__.return_value = fake_pdf
result = bulk_edit.remove_password( result = bulk_edit.remove_password(
[doc.id], [doc.id],
@@ -1680,14 +1670,8 @@ class TestPDFActions(DirectoriesMixin, TestCase):
) )
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
self.assertEqual( mock_needs_decrypt.assert_called_once_with(doc.source_path)
mock_open.call_args_list, mock_decrypt.assert_called_once_with(doc.source_path, mock.ANY, "secret")
[
mock.call(doc.source_path),
mock.call(doc.source_path, password="secret"),
],
)
fake_pdf.remove_unreferenced_resources.assert_called_once()
mock_update_document.assert_not_called() mock_update_document.assert_not_called()
mock_consume_delay.assert_called_once() mock_consume_delay.assert_called_once()
task_kwargs = mock_consume_delay.call_args.kwargs["kwargs"] task_kwargs = mock_consume_delay.call_args.kwargs["kwargs"]
@@ -1700,40 +1684,15 @@ class TestPDFActions(DirectoriesMixin, TestCase):
self.assertEqual(task_kwargs["input_doc"].root_document_id, doc.id) self.assertEqual(task_kwargs["input_doc"].root_document_id, doc.id)
self.assertIsNotNone(task_kwargs["overrides"]) self.assertIsNotNone(task_kwargs["overrides"])
@mock.patch("documents.tasks.consume_file.apply_async")
@mock.patch("documents.bulk_edit.tempfile.mkdtemp")
@mock.patch("pikepdf.open")
def test_remove_password_update_document_skips_unencrypted_pdf(
self,
mock_open,
mock_mkdtemp,
mock_consume_delay,
) -> None:
doc = self.doc1
fake_pdf = mock.MagicMock()
fake_pdf.is_encrypted = False
mock_open.return_value.__enter__.return_value = fake_pdf
result = bulk_edit.remove_password(
[doc.id],
password="secret",
update_document=True,
)
self.assertEqual(result, "OK")
mock_open.assert_called_once_with(doc.source_path)
fake_pdf.remove_unreferenced_resources.assert_not_called()
fake_pdf.save.assert_not_called()
mock_mkdtemp.assert_not_called()
mock_consume_delay.assert_not_called()
@mock.patch("documents.bulk_edit.update_document_content_maybe_archive_file.delay") @mock.patch("documents.bulk_edit.update_document_content_maybe_archive_file.delay")
@mock.patch("documents.tasks.consume_file.apply_async") @mock.patch("documents.tasks.consume_file.apply_async")
@mock.patch("documents.bulk_edit.tempfile.mkdtemp") @mock.patch("documents.bulk_edit.tempfile.mkdtemp")
@mock.patch("pikepdf.open") @mock.patch("documents.pdf_ops.decrypt_pdf")
@mock.patch("documents.pdf_ops.needs_decrypt", return_value=True)
def test_remove_password_update_document_uses_source_paths( def test_remove_password_update_document_uses_source_paths(
self, self,
mock_open, mock_needs_decrypt,
mock_decrypt,
mock_mkdtemp, mock_mkdtemp,
mock_consume_delay, mock_consume_delay,
mock_update_document, mock_update_document,
@@ -1744,14 +1703,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
temp_dir = self.dirs.scratch_dir / "remove-password-source-file" temp_dir = self.dirs.scratch_dir / "remove-password-source-file"
temp_dir.mkdir(parents=True, exist_ok=True) temp_dir.mkdir(parents=True, exist_ok=True)
mock_mkdtemp.return_value = str(temp_dir) mock_mkdtemp.return_value = str(temp_dir)
mock_decrypt.side_effect = self.fake_decrypt
fake_pdf = mock.MagicMock()
self.mock_password_required_pdf(mock_open, fake_pdf)
def save_side_effect(target_path):
Path(target_path).write_bytes(b"new pdf content")
fake_pdf.save.side_effect = save_side_effect
result = bulk_edit.remove_password( result = bulk_edit.remove_password(
[doc.id], [doc.id],
@@ -1761,22 +1713,19 @@ class TestPDFActions(DirectoriesMixin, TestCase):
) )
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
self.assertEqual( mock_needs_decrypt.assert_called_once_with(source_file)
mock_open.call_args_list, mock_decrypt.assert_called_once_with(source_file, mock.ANY, "secret")
[
mock.call(source_file),
mock.call(source_file, password="secret"),
],
)
mock_update_document.assert_not_called() mock_update_document.assert_not_called()
mock_consume_delay.assert_called_once() mock_consume_delay.assert_called_once()
@mock.patch("documents.data_models.magic.from_file", return_value="application/pdf") @mock.patch("documents.data_models.magic.from_file", return_value="application/pdf")
@mock.patch("documents.tasks.consume_file.apply_async") @mock.patch("documents.tasks.consume_file.apply_async")
@mock.patch("pikepdf.open") @mock.patch("documents.pdf_ops.decrypt_pdf")
@mock.patch("documents.pdf_ops.needs_decrypt", return_value=True)
def test_remove_password_explicit_selection_uses_root_source_when_root_selected( def test_remove_password_explicit_selection_uses_root_source_when_root_selected(
self, self,
mock_open, mock_needs_decrypt,
mock_decrypt,
mock_consume_delay, mock_consume_delay,
mock_magic, mock_magic,
) -> None: ) -> None:
@@ -1785,8 +1734,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
title="A version 1", title="A version 1",
root_document=self.doc1, root_document=self.doc1,
) )
fake_pdf = mock.MagicMock() mock_decrypt.return_value = Path("unprotected.pdf")
self.mock_password_required_pdf(mock_open, fake_pdf)
result = bulk_edit.remove_password( result = bulk_edit.remove_password(
[self.doc1.id], [self.doc1.id],
@@ -1796,12 +1744,11 @@ class TestPDFActions(DirectoriesMixin, TestCase):
) )
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
self.assertEqual( mock_needs_decrypt.assert_called_once_with(self.doc1.source_path)
mock_open.call_args_list, mock_decrypt.assert_called_once_with(
[ self.doc1.source_path,
mock.call(self.doc1.source_path), mock.ANY,
mock.call(self.doc1.source_path, password="secret"), "secret",
],
) )
mock_consume_delay.assert_called_once() mock_consume_delay.assert_called_once()
@@ -1809,10 +1756,12 @@ class TestPDFActions(DirectoriesMixin, TestCase):
@mock.patch("documents.bulk_edit.group") @mock.patch("documents.bulk_edit.group")
@mock.patch("documents.tasks.consume_file.s") @mock.patch("documents.tasks.consume_file.s")
@mock.patch("documents.bulk_edit.tempfile.mkdtemp") @mock.patch("documents.bulk_edit.tempfile.mkdtemp")
@mock.patch("pikepdf.open") @mock.patch("documents.pdf_ops.decrypt_pdf")
@mock.patch("documents.pdf_ops.needs_decrypt", return_value=True)
def test_remove_password_creates_consumable_document( def test_remove_password_creates_consumable_document(
self, self,
mock_open: mock.Mock, mock_needs_decrypt: mock.Mock,
mock_decrypt: mock.Mock,
mock_mkdtemp: mock.Mock, mock_mkdtemp: mock.Mock,
mock_consume_file: mock.Mock, mock_consume_file: mock.Mock,
mock_group: mock.Mock, mock_group: mock.Mock,
@@ -1822,15 +1771,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
temp_dir = self.dirs.scratch_dir / "remove-password" temp_dir = self.dirs.scratch_dir / "remove-password"
temp_dir.mkdir(parents=True, exist_ok=True) temp_dir.mkdir(parents=True, exist_ok=True)
mock_mkdtemp.return_value = str(temp_dir) mock_mkdtemp.return_value = str(temp_dir)
mock_decrypt.side_effect = self.fake_decrypt
fake_pdf = mock.MagicMock()
fake_pdf.pages = [mock.Mock(), mock.Mock()]
self.mock_password_required_pdf(mock_open, fake_pdf)
def save_side_effect(target_path: Path) -> None:
target_path.write_bytes(b"password removed")
fake_pdf.save.side_effect = save_side_effect
mock_group.return_value.delay.return_value = None mock_group.return_value.delay.return_value = None
user = User.objects.create(username="owner") user = User.objects.create(username="owner")
@@ -1845,13 +1786,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
) )
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
self.assertEqual( mock_decrypt.assert_called_once_with(doc.source_path, mock.ANY, "secret")
mock_open.call_args_list,
[
mock.call(doc.source_path),
mock.call(doc.source_path, password="secret"),
],
)
mock_consume_file.assert_called_once() mock_consume_file.assert_called_once()
call_kwargs = mock_consume_file.call_args.kwargs call_kwargs = mock_consume_file.call_args.kwargs
consumable_document = call_kwargs["input_doc"] consumable_document = call_kwargs["input_doc"]
@@ -1873,21 +1808,18 @@ class TestPDFActions(DirectoriesMixin, TestCase):
@mock.patch("documents.bulk_edit.chord") @mock.patch("documents.bulk_edit.chord")
@mock.patch("documents.bulk_edit.group") @mock.patch("documents.bulk_edit.group")
@mock.patch("documents.tasks.consume_file.s") @mock.patch("documents.tasks.consume_file.s")
@mock.patch("documents.bulk_edit.tempfile.mkdtemp") @mock.patch("documents.pdf_ops.decrypt_pdf")
@mock.patch("pikepdf.open") @mock.patch("documents.pdf_ops.needs_decrypt", return_value=False)
def test_remove_password_skips_unencrypted_pdf_without_queueing( def test_remove_password_skips_unencrypted_pdf_without_queueing(
self, self,
mock_open: mock.Mock, mock_needs_decrypt: mock.Mock,
mock_mkdtemp: mock.Mock, mock_decrypt: mock.Mock,
mock_consume_file: mock.Mock, mock_consume_file: mock.Mock,
mock_group: mock.Mock, mock_group: mock.Mock,
mock_chord: mock.Mock, mock_chord: mock.Mock,
mock_delete: mock.Mock, mock_delete: mock.Mock,
) -> None: ) -> None:
doc = self.doc2 doc = self.doc2
fake_pdf = mock.MagicMock()
fake_pdf.is_encrypted = False
mock_open.return_value.__enter__.return_value = fake_pdf
result = bulk_edit.remove_password( result = bulk_edit.remove_password(
[doc.id], [doc.id],
@@ -1897,10 +1829,8 @@ class TestPDFActions(DirectoriesMixin, TestCase):
) )
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
mock_open.assert_called_once_with(doc.source_path) mock_needs_decrypt.assert_called_once_with(doc.source_path)
fake_pdf.remove_unreferenced_resources.assert_not_called() mock_decrypt.assert_not_called()
fake_pdf.save.assert_not_called()
mock_mkdtemp.assert_not_called()
mock_consume_file.assert_not_called() mock_consume_file.assert_not_called()
mock_group.assert_not_called() mock_group.assert_not_called()
mock_chord.assert_not_called() mock_chord.assert_not_called()
@@ -1911,10 +1841,12 @@ class TestPDFActions(DirectoriesMixin, TestCase):
@mock.patch("documents.bulk_edit.group") @mock.patch("documents.bulk_edit.group")
@mock.patch("documents.tasks.consume_file.s") @mock.patch("documents.tasks.consume_file.s")
@mock.patch("documents.bulk_edit.tempfile.mkdtemp") @mock.patch("documents.bulk_edit.tempfile.mkdtemp")
@mock.patch("pikepdf.open") @mock.patch("documents.pdf_ops.decrypt_pdf")
@mock.patch("documents.pdf_ops.needs_decrypt", return_value=True)
def test_remove_password_deletes_original( def test_remove_password_deletes_original(
self, self,
mock_open: mock.Mock, mock_needs_decrypt: mock.Mock,
mock_decrypt: mock.Mock,
mock_mkdtemp: mock.Mock, mock_mkdtemp: mock.Mock,
mock_consume_file: mock.Mock, mock_consume_file: mock.Mock,
mock_group: mock.Mock, mock_group: mock.Mock,
@@ -1925,15 +1857,7 @@ class TestPDFActions(DirectoriesMixin, TestCase):
temp_dir = self.dirs.scratch_dir / "remove-password-delete" temp_dir = self.dirs.scratch_dir / "remove-password-delete"
temp_dir.mkdir(parents=True, exist_ok=True) temp_dir.mkdir(parents=True, exist_ok=True)
mock_mkdtemp.return_value = str(temp_dir) mock_mkdtemp.return_value = str(temp_dir)
mock_decrypt.side_effect = self.fake_decrypt
fake_pdf = mock.MagicMock()
fake_pdf.pages = [mock.Mock(), mock.Mock()]
self.mock_password_required_pdf(mock_open, fake_pdf)
def save_side_effect(target_path: Path) -> None:
target_path.write_bytes(b"password removed")
fake_pdf.save.side_effect = save_side_effect
mock_chord.return_value.delay.return_value = None mock_chord.return_value.delay.return_value = None
result = bulk_edit.remove_password( result = bulk_edit.remove_password(
@@ -1945,23 +1869,23 @@ class TestPDFActions(DirectoriesMixin, TestCase):
) )
self.assertEqual(result, "OK") self.assertEqual(result, "OK")
self.assertEqual( mock_decrypt.assert_called_once_with(doc.source_path, mock.ANY, "secret")
mock_open.call_args_list,
[
mock.call(doc.source_path),
mock.call(doc.source_path, password="secret"),
],
)
mock_consume_file.assert_called_once() mock_consume_file.assert_called_once()
mock_group.assert_not_called() mock_group.assert_not_called()
mock_chord.assert_called_once() mock_chord.assert_called_once()
mock_chord.return_value.delay.assert_called_once() mock_chord.return_value.delay.assert_called_once()
mock_delete.si.assert_called_once_with([doc.id]) mock_delete.si.assert_called_once_with([doc.id])
@mock.patch("pikepdf.open") @mock.patch(
def test_remove_password_open_failure(self, mock_open: mock.Mock) -> None: "documents.pdf_ops.decrypt_pdf",
mock_open.side_effect = RuntimeError("wrong password") side_effect=RuntimeError("wrong password"),
)
@mock.patch("documents.pdf_ops.needs_decrypt", return_value=True)
def test_remove_password_failure_raises_value_error(
self,
mock_needs_decrypt: mock.Mock,
mock_decrypt: mock.Mock,
) -> None:
with self.assertLogs("paperless.bulk_edit", level="ERROR") as cm: with self.assertLogs("paperless.bulk_edit", level="ERROR") as cm:
with self.assertRaises(ValueError) as exc: with self.assertRaises(ValueError) as exc:
bulk_edit.remove_password([self.doc1.id], password="secret") bulk_edit.remove_password([self.doc1.id], password="secret")
@@ -1970,18 +1894,22 @@ class TestPDFActions(DirectoriesMixin, TestCase):
self.assertIn("Error removing password from document", cm.output[0]) self.assertIn("Error removing password from document", cm.output[0])
class TestBulkEditReprocess(DirectoriesMixin, TestCase): @pytest.mark.django_db
def setUp(self) -> None: class TestBulkEditReprocess:
super().setUp() @pytest.fixture
def mock_task(self, mocker: MockerFixture) -> mock.MagicMock:
self.doc = Document.objects.create( return mocker.patch(
title="test", "documents.bulk_edit.update_document_content_maybe_archive_file",
checksum="A",
mime_type="application/pdf",
) )
@mock.patch("documents.bulk_edit.update_document_content_maybe_archive_file") @staticmethod
def test_reprocess_defaults_to_local(self, mock_task: mock.Mock) -> None: def _queued_ids(mock_task: mock.MagicMock) -> list[int]:
return [
call.kwargs["kwargs"]["document_id"]
for call in mock_task.apply_async.call_args_list
]
def test_reprocess_defaults_to_local(self, mock_task: mock.MagicMock) -> None:
""" """
GIVEN: GIVEN:
- A reprocess request that says nothing about remote OCR - A reprocess request that says nothing about remote OCR
@@ -1990,18 +1918,17 @@ class TestBulkEditReprocess(DirectoriesMixin, TestCase):
THEN: THEN:
- The task is queued without asking for the remote engine - The task is queued without asking for the remote engine
""" """
result = bulk_edit.reprocess([self.doc.id]) doc = DocumentFactory()
assert bulk_edit.reprocess([doc.id]) == "OK"
self.assertEqual(result, "OK")
mock_task.apply_async.assert_called_once() mock_task.apply_async.assert_called_once()
_, kwargs = mock_task.apply_async.call_args assert mock_task.apply_async.call_args.kwargs["kwargs"] == {
self.assertEqual( "document_id": doc.id,
kwargs["kwargs"], "remote_ocr": False,
{"document_id": self.doc.id, "remote_ocr": False}, }
)
@mock.patch("documents.bulk_edit.update_document_content_maybe_archive_file") def test_reprocess_passes_remote_ocr(self, mock_task: mock.MagicMock) -> None:
def test_reprocess_passes_remote_ocr(self, mock_task: mock.Mock) -> None:
""" """
GIVEN: GIVEN:
- A reprocess request that explicitly asks for remote OCR - A reprocess request that explicitly asks for remote OCR
@@ -2010,14 +1937,72 @@ class TestBulkEditReprocess(DirectoriesMixin, TestCase):
THEN: THEN:
- The request is forwarded to the task for every document - The request is forwarded to the task for every document
""" """
other = Document.objects.create( docs = DocumentFactory.create_batch(2)
title="test2",
checksum="B", bulk_edit.reprocess([doc.id for doc in docs], remote_ocr=True)
mime_type="application/pdf",
assert mock_task.apply_async.call_count == 2
for call in mock_task.apply_async.call_args_list:
assert call.kwargs["kwargs"]["remote_ocr"]
def test_reprocess_root_uses_latest_version(
self,
mock_task: mock.MagicMock,
) -> None:
"""
GIVEN:
- A root document with two versions
WHEN:
- reprocess is called with the root document
THEN:
- The latest version is reprocessed, not the root's original file
"""
root = DocumentFactory()
DocumentFactory(root_document=root, version_index=1)
latest = DocumentFactory(root_document=root, version_index=2)
bulk_edit.reprocess([root.id])
assert self._queued_ids(mock_task) == [latest.id]
def test_reprocess_explicit_version(self, mock_task: mock.MagicMock) -> None:
"""
GIVEN:
- A root document with two versions
WHEN:
- reprocess is called with the older version
THEN:
- That version is reprocessed
"""
root = DocumentFactory()
older = DocumentFactory(root_document=root, version_index=1)
DocumentFactory(root_document=root, version_index=2)
bulk_edit.reprocess([older.id])
assert self._queued_ids(mock_task) == [older.id]
def test_reprocess_root_and_latest_version_dispatches_once(
self,
mock_task: mock.MagicMock,
) -> None:
"""
GIVEN:
- A root document with two versions, the latest created on a
different date than the root
WHEN:
- reprocess is called with both the root and its latest version
THEN:
- The latest version is reprocessed only once
"""
root = DocumentFactory(created=date(2024, 1, 1))
DocumentFactory(root_document=root, version_index=1)
latest = DocumentFactory(
root_document=root,
version_index=2,
created=date(2025, 1, 1),
) )
bulk_edit.reprocess([self.doc.id, other.id], remote_ocr=True) bulk_edit.reprocess([root.id, latest.id])
self.assertEqual(mock_task.apply_async.call_count, 2) assert self._queued_ids(mock_task) == [latest.id]
for call in mock_task.apply_async.call_args_list:
self.assertTrue(call.kwargs["kwargs"]["remote_ocr"])
+22
View File
@@ -22,6 +22,7 @@ from documents.models import Document
from documents.tasks import update_document_content_maybe_archive_file from documents.tasks import update_document_content_maybe_archive_file
from paperless_testing.assertions import FileSystemAssertsMixin from paperless_testing.assertions import FileSystemAssertsMixin
from paperless_testing.dirs import DirectoriesMixin from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory
sample_file: Path = Path(__file__).parent / "samples" / "simple.pdf" sample_file: Path = Path(__file__).parent / "samples" / "simple.pdf"
@@ -116,6 +117,27 @@ class TestMakeIndex:
call_command("document_index", "reindex", skip_checks=True) call_command("document_index", "reindex", skip_checks=True)
mock_get_backend.return_value.rebuild.assert_called_once() mock_get_backend.return_value.rebuild.assert_called_once()
def test_reindex_skips_versions(self, mocker: MockerFixture) -> None:
"""
GIVEN:
- A root document with a version
WHEN:
- The reindex command runs
THEN:
- Only the root document is handed to the rebuild, since a version
is indexed as its root
"""
root = DocumentFactory()
DocumentFactory(root_document=root, version_index=1)
mock_get_backend = mocker.patch(
"documents.management.commands.document_index.get_backend",
)
call_command("document_index", "reindex", skip_checks=True)
documents = mock_get_backend.return_value.rebuild.call_args.args[0]
assert list(documents.values_list("pk", flat=True)) == [root.pk]
def test_optimize(self) -> None: def test_optimize(self) -> None:
"""Optimize command must execute without error (Tantivy handles optimization automatically).""" """Optimize command must execute without error (Tantivy handles optimization automatically)."""
call_command("document_index", "optimize", skip_checks=True) call_command("document_index", "optimize", skip_checks=True)
@@ -2,6 +2,7 @@ import hashlib
import json import json
import shutil import shutil
import tempfile import tempfile
from datetime import date
from datetime import timedelta from datetime import timedelta
from io import StringIO from io import StringIO
from pathlib import Path from pathlib import Path
@@ -44,14 +45,18 @@ from documents.models import Workflow
from documents.models import WorkflowAction from documents.models import WorkflowAction
from documents.models import WorkflowTrigger from documents.models import WorkflowTrigger
from documents.sanity_checker import check_sanity from documents.sanity_checker import check_sanity
from documents.settings import EXPORTER_ARCHIVE_NAME
from documents.settings import EXPORTER_FILE_NAME from documents.settings import EXPORTER_FILE_NAME
from documents.settings import EXPORTER_SHARE_LINK_BUNDLE_NAME from documents.settings import EXPORTER_SHARE_LINK_BUNDLE_NAME
from documents.settings import EXPORTER_THUMBNAIL_NAME
from documents.tests.utils import SampleDirMixin from documents.tests.utils import SampleDirMixin
from paperless_mail.models import MailAccount from paperless_mail.models import MailAccount
from paperless_testing.assertions import FileSystemAssertsMixin from paperless_testing.assertions import FileSystemAssertsMixin
from paperless_testing.dirs import DirectoriesMixin from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.dirs import PaperlessDirs
from paperless_testing.dirs import paperless_environment from paperless_testing.dirs import paperless_environment
from paperless_testing.factories import DocumentBarcodeFactory from paperless_testing.factories import DocumentBarcodeFactory
from paperless_testing.factories import DocumentFactory
from paperless_testing.permissions import grant_object from paperless_testing.permissions import grant_object
@@ -1462,3 +1467,73 @@ class TestCryptExportImport(
("No passphrase was given, sensitive fields will be in plaintext"), ("No passphrase was given, sensitive fields will be in plaintext"),
stdout.read(), stdout.read(),
) )
@pytest.mark.management
@pytest.mark.django_db
class TestExportDerivedFileNameCollisions:
def test_same_stem_different_extension_get_distinct_derived_files(
self,
paperless_dirs: PaperlessDirs,
tmp_path: Path,
) -> None:
"""
GIVEN:
- Two documents with the same title and created date
- One original is a PDF, the other a PNG, both with archive and thumbnail
WHEN:
- The exporter runs
THEN:
- Each document is exported under a distinct original, thumbnail and archive name
- Each exported file holds its own document's content
"""
for pk, (filename, mime_type) in enumerate(
[("collide.pdf", "application/pdf"), ("collide.png", "image/png")],
start=1,
):
(paperless_dirs.originals_dir / filename).write_bytes(
f"original {pk}".encode(),
)
(paperless_dirs.archive_dir / f"{pk:07}.pdf").write_bytes(
f"archive {pk}".encode(),
)
(paperless_dirs.thumbnail_dir / f"{pk:07}.webp").write_bytes(
f"thumbnail {pk}".encode(),
)
DocumentFactory(
pk=pk,
title="collide",
created=date(2026, 10, 7),
filename=filename,
archive_filename=f"{pk:07}.pdf",
mime_type=mime_type,
)
target = tmp_path / "export"
target.mkdir()
call_command(
"document_exporter",
str(target),
"--no-progress-bar",
skip_checks=True,
)
manifest = json.loads((target / "manifest.json").read_text())
records = {r["pk"]: r for r in manifest if r["model"] == "documents.document"}
for key in (
EXPORTER_FILE_NAME,
EXPORTER_THUMBNAIL_NAME,
EXPORTER_ARCHIVE_NAME,
):
assert records[1][key] != records[2][key]
for pk in (1, 2):
record = records[pk]
assert (target / record[EXPORTER_FILE_NAME]).read_text() == f"original {pk}"
assert (
target / record[EXPORTER_THUMBNAIL_NAME]
).read_text() == f"thumbnail {pk}"
assert (
target / record[EXPORTER_ARCHIVE_NAME]
).read_text() == f"archive {pk}"
+520
View File
@@ -0,0 +1,520 @@
"""
Tests for documents.pdf_ops.
These use real PDFs from the sample directories. No database, Celery or mocks.
Pages are compared by a hash of their content stream, so page identity and order
are easy to assert.
"""
import hashlib
from collections.abc import Callable
from pathlib import Path
import pikepdf
import pytest
from documents import pdf_ops
from documents.pdf_ops import PageSpec
SRC_ROOT = Path(__file__).parents[2]
SAMPLES = Path(__file__).parent / "samples"
THREE_PAGES = SAMPLES / "documents" / "originals" / "0000002.pdf"
TWELVE_PAGES = SAMPLES / "barcodes" / "split-by-asn-2.pdf"
ENCRYPTED = SAMPLES / "password-is-test.pdf"
SIGNED = SRC_ROOT / "paperless" / "tests" / "samples" / "tesseract" / "signed.pdf"
def _page_fingerprint(page: pikepdf.Page) -> str:
contents = page.obj.get("/Contents")
assert contents is not None, "sample page has no /Contents"
streams = list(contents) if isinstance(contents, pikepdf.Array) else [contents]
return hashlib.sha256(b"".join(s.read_bytes() for s in streams)).hexdigest()
def fingerprints(path: Path) -> list[str]:
with pikepdf.open(path) as pdf:
return [_page_fingerprint(page) for page in pdf.pages]
def rotations(path: Path) -> list[int]:
with pikepdf.open(path) as pdf:
return [int(page.obj.get("/Rotate", 0)) for page in pdf.pages]
def docinfo_keys(path: Path) -> set[str]:
with pikepdf.open(path) as pdf:
return set(pdf.docinfo.keys())
def constant(path: Path) -> Callable[[], Path]:
return lambda: path
@pytest.fixture
def source_fingerprints() -> list[str]:
fps = fingerprints(THREE_PAGES)
assert len(set(fps)) == 3, "sample must have three distinct pages"
return fps
class TestRotatePdf:
def test_rotation_is_relative_and_applies_to_every_page(
self,
tmp_path: Path,
) -> None:
"""
GIVEN:
- A three page PDF
WHEN:
- It is rotated by 90 degrees, then the result is rotated by 90 again
THEN:
- Every page is rotated relative to its current rotation
- The page content itself is unchanged
"""
once = tmp_path / "once.pdf"
twice = tmp_path / "twice.pdf"
pdf_ops.rotate_pdf(THREE_PAGES, once, 90)
pdf_ops.rotate_pdf(once, twice, 90)
assert rotations(once) == [90, 90, 90]
assert rotations(twice) == [180, 180, 180]
assert fingerprints(twice) == fingerprints(THREE_PAGES)
def test_keeps_document_info(self, tmp_path: Path) -> None:
"""
GIVEN:
- A PDF with document info
WHEN:
- It is rotated
THEN:
- The document info is still present in the output
"""
dst = tmp_path / "out.pdf"
pdf_ops.rotate_pdf(THREE_PAGES, dst, 90)
assert "/Creator" in docinfo_keys(dst)
class TestRemovePages:
@pytest.mark.parametrize(
("pages", "kept"),
[
pytest.param([2], [0, 2], id="single"),
pytest.param([3, 1], [1], id="unordered"),
pytest.param([2, 2], [0, 2], id="duplicates-remove-once"),
pytest.param([], [0, 1, 2], id="empty-keeps-everything"),
pytest.param([1, 2, 3], [], id="every-page"),
],
)
def test_removes_only_the_requested_pages(
self,
tmp_path: Path,
source_fingerprints: list[str],
pages: list[int],
kept: list[int],
) -> None:
"""
GIVEN:
- A three page PDF
WHEN:
- Pages are removed, in any order and possibly repeated
THEN:
- Exactly the other pages remain, in their original order
"""
dst = tmp_path / "out.pdf"
pdf_ops.remove_pages(THREE_PAGES, dst, pages)
assert fingerprints(dst) == [source_fingerprints[i] for i in kept]
def test_keeps_document_info(self, tmp_path: Path) -> None:
"""
GIVEN:
- A PDF with document info
WHEN:
- A page is removed
THEN:
- The document info is still present in the output
"""
dst = tmp_path / "out.pdf"
pdf_ops.remove_pages(THREE_PAGES, dst, [1])
assert "/Creator" in docinfo_keys(dst)
@pytest.mark.parametrize(
"bad_page",
[
pytest.param(0, id="zero"),
pytest.param(-1, id="negative"),
],
)
def test_rejects_pages_below_one(self, tmp_path: Path, bad_page: int) -> None:
"""
GIVEN:
- A three page PDF
WHEN:
- Pages are removed and one of them is below 1
THEN:
- ValueError is raised
- No output file is written
"""
dst = tmp_path / "out.pdf"
with pytest.raises(ValueError, match="start at 1"):
pdf_ops.remove_pages(THREE_PAGES, dst, [1, bad_page])
assert not dst.exists()
class TestBuildPdfs:
def test_selects_and_orders_pages(
self,
tmp_path: Path,
source_fingerprints: list[str],
) -> None:
"""
GIVEN:
- A three page PDF
WHEN:
- One output is built from pages 3 then 1
THEN:
- The output has those pages in that order
- Its path is returned
"""
dst = tmp_path / "out.pdf"
written = pdf_ops.build_pdfs(
THREE_PAGES,
[([PageSpec(3), PageSpec(1)], constant(dst))],
)
assert written == [dst]
assert fingerprints(dst) == [source_fingerprints[2], source_fingerprints[0]]
def test_rotates_only_the_requested_pages(self, tmp_path: Path) -> None:
"""
GIVEN:
- A three page PDF
WHEN:
- One output is built with a different rotation on each page
THEN:
- Each output page has exactly the rotation requested for it
"""
dst = tmp_path / "out.pdf"
pdf_ops.build_pdfs(
THREE_PAGES,
[([PageSpec(1), PageSpec(2, 90), PageSpec(3, 180)], constant(dst))],
)
assert rotations(dst) == [0, 90, 180]
def test_writes_one_file_per_output_in_order(self, tmp_path: Path) -> None:
"""
GIVEN:
- A twelve page PDF
WHEN:
- Two outputs are built from different page ranges
THEN:
- Two files are written and returned in output order
- Each holds exactly its own pages
"""
first = tmp_path / "first.pdf"
second = tmp_path / "second.pdf"
source = fingerprints(TWELVE_PAGES)
written = pdf_ops.build_pdfs(
TWELVE_PAGES,
[
([PageSpec(p) for p in (1, 2, 3)], constant(first)),
([PageSpec(p) for p in range(4, 13)], constant(second)),
],
)
assert written == [first, second]
assert fingerprints(first) == source[:3]
assert fingerprints(second) == source[3:]
def test_empty_page_list_writes_a_zero_page_file(self, tmp_path: Path) -> None:
"""
GIVEN:
- A three page PDF
WHEN:
- An output with no pages is built
THEN:
- A PDF with zero pages is written
"""
dst = tmp_path / "out.pdf"
pdf_ops.build_pdfs(THREE_PAGES, [([], constant(dst))])
assert fingerprints(dst) == []
def test_destination_is_not_requested_when_a_page_is_out_of_range(
self,
tmp_path: Path,
) -> None:
"""
GIVEN:
- A three page PDF
WHEN:
- Several outputs are built
- A later output refers to a page past the end
THEN:
- IndexError is raised
- No destination was requested for any output, including earlier valid ones
"""
requested: list[Path] = []
def make_dst() -> Path:
requested.append(tmp_path / "out.pdf")
return requested[-1]
with pytest.raises(IndexError):
pdf_ops.build_pdfs(
THREE_PAGES,
[([PageSpec(1)], make_dst), ([PageSpec(99)], make_dst)],
)
assert requested == []
@pytest.mark.parametrize(
"bad_page",
[
pytest.param(0, id="zero"),
pytest.param(-1, id="negative"),
],
)
def test_rejects_pages_below_one_before_opening_anything(
self,
tmp_path: Path,
bad_page: int,
) -> None:
"""
GIVEN:
- A three page PDF
WHEN:
- Several outputs are built and a later one has a page below 1
THEN:
- ValueError is raised
- No destination was requested for any output
"""
requested: list[Path] = []
def make_dst() -> Path:
requested.append(tmp_path / "out.pdf")
return requested[-1]
with pytest.raises(ValueError, match="start at 1"):
pdf_ops.build_pdfs(
THREE_PAGES,
[([PageSpec(1)], make_dst), ([PageSpec(bad_page)], make_dst)],
)
assert requested == []
class TestValidatePageOperations:
def test_returns_the_output_count(self) -> None:
"""
GIVEN:
- Operations that all target the default output
WHEN:
- They are validated
THEN:
- One output document is reported
"""
operations = [{"page": 1}, {"page": 2}, {"page": 3}]
assert pdf_ops.validate_page_operations(operations, single_output=True) == 1
def test_gap_in_output_indices_counts_up_to_the_highest(self) -> None:
"""
GIVEN:
- Operations that target outputs 0 and 2 but never 1
WHEN:
- They are validated
THEN:
- Three output documents are reported
"""
operations = [
{"page": 1, "doc": 0},
{"page": 2, "doc": 2},
{"page": 3, "doc": 0},
]
count = pdf_ops.validate_page_operations(operations, single_output=False)
assert count == 3
def test_empty_operations_are_rejected(self) -> None:
"""
GIVEN:
- No operations
WHEN:
- They are validated
THEN:
- ValueError is raised
"""
with pytest.raises(ValueError, match="index is out of bounds"):
pdf_ops.validate_page_operations([], single_output=False)
def test_multiple_outputs_rejected_when_single_output_required(self) -> None:
"""
GIVEN:
- Operations that target two outputs
WHEN:
- They are validated with a single output required
THEN:
- ValueError is raised
"""
operations = [{"page": 1, "doc": 0}, {"page": 2, "doc": 1}]
with pytest.raises(ValueError, match="Multiple output documents"):
pdf_ops.validate_page_operations(operations, single_output=True)
@pytest.mark.parametrize(
"doc",
[
pytest.param(-1, id="negative"),
pytest.param(2, id="equal-to-operation-count"),
pytest.param(2**32, id="huge"),
],
)
def test_output_index_out_of_bounds(self, doc: int) -> None:
"""
GIVEN:
- Two operations, one with an output index that is out of bounds
WHEN:
- They are validated
THEN:
- ValueError is raised
"""
operations = [{"page": 1, "doc": 0}, {"page": 2, "doc": doc}]
with pytest.raises(ValueError, match="index is out of bounds"):
pdf_ops.validate_page_operations(operations, single_output=False)
class TestDecrypt:
@pytest.mark.parametrize(
("path", "expected"),
[
pytest.param(ENCRYPTED, True, id="password-required"),
pytest.param(SIGNED, True, id="opens-without-password-but-encrypted"),
pytest.param(THREE_PAGES, False, id="not-encrypted"),
],
)
def test_needs_decrypt(self, path: Path, *, expected: bool) -> None:
"""
GIVEN:
- A PDF that is encrypted, or encrypted but openable, or plain
WHEN:
- needs_decrypt is asked about it
THEN:
- Only the unencrypted PDF reports False
"""
assert pdf_ops.needs_decrypt(path) is expected
def test_decrypt_writes_an_unencrypted_copy(self, tmp_path: Path) -> None:
"""
GIVEN:
- A password protected PDF
WHEN:
- It is decrypted with the correct password
THEN:
- The path from make_dst is returned
- The written copy no longer needs decrypting
"""
dst = tmp_path / "out.pdf"
result = pdf_ops.decrypt_pdf(ENCRYPTED, constant(dst), "test")
assert result == dst
assert pdf_ops.needs_decrypt(dst) is False
def test_wrong_password_raises_and_never_requests_a_destination(
self,
tmp_path: Path,
) -> None:
"""
GIVEN:
- A password protected PDF
WHEN:
- It is decrypted with the wrong password
THEN:
- PasswordError is raised
- No destination was requested
"""
requested: list[Path] = []
def make_dst() -> Path:
requested.append(tmp_path / "out.pdf")
return requested[-1]
with pytest.raises(pikepdf.PasswordError):
pdf_ops.decrypt_pdf(ENCRYPTED, make_dst, "wrong")
assert requested == []
class TestPdfMerger:
def test_pages_are_appended_in_the_order_added(
self,
tmp_path: Path,
source_fingerprints: list[str],
) -> None:
"""
GIVEN:
- A reordered PDF and the original three page PDF
WHEN:
- Both are added to a merger in that order and saved
THEN:
- The output holds all pages in the order they were added
"""
reordered = tmp_path / "reordered.pdf"
merged = tmp_path / "merged.pdf"
pdf_ops.build_pdfs(
THREE_PAGES,
[([PageSpec(3), PageSpec(1)], constant(reordered))],
)
with pdf_ops.PdfMerger() as merger:
merger.add(reordered)
merger.add(THREE_PAGES)
merger.save(merged)
assert fingerprints(merged) == [
source_fingerprints[2],
source_fingerprints[0],
*source_fingerprints,
]
def test_output_version_is_at_least_the_highest_source_version(
self,
tmp_path: Path,
) -> None:
"""
GIVEN:
- Two PDFs with different PDF versions
WHEN:
- Both are added to a merger and saved
THEN:
- The output version is at least the highest source version
"""
merged = tmp_path / "merged.pdf"
with pikepdf.open(TWELVE_PAGES) as pdf:
source_versions = [pdf.pdf_version]
with pikepdf.open(THREE_PAGES) as pdf:
source_versions.append(pdf.pdf_version)
with pdf_ops.PdfMerger() as merger:
merger.add(TWELVE_PAGES)
merger.add(THREE_PAGES)
merger.save(merged)
with pikepdf.open(merged) as pdf:
assert pdf.pdf_version >= max(source_versions)
@@ -18,6 +18,7 @@ from documents.models import Correspondent
from documents.models import DocumentType from documents.models import DocumentType
from documents.models import StoragePath from documents.models import StoragePath
from documents.models import Tag from documents.models import Tag
from documents.permissions import has_perms_owner_aware
from documents.permissions import permitted_document_ids from documents.permissions import permitted_document_ids
from documents.permissions import permitted_object_ids from documents.permissions import permitted_object_ids
from documents.permissions import restrict_queryset_to_visible from documents.permissions import restrict_queryset_to_visible
@@ -32,6 +33,8 @@ from paperless_testing.permissions import grant_global
from paperless_testing.permissions import grant_object from paperless_testing.permissions import grant_object
if TYPE_CHECKING: if TYPE_CHECKING:
from django.contrib.auth.models import User
from paperless_testing.dirs import PaperlessDirs from paperless_testing.dirs import PaperlessDirs
@@ -178,6 +181,382 @@ class TestPermittedDocumentIdsIncludeDeleted:
) )
@pytest.mark.django_db
class TestPermittedDocumentIdsVersions:
"""
A version is authorized by its root document: the version's own owner and
grants never matter.
"""
@pytest.mark.parametrize(
("root_owner", "version_owner", "expected_visible"),
[
pytest.param(
"other",
"nobody",
False,
id="unowned-version-of-private-root",
),
pytest.param("other", "user", False, id="own-version-of-private-root"),
pytest.param("user", "other", True, id="foreign-version-of-own-root"),
pytest.param("user", "nobody", True, id="unowned-version-of-own-root"),
pytest.param("nobody", "other", True, id="private-version-of-unowned-root"),
],
)
def test_version_follows_root_owner(
self,
root_owner: str,
version_owner: str,
*,
expected_visible: bool,
) -> None:
"""
GIVEN:
- A root document and a version with differing owners
WHEN:
- The permitted document ids are resolved for the user
THEN:
- The version is visible exactly when its root is
"""
user = UserFactory()
owners = {"user": user, "other": UserFactory(), "nobody": None}
root = DocumentFactory(owner=owners[root_owner])
version = DocumentFactory(root_document=root, owner=owners[version_owner])
visible = set(permitted_document_ids(user))
assert (version.pk in visible) is expected_visible
assert (root.pk in visible) is expected_visible
@staticmethod
def grantee(user: User, kind: str) -> User | Group:
"""The user itself, or a new group the user belongs to."""
if kind == "user":
return user
group = Group.objects.create(name="shared")
user.groups.add(group)
return group
@pytest.mark.parametrize(
"grantee_kind",
[pytest.param("user", id="user"), pytest.param("group", id="group")],
)
def test_grant_on_root_applies_to_version(self, grantee_kind: str) -> None:
"""
GIVEN:
- A private root document shared with a user or one of their groups
- A version of it owned by someone else
WHEN:
- The permitted document ids are resolved for the user
THEN:
- Both the root and the version are visible
- A user without the grant sees neither
"""
user = UserFactory()
stranger = UserFactory()
root = DocumentFactory(owner=UserFactory())
version = DocumentFactory(root_document=root, owner=UserFactory())
grant_object(self.grantee(user, grantee_kind), root, "view_document")
assert_visible_document_ids(
permitted_document_ids(user),
expected_visible=[root.pk, version.pk],
expected_hidden=[],
)
assert_visible_document_ids(
permitted_document_ids(stranger),
expected_visible=[],
expected_hidden=[root.pk, version.pk],
)
@pytest.mark.parametrize(
"grantee_kind",
[pytest.param("user", id="user"), pytest.param("group", id="group")],
)
def test_grant_on_version_is_ignored(self, grantee_kind: str) -> None:
"""
GIVEN:
- A private root document
- A version with an explicit grant for the user or one of their groups
WHEN:
- The permitted document ids are resolved for the user
THEN:
- Neither the root nor the version is visible
"""
user = UserFactory()
root = DocumentFactory(owner=UserFactory())
version = DocumentFactory(root_document=root, owner=UserFactory())
grant_object(self.grantee(user, grantee_kind), version, "view_document")
assert_visible_document_ids(
permitted_document_ids(user),
expected_visible=[],
expected_hidden=[root.pk, version.pk],
)
def test_grant_on_one_root_does_not_reach_another_roots_version(self) -> None:
"""
GIVEN:
- Two private roots, each with a version
- The user may view only the first root
WHEN:
- The permitted document ids are resolved for the user
THEN:
- Only the first root and its version are visible
"""
user = UserFactory()
first = DocumentFactory(owner=UserFactory())
first_version = DocumentFactory(root_document=first, owner=UserFactory())
second = DocumentFactory(owner=UserFactory())
second_version = DocumentFactory(root_document=second, owner=user)
grant_object(user, first, "view_document")
assert_visible_document_ids(
permitted_document_ids(user),
expected_visible=[first.pk, first_version.pk],
expected_hidden=[second.pk, second_version.pk],
)
def test_user_in_several_groups(self) -> None:
"""
GIVEN:
- A user in two groups
- Two private roots shared with one group each, and a third shared with nobody
- A version of each root
WHEN:
- The permitted document ids are resolved for the user
THEN:
- The two shared roots and their versions are visible
- The third root and its version are not
"""
user = UserFactory()
groups = [Group.objects.create(name=f"group{i}") for i in range(2)]
user.groups.add(*groups)
shared = [DocumentFactory(owner=UserFactory()) for _ in groups]
for root, group in zip(shared, groups, strict=True):
grant_object(group, root, "view_document")
unshared = DocumentFactory(owner=UserFactory())
shared_versions = [
DocumentFactory(root_document=root, owner=UserFactory()) for root in shared
]
unshared_version = DocumentFactory(root_document=unshared, owner=None)
assert_visible_document_ids(
permitted_document_ids(user),
expected_visible=[
*(root.pk for root in shared),
*(version.pk for version in shared_versions),
],
expected_hidden=[unshared.pk, unshared_version.pk],
)
def test_permission_is_resolved_through_the_root(self) -> None:
"""
GIVEN:
- A private root document where the user may view and change
WHEN:
- The permitted ids are resolved for view, change and delete
THEN:
- The version is visible for view and change only
"""
user = UserFactory()
root = DocumentFactory(owner=UserFactory())
version = DocumentFactory(root_document=root, owner=UserFactory())
grant_object(user, root, "view_document", "change_document")
assert version.pk in set(permitted_document_ids(user))
assert version.pk in set(permitted_document_ids(user, perm="change_document"))
assert version.pk in set(
permitted_document_ids(user, perm="documents.change_document"),
)
assert version.pk not in set(
permitted_document_ids(user, perm="delete_document"),
)
def test_anonymous_sees_versions_of_unowned_roots_only(self) -> None:
"""
GIVEN:
- A version owned by nobody under a private root
- A version owned by someone under an unowned root
WHEN:
- The permitted document ids are resolved for an anonymous user
THEN:
- Only the version of the unowned root is visible
"""
private_root = DocumentFactory(owner=UserFactory())
private_version = DocumentFactory(root_document=private_root, owner=None)
open_root = DocumentFactory(owner=None)
open_version = DocumentFactory(root_document=open_root, owner=UserFactory())
assert_visible_document_ids(
permitted_document_ids(AnonymousUser()),
expected_visible=[open_root.pk, open_version.pk],
expected_hidden=[private_root.pk, private_version.pk],
)
def test_deleted_versions_follow_their_deleted_root(self) -> None:
"""
GIVEN:
- A soft-deleted root document and its version, which deleting the
root soft-deletes too; the version is owned by someone else
WHEN:
- The permitted document ids are resolved with and without deleted
documents
THEN:
- Nothing is visible by default
- With deleted documents included, the version is visible to the
root's owner and not to the version's own owner
"""
owner = UserFactory()
version_owner = UserFactory()
root = DocumentFactory(owner=owner)
version = DocumentFactory(root_document=root, owner=version_owner)
root.delete()
assert not {root.pk, version.pk} & set(permitted_document_ids(owner))
assert_visible_document_ids(
permitted_document_ids(owner, include_deleted=True),
expected_visible=[root.pk, version.pk],
expected_hidden=[],
)
assert_visible_document_ids(
permitted_document_ids(version_owner, include_deleted=True),
expected_visible=[],
expected_hidden=[root.pk, version.pk],
)
@pytest.mark.parametrize(
"is_superuser",
[
pytest.param(False, id="regular-user"),
pytest.param(True, id="superuser"),
],
)
def test_inactive_user_sees_no_versions(self, *, is_superuser: bool) -> None:
"""
GIVEN:
- An inactive user, possibly a superuser, who owns a root and its version
WHEN:
- The permitted document ids are resolved for them
THEN:
- Nothing is visible
"""
user = UserFactory(is_active=False, is_superuser=is_superuser)
root = DocumentFactory(owner=user)
version = DocumentFactory(root_document=root, owner=user)
assert_visible_document_ids(
permitted_document_ids(user),
expected_visible=[],
expected_hidden=[root.pk, version.pk],
)
def test_superuser_sees_all_versions(self) -> None:
"""
GIVEN:
- A private root owned by someone else, with a version
WHEN:
- The permitted document ids are resolved for a superuser
THEN:
- Both the root and the version are visible
"""
superuser = UserFactory(superuser=True)
root = DocumentFactory(owner=UserFactory())
version = DocumentFactory(root_document=root, owner=UserFactory())
assert_visible_document_ids(
permitted_document_ids(superuser),
expected_visible=[root.pk, version.pk],
expected_hidden=[],
)
@pytest.mark.django_db
class TestHasPermsOwnerAwareVersions:
"""
The single-object check agrees with permitted_document_ids: a version is
authorized by its root document.
"""
@pytest.mark.parametrize(
("root_owner", "version_owner", "expected"),
[
pytest.param(
"other",
"nobody",
False,
id="unowned-version-of-private-root",
),
pytest.param("other", "user", False, id="own-version-of-private-root"),
pytest.param("user", "other", True, id="foreign-version-of-own-root"),
pytest.param("nobody", "other", True, id="private-version-of-unowned-root"),
],
)
def test_version_follows_root_owner(
self,
root_owner: str,
version_owner: str,
*,
expected: bool,
) -> None:
"""
GIVEN:
- A root document and a version with differing owners
WHEN:
- The single-object check runs for the version
THEN:
- The version is allowed exactly when its root is
"""
user = UserFactory()
owners = {"user": user, "other": UserFactory(), "nobody": None}
root = DocumentFactory(owner=owners[root_owner])
version = DocumentFactory(root_document=root, owner=owners[version_owner])
assert has_perms_owner_aware(user, "view_document", version) is expected
assert has_perms_owner_aware(user, "view_document", root) is expected
def test_grant_on_root_applies_and_grant_on_version_does_not(self) -> None:
"""
GIVEN:
- A private root with a version, and a second private root with a version
- The user may change only the first root, and was granted the second
root's version directly
WHEN:
- The single-object check runs for each version
THEN:
- Only the first root's version is allowed
"""
user = UserFactory()
shared_root = DocumentFactory(owner=UserFactory())
shared_version = DocumentFactory(root_document=shared_root, owner=UserFactory())
private_root = DocumentFactory(owner=UserFactory())
private_version = DocumentFactory(
root_document=private_root,
owner=UserFactory(),
)
grant_object(user, shared_root, "change_document")
grant_object(user, private_version, "change_document")
assert has_perms_owner_aware(user, "change_document", shared_version)
assert not has_perms_owner_aware(user, "change_document", private_version)
def test_other_models_use_their_own_owner(self) -> None:
"""
GIVEN:
- A tag owned by someone else, and one owned by the user
WHEN:
- The single-object check runs for each
THEN:
- Only the user's own tag is allowed without a grant
"""
user = UserFactory()
mine = TagFactory(owner=user)
theirs = TagFactory(owner=UserFactory())
assert has_perms_owner_aware(user, "view_tag", mine)
assert not has_perms_owner_aware(user, "view_tag", theirs)
@pytest.mark.django_db @pytest.mark.django_db
class TestAiChatAllDocumentsPermissionBoundary: class TestAiChatAllDocumentsPermissionBoundary:
""" """
@@ -90,10 +90,13 @@ class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn("document_ids", response.data) self.assertIn("document_ids", response.data)
@mock.patch("documents.views.permitted_document_ids", return_value=set()) def test_create_bundle_rejects_insufficient_permissions(self) -> None:
def test_create_bundle_rejects_insufficient_permissions(self, perms_mock) -> None: requester = UserFactory(username="bundle_creator")
grant_global(requester, "add_sharelinkbundle", "view_document")
self.client.force_authenticate(requester)
document = DocumentFactory(owner=UserFactory(username="document_owner"))
payload = { payload = {
"document_ids": [self.document.pk], "document_ids": [self.document.pk, document.pk],
"file_version": ShareLink.FileVersion.ARCHIVE, "file_version": ShareLink.FileVersion.ARCHIVE,
"expiration_days": 7, "expiration_days": 7,
} }
@@ -101,8 +104,8 @@ class ShareLinkBundleAPITests(DirectoriesMixin, APITestCase):
response = self.client.post(self.ENDPOINT, payload, format="json") response = self.client.post(self.ENDPOINT, payload, format="json")
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn("document_ids", response.data) self.assertIn(str(document.pk), str(response.data["document_ids"]))
perms_mock.assert_called() self.assertFalse(ShareLinkBundle.objects.exists())
@mock.patch("documents.views.build_share_link_bundle.apply_async") @mock.patch("documents.views.build_share_link_bundle.apply_async")
def test_rebuild_bundle_resets_state(self, delay_mock) -> None: def test_rebuild_bundle_resets_state(self, delay_mock) -> None:
+75
View File
@@ -20,6 +20,7 @@ from documents.sanity_checker import SanityCheckMessages
from documents.tests.helpers import dummy_preprocess from documents.tests.helpers import dummy_preprocess
from paperless_testing.assertions import FileSystemAssertsMixin from paperless_testing.assertions import FileSystemAssertsMixin
from paperless_testing.dirs import DirectoriesMixin from paperless_testing.dirs import DirectoriesMixin
from paperless_testing.factories import DocumentFactory
@pytest.mark.django_db @pytest.mark.django_db
@@ -287,6 +288,80 @@ class TestUpdateContent(DirectoriesMixin, TestCase):
tasks.update_document_content_maybe_archive_file(doc.pk) tasks.update_document_content_maybe_archive_file(doc.pk)
self.assertNotEqual(Document.objects.get(pk=doc.pk).content, "test") self.assertNotEqual(Document.objects.get(pk=doc.pk).content, "test")
def _create_root_with_version(self) -> tuple[Document, Document]:
sample1 = self.dirs.scratch_dir / "sample.pdf"
shutil.copy(
Path(__file__).parent
/ "samples"
/ "documents"
/ "originals"
/ "0000001.pdf",
sample1,
)
root = DocumentFactory(content="root content", mime_type="application/pdf")
version = DocumentFactory(
content="my document",
filename=sample1,
mime_type="application/pdf",
root_document=root,
version_index=1,
)
return root, version
@mock.patch("documents.tasks.clear_document_caches")
@mock.patch("documents.search.get_backend")
def test_update_content_version_clears_caches_for_root(
self,
mock_get_backend: mock.Mock,
mock_clear_caches: mock.Mock,
) -> None:
"""
GIVEN:
- A root document with a version
WHEN:
- Update content task is called for the version
THEN:
- The version's content is updated, not the root's
- The document is indexed
- Caches are cleared for both
"""
root, version = self._create_root_with_version()
tasks.update_document_content_maybe_archive_file(version.pk)
self.assertNotEqual(
Document.objects.get(pk=version.pk).content,
"my document",
)
self.assertEqual(Document.objects.get(pk=root.pk).content, "root content")
mock_get_backend.return_value.add_or_update.assert_called_once()
mock_clear_caches.assert_has_calls(
[mock.call(version.pk), mock.call(root.pk)],
)
@override_settings(AI_ENABLED=True, LLM_EMBEDDING_BACKEND="huggingface")
@mock.patch("documents.tasks.llm_index_add_or_update_document")
@mock.patch("documents.search.get_backend")
def test_update_content_version_updates_llm_index(
self,
mock_get_backend: mock.Mock,
mock_llm_index: mock.Mock,
) -> None:
"""
GIVEN:
- A root document with a version
- The LLM index is enabled
WHEN:
- Update content task is called for the version
THEN:
- The LLM index is updated
"""
_, version = self._create_root_with_version()
tasks.update_document_content_maybe_archive_file(version.pk)
mock_llm_index.assert_called_once()
class TestUpdateContentRemoteOCR(DirectoriesMixin, TestCase): class TestUpdateContentRemoteOCR(DirectoriesMixin, TestCase):
""" """
+17
View File
@@ -17,9 +17,26 @@ from django.db.models.functions import RowNumber
from documents.models import Document from documents.models import Document
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Iterable
from rest_framework.request import Request from rest_framework.request import Request
def root_document_ids(ids: Iterable[int]) -> QuerySet[int]:
"""
The ids of the root documents of the given documents: a root stands for
itself and a version for its root. Only the indexes' bookkeeping needs
this, since they hold root documents only.
"""
return (
Document.objects.filter(pk__in=ids)
.annotate(root_id=Coalesce("root_document_id", "id"))
.order_by()
.values_list("root_id", flat=True)
.distinct()
)
def versions_newest_first(documents: QuerySet[Document]) -> QuerySet[Document]: def versions_newest_first(documents: QuerySet[Document]) -> QuerySet[Document]:
""" """
Sorts versions so the newest one comes first using version_index and not on id, Sorts versions so the newest one comes first using version_index and not on id,
+68 -51
View File
@@ -329,9 +329,10 @@ def _get_tantivy_query_and_mode(params):
def _get_more_like_id(query_params: dict[str, Any], user: User | None) -> int: def _get_more_like_id(query_params: dict[str, Any], user: User | None) -> int:
try: try:
more_like_doc_id = int(query_params["more_like_id"]) more_like_doc_id = int(query_params["more_like_id"])
more_like_doc = Document.objects.select_related("owner").get( more_like_doc = Document.objects.select_related(
pk=more_like_doc_id, "owner",
) "root_document__owner",
).get(pk=more_like_doc_id)
except (TypeError, ValueError, Document.DoesNotExist): except (TypeError, ValueError, Document.DoesNotExist):
raise PermissionDenied(_("Invalid more_like_id")) raise PermissionDenied(_("Invalid more_like_id"))
@@ -342,7 +343,8 @@ def _get_more_like_id(query_params: dict[str, Any], user: User | None) -> int:
): ):
raise PermissionDenied(_("Insufficient permissions.")) raise PermissionDenied(_("Insufficient permissions."))
return more_like_doc_id # Only root documents are indexed, a version stands for its root
return more_like_doc.root_document_id or more_like_doc.pk
class SearchParams(NamedTuple): class SearchParams(NamedTuple):
@@ -1550,7 +1552,10 @@ class DocumentViewSet(
) )
def suggestions(self, request, pk=None): def suggestions(self, request, pk=None):
doc = get_object_or_404( doc = get_object_or_404(
Document.objects.select_related("owner").prefetch_related("versions"), Document.objects.select_related(
"owner",
"root_document__owner",
).prefetch_related("versions"),
pk=pk, pk=pk,
) )
if request.user is not None and not has_perms_owner_aware( if request.user is not None and not has_perms_owner_aware(
@@ -1610,7 +1615,10 @@ class DocumentViewSet(
@method_decorator(cache_control(no_cache=True)) @method_decorator(cache_control(no_cache=True))
def ai_suggestions(self, request, pk=None): def ai_suggestions(self, request, pk=None):
doc = get_object_or_404( doc = get_object_or_404(
Document.objects.select_related("owner").prefetch_related("versions"), Document.objects.select_related(
"owner",
"root_document__owner",
).prefetch_related("versions"),
pk=pk, pk=pk,
) )
if request.user is not None and not has_perms_owner_aware( if request.user is not None and not has_perms_owner_aware(
@@ -1856,9 +1864,14 @@ class DocumentViewSet(
currentUser = request.user currentUser = request.user
try: try:
doc = ( doc = (
Document.objects.select_related("owner") Document.objects.select_related("owner", "root_document__owner")
.prefetch_related("notes") .prefetch_related("notes")
.only("pk", "owner__id") .only(
"pk",
"owner__id",
"root_document__id",
"root_document__owner__id",
)
.get(pk=pk) .get(pk=pk)
) )
if currentUser is not None and not has_perms_owner_aware( if currentUser is not None and not has_perms_owner_aware(
@@ -1973,7 +1986,9 @@ class DocumentViewSet(
def share_links(self, request, pk=None): def share_links(self, request, pk=None):
currentUser = request.user currentUser = request.user
try: try:
doc = Document.objects.select_related("owner").get(pk=pk) doc = Document.objects.select_related("owner", "root_document__owner").get(
pk=pk,
)
if currentUser is not None and not has_perms_owner_aware( if currentUser is not None and not has_perms_owner_aware(
currentUser, currentUser,
"change_document", "change_document",
@@ -2008,10 +2023,11 @@ class DocumentViewSet(
if not settings.AUDIT_LOG_ENABLED: if not settings.AUDIT_LOG_ENABLED:
return HttpResponseBadRequest("Audit log is disabled") return HttpResponseBadRequest("Audit log is disabled")
try: try:
doc = Document.objects.get(pk=pk) doc = Document.objects.select_related("root_document__owner").get(pk=pk)
root_doc = get_root_document(doc)
if not request.user.has_perm("auditlog.view_logentry") or ( if not request.user.has_perm("auditlog.view_logentry") or (
doc.owner is not None root_doc.owner is not None
and doc.owner != request.user and root_doc.owner != request.user
and not request.user.is_superuser and not request.user.is_superuser
): ):
return HttpResponseForbidden( return HttpResponseForbidden(
@@ -2102,9 +2118,7 @@ class DocumentViewSet(
documents = Document.objects.filter(pk__in=document_ids) documents = Document.objects.filter(pk__in=document_ids)
if ( if (
request.user is not None request.user is not None
and documents.exclude( and documents.exclude(id__in=permitted_document_ids(request.user)).exists()
pk__in=permitted_document_ids(request.user),
).exists()
): ):
return HttpResponseForbidden("Insufficient permissions") return HttpResponseForbidden("Insufficient permissions")
@@ -2430,11 +2444,17 @@ class ChatStreamingView(GenericAPIView[Any]):
if doc_id: if doc_id:
try: try:
document = Document.objects.get(id=doc_id) document = Document.objects.select_related(
"root_document__owner",
).get(id=doc_id)
except Document.DoesNotExist: except Document.DoesNotExist:
return HttpResponseBadRequest("Document not found") return HttpResponseBadRequest("Document not found")
if not has_perms_owner_aware(request.user, "view_document", document): if not has_perms_owner_aware(
request.user,
"view_document",
document,
):
return HttpResponseForbidden("Insufficient permissions") return HttpResponseForbidden("Insufficient permissions")
documents = Document.objects.filter(pk=document.pk) documents = Document.objects.filter(pk=document.pk)
@@ -2990,12 +3010,8 @@ class DocumentOperationPermissionMixin(PassUserMixin, DocumentSelectionMixin):
user.has_perm( user.has_perm(
"documents.change_document", "documents.change_document",
) )
and not Document.global_objects.filter( and not Document.global_objects.filter(pk__in=documents)
pk__in=[doc.pk for doc in root_docs], .exclude(pk__in=permitted_document_ids(user, perm="change_document"))
)
.exclude(
pk__in=permitted_document_ids(user, perm="change_document"),
)
.exists() .exists()
) )
@@ -3605,10 +3621,11 @@ class SelectionDataView(DocumentSelectionMixin, GenericAPIView[Any]):
user=request.user, user=request.user,
validated_data=serializer.validated_data, validated_data=serializer.validated_data,
) )
permitted_documents = Document.objects.filter( documents = Document.objects.filter(pk__in=ids)
id__in=permitted_document_ids(request.user), if (
) documents.count() != len(ids)
if permitted_documents.filter(pk__in=ids).count() != len(ids): or documents.exclude(id__in=permitted_document_ids(request.user)).exists()
):
return HttpResponseForbidden("Insufficient permissions") return HttpResponseForbidden("Insufficient permissions")
correspondents = Correspondent.objects.annotate( correspondents = Correspondent.objects.annotate(
@@ -4104,21 +4121,16 @@ class BulkDownloadView(DocumentSelectionMixin, GenericAPIView[Any]):
validated_data=serializer.validated_data, validated_data=serializer.validated_data,
) )
documents = Document.objects.filter(pk__in=ids) documents = Document.objects.filter(pk__in=ids)
versioned_documents = []
compression = serializer.validated_data.get("compression") compression = serializer.validated_data.get("compression")
content = serializer.validated_data.get("content") content = serializer.validated_data.get("content")
follow_filename_format = serializer.validated_data.get("follow_formatting") follow_filename_format = serializer.validated_data.get("follow_formatting")
permitted_ids = set(permitted_document_ids(request.user)) if documents.exclude(id__in=permitted_document_ids(request.user)).exists():
for document in documents: return HttpResponseForbidden("Insufficient permissions")
root_doc = get_root_document(document) versioned_documents = [
if root_doc.pk not in permitted_ids: get_latest_version_for_root(get_root_document(document))
return HttpResponseForbidden("Insufficient permissions") for document in documents
versioned_documents.append( ]
get_latest_version_for_root(
root_doc,
),
)
if content == "both": if content == "both":
strategy_class = OriginalAndArchiveStrategy strategy_class = OriginalAndArchiveStrategy
@@ -4790,19 +4802,23 @@ class ShareLinkBundleViewSet(PassUserMixin, ModelViewSet[ShareLinkBundle]):
}, },
) )
documents = list(documents_qs) denied_id = (
permitted_ids = set(permitted_document_ids(request.user)) documents_qs.exclude(id__in=permitted_document_ids(request.user))
for document in documents: .order_by("pk")
if document.pk not in permitted_ids: .values_list("pk", flat=True)
raise ValidationError( .first()
{ )
"document_ids": _( if denied_id is not None:
"Insufficient permissions to share document %(id)s.", raise ValidationError(
) {
% {"id": document.pk}, "document_ids": _(
}, "Insufficient permissions to share document %(id)s.",
) )
% {"id": denied_id},
},
)
documents = list(documents_qs)
document_map = {document.pk: document for document in documents} document_map = {document.pk: document for document in documents}
ordered_documents = [document_map[doc_id] for doc_id in document_ids] ordered_documents = [document_map[doc_id] for doc_id in document_ids]
@@ -5586,6 +5602,7 @@ class TrashView(ListModelMixin, PassUserMixin):
class _TrashPermittedObjectsFilter(PermittedObjectsFilter): class _TrashPermittedObjectsFilter(PermittedObjectsFilter):
include_granted = False include_granted = False
parent_field = "root_document"
filter_backends = (_TrashPermittedObjectsFilter,) filter_backends = (_TrashPermittedObjectsFilter,)
pagination_class = StandardPagination pagination_class = StandardPagination
@@ -5617,7 +5634,7 @@ class TrashView(ListModelMixin, PassUserMixin):
else self.filter_queryset(self.get_queryset()).all() else self.filter_queryset(self.get_queryset()).all()
) )
if docs.exclude( if docs.exclude(
pk__in=permitted_document_ids( id__in=permitted_document_ids(
request.user, request.user,
perm="delete_document", perm="delete_document",
include_deleted=True, include_deleted=True,
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Afrikaans\n" "Language-Team: Afrikaans\n"
"Language: af_ZA\n" "Language: af_ZA\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumente" msgstr "Dokumente"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Waarde moet geldige JSON wees." msgstr "Waarde moet geldige JSON wees."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Ongeldige gepasmaakte veldnavraaguitdrukking" msgstr "Ongeldige gepasmaakte veldnavraaguitdrukking"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Ongeldige uitdrukking lys. Moet nie leeg wees nie." msgstr "Ongeldige uitdrukking lys. Moet nie leeg wees nie."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Ongeldige logiese uitdrukking {op!r}" msgstr "Ongeldige logiese uitdrukking {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "" msgstr ""
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "" msgstr ""
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "" msgstr ""
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "" msgstr ""
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "" msgstr ""
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Ongeldige veranderlike bespeur."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1912,59 +1912,44 @@ msgstr ""
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "" msgstr ""
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2256,151 +2241,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Engels (VS)" msgstr "Engels (VS)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arabies" msgstr "Arabies"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Belorussies" msgstr "Belorussies"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Katalaans" msgstr "Katalaans"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Tsjeggies" msgstr "Tsjeggies"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Deens" msgstr "Deens"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Duits" msgstr "Duits"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Engels (GB)" msgstr "Engels (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Spaans" msgstr "Spaans"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Fins" msgstr "Fins"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Frans" msgstr "Frans"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italiaans" msgstr "Italiaans"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luxemburgs" msgstr "Luxemburgs"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Nederlands" msgstr "Nederlands"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Pools" msgstr "Pools"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugees (Brasilië)" msgstr "Portugees (Brasilië)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugees" msgstr "Portugees"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Roemeens" msgstr "Roemeens"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Russies" msgstr "Russies"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Sloweens" msgstr "Sloweens"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serwies" msgstr "Serwies"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Sweeds" msgstr "Sweeds"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turks" msgstr "Turks"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Vereenvoudigde Sjinees" msgstr "Vereenvoudigde Sjinees"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Amharic\n" "Language-Team: Amharic\n"
"Language: am_ET\n" "Language: am_ET\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "መዝገባት" msgstr "መዝገባት"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "የሚሰራው እሴት \"JSON\" መሆን አለበት" msgstr "የሚሰራው እሴት \"JSON\" መሆን አለበት"
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "ልክ ያልሆነ የተወሰነ የቦታ መጠይቅ አገላለጽ" msgstr "ልክ ያልሆነ የተወሰነ የቦታ መጠይቅ አገላለጽ"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "ልክ ያልሆነ የመግለጫ ዝርዝር። ባዶ መሆን የለበትም።" msgstr "ልክ ያልሆነ የመግለጫ ዝርዝር። ባዶ መሆን የለበትም።"
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "ልክ ያልሆነ የሎጂክ ኦፕሬተር {op!r}" msgstr "ልክ ያልሆነ የሎጂክ ኦፕሬተር {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "ከፍተኛው የጥያቄ ሁኔታዎች/መጠን ብዛት አልፏል።" msgstr "ከፍተኛው የጥያቄ ሁኔታዎች/መጠን ብዛት አልፏል።"
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} ይሄ ታዐማኒነት ያለው ልማድ አይደለም።" msgstr "{name!r} ይሄ ታዐማኒነት ያለው ልማድ አይደለም።"
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "ጥያቄን አይደግፍም expr {expr!r}." msgstr "ጥያቄን አይደግፍም expr {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "ከፍተኛው የጥገኝነት ጥልቀት አልፏል።" msgstr "ከፍተኛው የጥገኝነት ጥልቀት አልፏል።"
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "ይህ ልማድ አልተገኘም" msgstr "ይህ ልማድ አልተገኘም"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1912,59 +1912,44 @@ msgstr ""
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "" msgstr ""
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2256,151 +2241,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Arabic\n" "Language-Team: Arabic\n"
"Language: ar_SA\n" "Language: ar_SA\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "المستندات" msgstr "المستندات"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "يجب أن تكون القيمة JSON." msgstr "يجب أن تكون القيمة JSON."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "تعبير استعلام غير صالح للحقول المخصصة" msgstr "تعبير استعلام غير صالح للحقول المخصصة"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "قائمة عبارة خاطئة." msgstr "قائمة عبارة خاطئة."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "" msgstr ""
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "تجاوز الحد الأقصى لعدد شروط الاستعلام." msgstr "تجاوز الحد الأقصى لعدد شروط الاستعلام."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} حقل مخصص غير صالح." msgstr "{name!r} حقل مخصص غير صالح."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} لا يدعم تعبير الاستعلام {expr!r}." msgstr "{data_type} لا يدعم تعبير الاستعلام {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "" msgstr ""
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "لم يتم العثور على حقل مخصص" msgstr "لم يتم العثور على حقل مخصص"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "اكتشاف متغير خاطئ."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1913,59 +1913,44 @@ msgstr "أنت على وشك استعمال حسابك في %(provider_name)s ل
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "كخطوة أخيرة، يرجى ملء النموذج التالي:" msgstr "كخطوة أخيرة، يرجى ملء النموذج التالي:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2257,151 +2242,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "إعدادات التطبيق paperless" msgstr "إعدادات التطبيق paperless"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "الإنجليزية (الولايات المتحدة)" msgstr "الإنجليزية (الولايات المتحدة)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "العربية" msgstr "العربية"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "اللغة الأفريقانية" msgstr "اللغة الأفريقانية"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "البيلاروسية" msgstr "البيلاروسية"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "البلغارية" msgstr "البلغارية"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "اللغة الكتالونية" msgstr "اللغة الكتالونية"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "التشيكية" msgstr "التشيكية"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "الدانماركية" msgstr "الدانماركية"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "الألمانية" msgstr "الألمانية"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "اليونانية" msgstr "اليونانية"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "الإنجليزية (المملكة المتحدة)" msgstr "الإنجليزية (المملكة المتحدة)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "الإسبانية" msgstr "الإسبانية"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "الفنلندية" msgstr "الفنلندية"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "الفرنسية" msgstr "الفرنسية"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "المجرية" msgstr "المجرية"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "الإيطالية" msgstr "الإيطالية"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "اليابانية" msgstr "اليابانية"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "اللوكسمبرجية" msgstr "اللوكسمبرجية"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "النرويجية" msgstr "النرويجية"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "الهولندية" msgstr "الهولندية"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "البولندية" msgstr "البولندية"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "البرتغالية (البرازيل)" msgstr "البرتغالية (البرازيل)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "البرتغالية" msgstr "البرتغالية"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "الرومانية" msgstr "الرومانية"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "الروسية" msgstr "الروسية"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "السلوفاكية" msgstr "السلوفاكية"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "السلوفانية" msgstr "السلوفانية"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "الصربية" msgstr "الصربية"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "السويدية" msgstr "السويدية"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "التركية" msgstr "التركية"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "الأوكرانية" msgstr "الأوكرانية"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "الصينية المبسطة" msgstr "الصينية المبسطة"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Belarusian\n" "Language-Team: Belarusian\n"
"Language: be_BY\n" "Language: be_BY\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Дакументы" msgstr "Дакументы"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "" msgstr ""
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "" msgstr ""
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "" msgstr ""
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "" msgstr ""
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "" msgstr ""
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "" msgstr ""
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "" msgstr ""
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "" msgstr ""
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "" msgstr ""
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Выяўлена няправільная зменная."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1912,59 +1912,44 @@ msgstr ""
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "" msgstr ""
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2256,151 +2241,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Англійская (ЗША)" msgstr "Англійская (ЗША)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Беларуская" msgstr "Беларуская"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Чэшская" msgstr "Чэшская"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Дацкая" msgstr "Дацкая"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Нямецкая" msgstr "Нямецкая"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Англійская (Вялікабрытанія)" msgstr "Англійская (Вялікабрытанія)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Іспанская" msgstr "Іспанская"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Французская" msgstr "Французская"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Італьянская" msgstr "Італьянская"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Люксембургская" msgstr "Люксембургская"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Нідэрландская" msgstr "Нідэрландская"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Польская" msgstr "Польская"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Партугальская (Бразілія)" msgstr "Партугальская (Бразілія)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Партугальская" msgstr "Партугальская"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Румынская" msgstr "Румынская"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Руская" msgstr "Руская"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Славенская" msgstr "Славенская"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Сербская" msgstr "Сербская"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Шведская" msgstr "Шведская"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Турэцкая" msgstr "Турэцкая"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Кітайская спрошчаная" msgstr "Кітайская спрошчаная"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Bulgarian\n" "Language-Team: Bulgarian\n"
"Language: bg_BG\n" "Language: bg_BG\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Документи" msgstr "Документи"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Стойността трябва да е валидна JSON." msgstr "Стойността трябва да е валидна JSON."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Невалидна заявка на персонализираното полето" msgstr "Невалидна заявка на персонализираното полето"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Списък с невалиден израз. Не може да е празно." msgstr "Списък с невалиден израз. Не може да е празно."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Невалиден логически оператор {op!r}" msgstr "Невалиден логически оператор {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Надвишен е максимален брой за заявки." msgstr "Надвишен е максимален брой за заявки."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} не е валидно персонализирано поле." msgstr "{name!r} не е валидно персонализирано поле."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} не поддържа заявка expr {expr!r}." msgstr "{data_type} не поддържа заявка expr {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Надвишена е максималната дълбочина на вмъкване." msgstr "Надвишена е максималната дълбочина на вмъкване."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Персонализирано поле не е намерено" msgstr "Персонализирано поле не е намерено"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "стартиране на работните процеси" msgstr "стартиране на работните процеси"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Засечена е невалидна променлива."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1913,59 +1913,44 @@ msgstr "На път сте да използвате профила си в %(pr
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Като последна стъпка, моля, попълнете следната форма:" msgstr "Като последна стъпка, моля, попълнете следната форма:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2257,151 +2242,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "настройки на софтуера" msgstr "настройки на софтуера"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Английски (САЩ)" msgstr "Английски (САЩ)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Арабски" msgstr "Арабски"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Африканс" msgstr "Африканс"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Беларуски" msgstr "Беларуски"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Български" msgstr "Български"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Каталунски" msgstr "Каталунски"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Чешки" msgstr "Чешки"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Датски" msgstr "Датски"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Немски" msgstr "Немски"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Гръцки" msgstr "Гръцки"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Английски (Великобритания)" msgstr "Английски (Великобритания)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Испански" msgstr "Испански"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Финландски" msgstr "Финландски"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Френски" msgstr "Френски"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Унгарски" msgstr "Унгарски"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Италиански" msgstr "Италиански"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Японски" msgstr "Японски"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Корейски" msgstr "Корейски"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Люксембургски" msgstr "Люксембургски"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Норвежки" msgstr "Норвежки"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Холандски" msgstr "Холандски"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Полски" msgstr "Полски"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Португалски (Бразилия)" msgstr "Португалски (Бразилия)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Португалски" msgstr "Португалски"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Румънски" msgstr "Румънски"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Руски" msgstr "Руски"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Словашки" msgstr "Словашки"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Словенски" msgstr "Словенски"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Сръбски" msgstr "Сръбски"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Шведски" msgstr "Шведски"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Турски" msgstr "Турски"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Украински" msgstr "Украински"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Китайски опростен" msgstr "Китайски опростен"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Китайски традиционен" msgstr "Китайски традиционен"
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Catalan\n" "Language-Team: Catalan\n"
"Language: ca_ES\n" "Language: ca_ES\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Documents " msgstr "Documents "
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Valor ha de ser un JSON valid." msgstr "Valor ha de ser un JSON valid."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Expressió de camp de consulta invàlid" msgstr "Expressió de camp de consulta invàlid"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Expressió de llista invàlida. No ha d'estar buida." msgstr "Expressió de llista invàlida. No ha d'estar buida."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Invàlid operand lògic {op!r}" msgstr "Invàlid operand lògic {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Condicions de consulta excedits." msgstr "Condicions de consulta excedits."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} no és un camp personalitzat vàlid." msgstr "{name!r} no és un camp personalitzat vàlid."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} no suporta expressió de consulta {expr!r}." msgstr "{data_type} no suporta expressió de consulta {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Màxima profunditat anidada excedida." msgstr "Màxima profunditat anidada excedida."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Camp personalitzat no trobat" msgstr "Camp personalitzat no trobat"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "flux corrents" msgstr "flux corrents"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Permisos insuficients." msgstr "Permisos insuficients."
@@ -1676,7 +1676,7 @@ msgstr "Variable detectada invàlida."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "Duplicat d'identificadors de documents no permès." msgstr "Duplicat d'identificadors de documents no permès."
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "Documents no trobats: %(ids)s" msgstr "Documents no trobats: %(ids)s"
@@ -1913,59 +1913,44 @@ msgstr "Estàs a punt d'emprar el teu %(provider_name)s compte per a fer login."
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Com a pas final, completa el següent formulari:" msgstr "Com a pas final, completa el següent formulari:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "No es pot analitzar l'URI {value}, falta l'esquema"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "No es pot analitzar l'URI {value}, falta la ubicació de xarxa o la ruta"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "L'esquema d'URI '{parts.scheme}' no està permès. Esquemes permesos: {', '.join(allowed_schemes)}" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "No s'ha pogut analitzar l'URI {value}"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "Invalid more_like_id" msgstr "Invalid more_like_id"
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "Configuració AI invàlida." msgstr "Configuració AI invàlida."
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Especifica només un dels següents valors: text, title_search, query o more_like_id." msgstr "Especifica només un dels següents valors: text, title_search, query o more_like_id."
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Permisos insuficients per compartir document %(id)s." msgstr "Permisos insuficients per compartir document %(id)s."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "Paquet ja s'està processant." msgstr "Paquet ja s'està processant."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "El paquet de link encarà s'està preparant. Prova de nou més tard." msgstr "El paquet de link encarà s'està preparant. Prova de nou més tard."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "El paquet d'enllaç no està disponible." msgstr "El paquet d'enllaç no està disponible."
@@ -2257,151 +2242,151 @@ msgstr "Estableix temps d'espera en segons"
msgid "paperless application settings" msgid "paperless application settings"
msgstr "configuració de l'aplicació paperless" msgstr "configuració de l'aplicació paperless"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Anglès (US)" msgstr "Anglès (US)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Àrab" msgstr "Àrab"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Africà" msgstr "Africà"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Bielorús" msgstr "Bielorús"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Búlgar" msgstr "Búlgar"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Català" msgstr "Català"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Txec" msgstr "Txec"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Danès" msgstr "Danès"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Alemany" msgstr "Alemany"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Grec" msgstr "Grec"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Anglès (GB)" msgstr "Anglès (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Espanyol" msgstr "Espanyol"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Persa" msgstr "Persa"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Finès" msgstr "Finès"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Francès" msgstr "Francès"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Hongarès" msgstr "Hongarès"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indonesi" msgstr "Indonesi"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italià" msgstr "Italià"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japonès" msgstr "Japonès"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Coreà" msgstr "Coreà"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luxemburguès" msgstr "Luxemburguès"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Noruec" msgstr "Noruec"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Holandès" msgstr "Holandès"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polonès" msgstr "Polonès"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portuguès (Brasil)" msgstr "Portuguès (Brasil)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portuguès" msgstr "Portuguès"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Romanès" msgstr "Romanès"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Rus" msgstr "Rus"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Eslovac" msgstr "Eslovac"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Eslovè" msgstr "Eslovè"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbi" msgstr "Serbi"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Suec" msgstr "Suec"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turc" msgstr "Turc"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ucranià" msgstr "Ucranià"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Vietnamita" msgstr "Vietnamita"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Xinès Simplificat" msgstr "Xinès Simplificat"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Xinès tradicional" msgstr "Xinès tradicional"
+61 -77
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Czech\n" "Language-Team: Czech\n"
"Language: cs_CZ\n" "Language: cs_CZ\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumenty" msgstr "Dokumenty"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Hodnota musí být platný JSON." msgstr "Hodnota musí být platný JSON."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Neplatný výraz dotazu na vlastní pole" msgstr "Neplatný výraz dotazu na vlastní pole"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Neplatný seznam výrazů. Nesmí být prázdný." msgstr "Neplatný seznam výrazů. Nesmí být prázdný."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Neplatný logický operátor {op!r}" msgstr "Neplatný logický operátor {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Překročen maximální počet podmínek dotazu." msgstr "Překročen maximální počet podmínek dotazu."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} není platné vlastní pole." msgstr "{name!r} není platné vlastní pole."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} nepodporuje výraz dotazu {expr!r}." msgstr "{data_type} nepodporuje výraz dotazu {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Překročena maximální hloubka větvení." msgstr "Překročena maximální hloubka větvení."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Vlastní pole nebylo nalezeno" msgstr "Vlastní pole nebylo nalezeno"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "spuštění pracovních postupů" msgstr "spuštění pracovních postupů"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Nedostatečná oprávnění." msgstr "Nedostatečná oprávnění."
@@ -1676,7 +1676,7 @@ msgstr "Zjištěna neplatná proměnná."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1913,60 +1913,44 @@ msgstr "Chystáte se použít svůj účet %(provider_name)s k přihlášení."
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Jako poslední krok vyplňte následující formulář:" msgstr "Jako poslední krok vyplňte následující formulář:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "Nelze analyzovat URI {value}, chybí schéma"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "Nelze analyzovat URI {value}, chybí síťové umístění nebo cesta"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "URI schéma '{parts.scheme}' není povoleno. Povolená schémata: {',\n" msgstr ""
"'.join(allowed_schemes)}"
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "Nelze zpracovat URI {value}"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Nedostatečná oprávnění ke sdílení dokumentu %(id)s." msgstr "Nedostatečná oprávnění ke sdílení dokumentu %(id)s."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2258,151 +2242,151 @@ msgstr "Nastaví časový limit LLM v sekundách"
msgid "paperless application settings" msgid "paperless application settings"
msgstr "nastavení aplikace paperless" msgstr "nastavení aplikace paperless"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Angličtina (US)" msgstr "Angličtina (US)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arabština" msgstr "Arabština"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Afrikánština" msgstr "Afrikánština"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Běloruština" msgstr "Běloruština"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Bulharština" msgstr "Bulharština"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Katalánština" msgstr "Katalánština"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Čeština" msgstr "Čeština"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Dánština" msgstr "Dánština"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Němčina" msgstr "Němčina"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Řečtina" msgstr "Řečtina"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Angličtina (GB)" msgstr "Angličtina (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Španělština" msgstr "Španělština"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Perština" msgstr "Perština"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Finština" msgstr "Finština"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Francouzština" msgstr "Francouzština"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Maďarština" msgstr "Maďarština"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indonéština" msgstr "Indonéština"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italština" msgstr "Italština"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japonština" msgstr "Japonština"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Korejština" msgstr "Korejština"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Lucemburština" msgstr "Lucemburština"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norština" msgstr "Norština"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Holandština" msgstr "Holandština"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polština" msgstr "Polština"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugalština (Brazílie)" msgstr "Portugalština (Brazílie)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugalština" msgstr "Portugalština"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Rumunština" msgstr "Rumunština"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Ruština" msgstr "Ruština"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Slovenština" msgstr "Slovenština"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Slovinština" msgstr "Slovinština"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Srbština" msgstr "Srbština"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Švédština" msgstr "Švédština"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turečtina" msgstr "Turečtina"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukrajinština" msgstr "Ukrajinština"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Vietnamština" msgstr "Vietnamština"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Čínština (zjednodušená)" msgstr "Čínština (zjednodušená)"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Čínština (tradiční)" msgstr "Čínština (tradiční)"
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Danish\n" "Language-Team: Danish\n"
"Language: da_DK\n" "Language: da_DK\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumenter" msgstr "Dokumenter"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Værdien skal være gyldig JSON." msgstr "Værdien skal være gyldig JSON."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Ugyldigt tilpasset feltforespørgselsudtryk" msgstr "Ugyldigt tilpasset feltforespørgselsudtryk"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Ugyldig udtryksliste. Må ikke være tom." msgstr "Ugyldig udtryksliste. Må ikke være tom."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Ugyldig logisk operatør {op!r}" msgstr "Ugyldig logisk operatør {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Maksimalt antal forespørgselsbetingelser overskredet." msgstr "Maksimalt antal forespørgselsbetingelser overskredet."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} er ikke et gyldigt tilpasset felt." msgstr "{name!r} er ikke et gyldigt tilpasset felt."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} understøtter ikke forespørgsel expr {expr!r}." msgstr "{data_type} understøtter ikke forespørgsel expr {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Maksimal indlejringsdybde overskredet." msgstr "Maksimal indlejringsdybde overskredet."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Tilpasset felt ikke fundet" msgstr "Tilpasset felt ikke fundet"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "workflow-kørsler" msgstr "workflow-kørsler"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Ugyldig variabel fundet."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1913,59 +1913,44 @@ msgstr "En %(provider_name)s-konto er ved at blive brugt til indlogning."
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Som sidste trin, udfyld venligst flg. formular:" msgstr "Som sidste trin, udfyld venligst flg. formular:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2257,151 +2242,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "paperless-applikationsindstillinger" msgstr "paperless-applikationsindstillinger"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Engelsk (USA)" msgstr "Engelsk (USA)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Afrikaans" msgstr "Afrikaans"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Hviderussisk" msgstr "Hviderussisk"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Bulgarsk" msgstr "Bulgarsk"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Katalansk" msgstr "Katalansk"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Tjekkisk" msgstr "Tjekkisk"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Dansk" msgstr "Dansk"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Tysk" msgstr "Tysk"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Græsk" msgstr "Græsk"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Engelsk (GB)" msgstr "Engelsk (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Spansk" msgstr "Spansk"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Finsk" msgstr "Finsk"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Fransk" msgstr "Fransk"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Ungarsk" msgstr "Ungarsk"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italiensk" msgstr "Italiensk"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japansk" msgstr "Japansk"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Koreansk" msgstr "Koreansk"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luxemburgsk" msgstr "Luxemburgsk"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norsk" msgstr "Norsk"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Hollandsk" msgstr "Hollandsk"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polsk" msgstr "Polsk"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugisisk (Brasilien)" msgstr "Portugisisk (Brasilien)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugisisk" msgstr "Portugisisk"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Romansk" msgstr "Romansk"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Russisk" msgstr "Russisk"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Slovakisk" msgstr "Slovakisk"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Slovensk" msgstr "Slovensk"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbisk" msgstr "Serbisk"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Svensk" msgstr "Svensk"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Tyrkisk" msgstr "Tyrkisk"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukrainsk" msgstr "Ukrainsk"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Kinesisk, forenklet" msgstr "Kinesisk, forenklet"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Kinesisk, traditionelt" msgstr "Kinesisk, traditionelt"
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: German, Switzerland\n" "Language-Team: German, Switzerland\n"
"Language: de_CH\n" "Language: de_CH\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumente" msgstr "Dokumente"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Wert muss gültiges JSON sein." msgstr "Wert muss gültiges JSON sein."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Ungültiger benutzerdefinierter Feldabfrageausdruck" msgstr "Ungültiger benutzerdefinierter Feldabfrageausdruck"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Ungültige Ausdrucksliste. Darf nicht leer sein." msgstr "Ungültige Ausdrucksliste. Darf nicht leer sein."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Ungültiger logischer Operator {op!r}" msgstr "Ungültiger logischer Operator {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Maximale Anzahl an Abfragebedingungen überschritten." msgstr "Maximale Anzahl an Abfragebedingungen überschritten."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} ist kein gültiges Zusatzfeld." msgstr "{name!r} ist kein gültiges Zusatzfeld."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} unterstützt den Abfrageausdruck {expr!r} nicht." msgstr "{data_type} unterstützt den Abfrageausdruck {expr!r} nicht."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Maximale Verschachtelungstiefe überschritten." msgstr "Maximale Verschachtelungstiefe überschritten."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Benutzerdefiniertes Feld nicht gefunden" msgstr "Benutzerdefiniertes Feld nicht gefunden"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "Arbeitsablauf wird ausgeführt" msgstr "Arbeitsablauf wird ausgeführt"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Unzureichende Berechtigungen." msgstr "Unzureichende Berechtigungen."
@@ -1676,7 +1676,7 @@ msgstr "Ungültige Variable erkannt."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "Doppelte Dokumentbezeichner sind nicht erlaubt." msgstr "Doppelte Dokumentbezeichner sind nicht erlaubt."
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "Dokumente nicht gefunden: %(ids)s" msgstr "Dokumente nicht gefunden: %(ids)s"
@@ -1913,59 +1913,44 @@ msgstr "Sie sind dabei, Ihr %(provider_name)s-Konto zur Anmeldung zu verwenden."
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Bitte füllen Sie zum Abschluss folgendes Formular aus:" msgstr "Bitte füllen Sie zum Abschluss folgendes Formular aus:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "URI {value} kann nicht gelesen werden, fehlendes Schema"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "URI {value} kann nicht gelesen werden, Netzadresse oder Pfad fehlt"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "URI-Schema „{parts.scheme}“ ist nicht erlaubt. Erlaubte Schemata: {', '.join(allowed_schemes)}" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "URI {value} kann nicht gelesen werden"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "Ungültige more_like_id" msgstr "Ungültige more_like_id"
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "Ungültige KI-Konfiguration." msgstr "Ungültige KI-Konfiguration."
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Geben Sie nur einen von text, title_search, query, oder more_like_id an." msgstr "Geben Sie nur einen von text, title_search, query, oder more_like_id an."
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Unzureichende Berechtigungen, um Dokument %(id)s zu teilen." msgstr "Unzureichende Berechtigungen, um Dokument %(id)s zu teilen."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "Paket wird bereits verarbeitet." msgstr "Paket wird bereits verarbeitet."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Das Freigabelink-Paket wird noch vorbereitet. Bitte versuchen Sie es später erneut." msgstr "Das Freigabelink-Paket wird noch vorbereitet. Bitte versuchen Sie es später erneut."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "Das Freigabelink-Paket ist nicht verfügbar." msgstr "Das Freigabelink-Paket ist nicht verfügbar."
@@ -2257,151 +2242,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "Paperless-ngx-Anwendungseinstellungen" msgstr "Paperless-ngx-Anwendungseinstellungen"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Englisch (US)" msgstr "Englisch (US)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arabisch" msgstr "Arabisch"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Afrikanisch" msgstr "Afrikanisch"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Belarussisch" msgstr "Belarussisch"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Bulgarisch" msgstr "Bulgarisch"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Katalanisch" msgstr "Katalanisch"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Tschechisch" msgstr "Tschechisch"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Dänisch" msgstr "Dänisch"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Griechisch" msgstr "Griechisch"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Englisch (UK)" msgstr "Englisch (UK)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Spanisch" msgstr "Spanisch"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Persisch" msgstr "Persisch"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Finnisch" msgstr "Finnisch"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Französisch" msgstr "Französisch"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Ungarisch" msgstr "Ungarisch"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indonesisch" msgstr "Indonesisch"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italienisch" msgstr "Italienisch"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japanisch" msgstr "Japanisch"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Koreanisch" msgstr "Koreanisch"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luxemburgisch" msgstr "Luxemburgisch"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norwegisch" msgstr "Norwegisch"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Niederländisch" msgstr "Niederländisch"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polnisch" msgstr "Polnisch"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugiesisch (Brasilien)" msgstr "Portugiesisch (Brasilien)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugiesisch" msgstr "Portugiesisch"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Rumänisch" msgstr "Rumänisch"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Russisch" msgstr "Russisch"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Slowakisch" msgstr "Slowakisch"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Slowenisch" msgstr "Slowenisch"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbisch" msgstr "Serbisch"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Schwedisch" msgstr "Schwedisch"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Türkisch" msgstr "Türkisch"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukrainisch" msgstr "Ukrainisch"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Vietnamesisch" msgstr "Vietnamesisch"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Chinesisch (vereinfacht)" msgstr "Chinesisch (vereinfacht)"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Chinesisch (traditionell)" msgstr "Chinesisch (traditionell)"
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-06 00:56\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: German\n" "Language-Team: German\n"
"Language: de_DE\n" "Language: de_DE\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumente" msgstr "Dokumente"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Wert muss gültiges JSON sein." msgstr "Wert muss gültiges JSON sein."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Ungültiger Zusatzfeld-Abfrageausdruck" msgstr "Ungültiger Zusatzfeld-Abfrageausdruck"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Ungültige Ausdrucksliste. Darf nicht leer sein." msgstr "Ungültige Ausdrucksliste. Darf nicht leer sein."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Ungültiger logischer Operator {op!r}" msgstr "Ungültiger logischer Operator {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Maximale Anzahl an Abfragebedingungen überschritten." msgstr "Maximale Anzahl an Abfragebedingungen überschritten."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} ist kein gültiges Zusatzfeld." msgstr "{name!r} ist kein gültiges Zusatzfeld."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} unterstützt den Abfrageausdruck {expr!r} nicht." msgstr "{data_type} unterstützt den Abfrageausdruck {expr!r} nicht."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Maximale Verschachtelungstiefe überschritten." msgstr "Maximale Verschachtelungstiefe überschritten."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Zusatzfeld nicht gefunden" msgstr "Zusatzfeld nicht gefunden"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "Arbeitsablauf wird ausgeführt" msgstr "Arbeitsablauf wird ausgeführt"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Unzureichende Berechtigungen." msgstr "Unzureichende Berechtigungen."
@@ -1676,7 +1676,7 @@ msgstr "Ungültige Variable erkannt."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "Doppelte Dokumentbezeichner sind nicht erlaubt." msgstr "Doppelte Dokumentbezeichner sind nicht erlaubt."
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "Dokumente nicht gefunden: %(ids)s" msgstr "Dokumente nicht gefunden: %(ids)s"
@@ -1913,59 +1913,44 @@ msgstr "Sie sind dabei, Ihr %(provider_name)s-Konto zur Anmeldung zu verwenden."
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Bitte füllen Sie zum Abschluss folgendes Formular aus:" msgstr "Bitte füllen Sie zum Abschluss folgendes Formular aus:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "URI {value} kann nicht gelesen werden, fehlendes Schema"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "URI {value} kann nicht gelesen werden, Netzadresse oder Pfad fehlt"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "URI-Schema „{parts.scheme}“ ist nicht erlaubt. Erlaubte Schemata: {', '.join(allowed_schemes)}" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "URI {value} kann nicht gelesen werden"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "Ungültige more_like_id" msgstr "Ungültige more_like_id"
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "Ungültige KI-Konfiguration." msgstr "Ungültige KI-Konfiguration."
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "Zeitüberschreitung bei der KI-Backendanfrage." msgstr "Zeitüberschreitung bei der KI-Backendanfrage."
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "Das KI-Backend hat die Anfrage abgelehnt. Details in den Protokollen." msgstr "Das KI-Backend hat die Anfrage abgelehnt. Details in den Protokollen."
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Geben Sie nur einen von text, title_search, query, oder more_like_id an." msgstr "Geben Sie nur einen von text, title_search, query, oder more_like_id an."
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Unzureichende Berechtigungen, um Dokument %(id)s zu teilen." msgstr "Unzureichende Berechtigungen, um Dokument %(id)s zu teilen."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "Paket wird bereits verarbeitet." msgstr "Paket wird bereits verarbeitet."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Das Freigabelink-Paket wird noch vorbereitet. Bitte versuchen Sie es später erneut." msgstr "Das Freigabelink-Paket wird noch vorbereitet. Bitte versuchen Sie es später erneut."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "Das Freigabelink-Paket ist nicht verfügbar." msgstr "Das Freigabelink-Paket ist nicht verfügbar."
@@ -2257,151 +2242,151 @@ msgstr "Legt das LLM-Zeitlimit in Sekunden fest"
msgid "paperless application settings" msgid "paperless application settings"
msgstr "Paperless-ngx-Anwendungseinstellungen" msgstr "Paperless-ngx-Anwendungseinstellungen"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Englisch (US)" msgstr "Englisch (US)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arabisch" msgstr "Arabisch"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Afrikanisch" msgstr "Afrikanisch"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Belarussisch" msgstr "Belarussisch"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Bulgarisch" msgstr "Bulgarisch"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Katalanisch" msgstr "Katalanisch"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Tschechisch" msgstr "Tschechisch"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Dänisch" msgstr "Dänisch"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Deutsch" msgstr "Deutsch"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Griechisch" msgstr "Griechisch"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Englisch (UK)" msgstr "Englisch (UK)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Spanisch" msgstr "Spanisch"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Persisch" msgstr "Persisch"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Finnisch" msgstr "Finnisch"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Französisch" msgstr "Französisch"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Ungarisch" msgstr "Ungarisch"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indonesisch" msgstr "Indonesisch"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italienisch" msgstr "Italienisch"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japanisch" msgstr "Japanisch"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Koreanisch" msgstr "Koreanisch"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luxemburgisch" msgstr "Luxemburgisch"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norwegisch" msgstr "Norwegisch"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Niederländisch" msgstr "Niederländisch"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polnisch" msgstr "Polnisch"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugiesisch (Brasilien)" msgstr "Portugiesisch (Brasilien)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugiesisch" msgstr "Portugiesisch"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Rumänisch" msgstr "Rumänisch"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Russisch" msgstr "Russisch"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Slowakisch" msgstr "Slowakisch"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Slowenisch" msgstr "Slowenisch"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbisch" msgstr "Serbisch"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Schwedisch" msgstr "Schwedisch"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Türkisch" msgstr "Türkisch"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukrainisch" msgstr "Ukrainisch"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Vietnamesisch" msgstr "Vietnamesisch"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Chinesisch (vereinfacht)" msgstr "Chinesisch (vereinfacht)"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Chinesisch (traditionell)" msgstr "Chinesisch (traditionell)"
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Greek\n" "Language-Team: Greek\n"
"Language: el_GR\n" "Language: el_GR\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Έγγραφα" msgstr "Έγγραφα"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Η τιμή πρέπει να είναι σε έγκυρη μορφή JSON." msgstr "Η τιμή πρέπει να είναι σε έγκυρη μορφή JSON."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Μη έγκυρη έκφραση προσαρμοσμένου ερωτήματος πεδίου" msgstr "Μη έγκυρη έκφραση προσαρμοσμένου ερωτήματος πεδίου"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Μη έγκυρη λίστα έκφρασης. Πρέπει να είναι μη κενή." msgstr "Μη έγκυρη λίστα έκφρασης. Πρέπει να είναι μη κενή."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Μη έγκυρος λογικός τελεστής {op!r}" msgstr "Μη έγκυρος λογικός τελεστής {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Υπέρβαση μέγιστου αριθμού συνθηκών ερωτήματος." msgstr "Υπέρβαση μέγιστου αριθμού συνθηκών ερωτήματος."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "Το προσαρμοσμένο πεδίο {name!r} δεν είναι ένα έγκυρο." msgstr "Το προσαρμοσμένο πεδίο {name!r} δεν είναι ένα έγκυρο."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "Το {data_type} δεν υποστηρίζει το ερώτημα expr {expr!r}s." msgstr "Το {data_type} δεν υποστηρίζει το ερώτημα expr {expr!r}s."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Υπέρβαση μέγιστου βάθους εμφώλευσης." msgstr "Υπέρβαση μέγιστου βάθους εμφώλευσης."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Το προσαρμοσμένο πεδίο δε βρέθηκε" msgstr "Το προσαρμοσμένο πεδίο δε βρέθηκε"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "εκτελέσεις ροής εργασίας" msgstr "εκτελέσεις ροής εργασίας"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Εντοπίστηκε μη έγκυρη μεταβλητή."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1913,59 +1913,44 @@ msgstr "Πρόκειται να χρησιμοποιήσετε το λογαρι
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Ως τελικό βήμα, παρακαλούμε να συμπληρώσετε την ακόλουθη φόρμα:" msgstr "Ως τελικό βήμα, παρακαλούμε να συμπληρώσετε την ακόλουθη φόρμα:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2257,151 +2242,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "ρυθμίσεις εφαρμογής paperless" msgstr "ρυθμίσεις εφαρμογής paperless"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Αγγλικά (ΗΠΑ)" msgstr "Αγγλικά (ΗΠΑ)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Αραβικά" msgstr "Αραβικά"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Αφρικανικά" msgstr "Αφρικανικά"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Λευκορωσικά" msgstr "Λευκορωσικά"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Βουλγαρικά" msgstr "Βουλγαρικά"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Καταλανικά" msgstr "Καταλανικά"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Τσέχικα" msgstr "Τσέχικα"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Δανέζικα" msgstr "Δανέζικα"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Γερμανικά" msgstr "Γερμανικά"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Ελληνικά" msgstr "Ελληνικά"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Αγγλικά (Ηνωμένο Βασίλειο)" msgstr "Αγγλικά (Ηνωμένο Βασίλειο)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Ισπανικά" msgstr "Ισπανικά"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Φινλανδικά" msgstr "Φινλανδικά"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Γαλλικά" msgstr "Γαλλικά"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Ουγγρικά" msgstr "Ουγγρικά"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Ιταλικά" msgstr "Ιταλικά"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Ιαπωνικά" msgstr "Ιαπωνικά"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Κορεάτικα" msgstr "Κορεάτικα"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Λουξεμβουργικά" msgstr "Λουξεμβουργικά"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Νορβηγικά" msgstr "Νορβηγικά"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Ολλανδικά" msgstr "Ολλανδικά"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Πολωνικά" msgstr "Πολωνικά"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Πορτογαλικά (Βραζιλίας)" msgstr "Πορτογαλικά (Βραζιλίας)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Πορτογαλικά" msgstr "Πορτογαλικά"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Ρουμάνικα" msgstr "Ρουμάνικα"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Ρωσικά" msgstr "Ρωσικά"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Σλοβακικά" msgstr "Σλοβακικά"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Σλοβενικά" msgstr "Σλοβενικά"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Σερβικά" msgstr "Σερβικά"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Σουηδικά" msgstr "Σουηδικά"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Τούρκικα" msgstr "Τούρκικα"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ουκρανικά" msgstr "Ουκρανικά"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Κινέζικα Απλοποιημένα" msgstr "Κινέζικα Απλοποιημένα"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Κινεζικά Παραδοσιακά" msgstr "Κινεζικά Παραδοσιακά"
+58 -58
View File
@@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-06 15:12+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2022-02-17 04:17\n" "PO-Revision-Date: 2022-02-17 04:17\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: English\n" "Language-Team: English\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "" msgstr ""
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "" msgstr ""
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "" msgstr ""
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "" msgstr ""
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "" msgstr ""
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "" msgstr ""
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "" msgstr ""
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "" msgstr ""
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "" msgstr ""
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "" msgstr ""
@@ -1653,7 +1653,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1694,7 +1694,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1945,40 +1945,40 @@ msgstr ""
msgid ", " msgid ", "
msgstr "" msgstr ""
#: documents/views.py:336 documents/views.py:2729 #: documents/views.py:337 documents/views.py:2749
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2270,151 +2270,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Spanish\n" "Language-Team: Spanish\n"
"Language: es_ES\n" "Language: es_ES\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Documentos" msgstr "Documentos"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "El valor debe ser un JSON válido." msgstr "El valor debe ser un JSON válido."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Expresión de consulta de campo personalizado no válida" msgstr "Expresión de consulta de campo personalizado no válida"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Lista de expresiones no válida. No debe estar vacía." msgstr "Lista de expresiones no válida. No debe estar vacía."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Operador lógico inválido {op!r}" msgstr "Operador lógico inválido {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Se ha superado el número máximo de condiciones de consulta." msgstr "Se ha superado el número máximo de condiciones de consulta."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{nombre!r} no es un campo personalizado válido." msgstr "{nombre!r} no es un campo personalizado válido."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} no admite la consulta expr {expr!r}." msgstr "{data_type} no admite la consulta expr {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Profundidad máxima de nidificación superada." msgstr "Profundidad máxima de nidificación superada."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Campo personalizado no encontrado" msgstr "Campo personalizado no encontrado"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "ejecuciones de flujo de trabajo" msgstr "ejecuciones de flujo de trabajo"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Permisos insuficientes." msgstr "Permisos insuficientes."
@@ -1676,7 +1676,7 @@ msgstr "Variable inválida."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "No se permiten identificadores de documento duplicados." msgstr "No se permiten identificadores de documento duplicados."
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "Documentos no encontrados: %(ids)s" msgstr "Documentos no encontrados: %(ids)s"
@@ -1913,59 +1913,44 @@ msgstr "Estás a punto de usar tu cuenta de %(provider_name)s para iniciar sesi
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Como último paso, por favor complete el siguiente formulario:" msgstr "Como último paso, por favor complete el siguiente formulario:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "No se puede analizar la URI {value}, falta el esquema"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "No se puede analizar la URI {value}, falta la ubicación de la red o la ruta"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "El esquema URI '{parts.scheme}' no está permitido. Esquemas permitidos: {', '.join(allowed_schemes)}" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "No se puede analizar la URI {value}"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "Configuración de IA inválida." msgstr "Configuración de IA inválida."
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Especifique solo uno entre text, title_search, query, o more_like_id." msgstr "Especifique solo uno entre text, title_search, query, o more_like_id."
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Permisos insuficientes para compartir el documento %(id)s." msgstr "Permisos insuficientes para compartir el documento %(id)s."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "El paquete ya está siendo procesado." msgstr "El paquete ya está siendo procesado."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "El paquete de enlace compartido aún está siendo preparado. Por favor, inténtalo de nuevo más tarde." msgstr "El paquete de enlace compartido aún está siendo preparado. Por favor, inténtalo de nuevo más tarde."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "El paquete de enlace compartido no está disponible." msgstr "El paquete de enlace compartido no está disponible."
@@ -2257,151 +2242,151 @@ msgstr "Establece el tiempo de espera de LLM en segundos"
msgid "paperless application settings" msgid "paperless application settings"
msgstr "Ajustes de la aplicación paperless" msgstr "Ajustes de la aplicación paperless"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Inglés (US)" msgstr "Inglés (US)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Árabe" msgstr "Árabe"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Africano" msgstr "Africano"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Bielorruso" msgstr "Bielorruso"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Búlgaro" msgstr "Búlgaro"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Catalán" msgstr "Catalán"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Checo" msgstr "Checo"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Danés" msgstr "Danés"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Alemán" msgstr "Alemán"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Griego" msgstr "Griego"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Inglés (Gran Bretaña)" msgstr "Inglés (Gran Bretaña)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Español" msgstr "Español"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Persa" msgstr "Persa"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Finlandés" msgstr "Finlandés"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Francés" msgstr "Francés"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Húngaro" msgstr "Húngaro"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indonesio" msgstr "Indonesio"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italiano" msgstr "Italiano"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japonés" msgstr "Japonés"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Coreano" msgstr "Coreano"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luxemburgués" msgstr "Luxemburgués"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Noruego" msgstr "Noruego"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Alemán" msgstr "Alemán"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polaco" msgstr "Polaco"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugués (Brasil)" msgstr "Portugués (Brasil)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugués" msgstr "Portugués"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Rumano" msgstr "Rumano"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Ruso" msgstr "Ruso"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Eslovaco" msgstr "Eslovaco"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Esloveno" msgstr "Esloveno"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbio" msgstr "Serbio"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Sueco" msgstr "Sueco"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turco" msgstr "Turco"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ucraniano" msgstr "Ucraniano"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Vietnamita" msgstr "Vietnamita"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Chino simplificado" msgstr "Chino simplificado"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Chino tradicional" msgstr "Chino tradicional"
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Estonian\n" "Language-Team: Estonian\n"
"Language: et_EE\n" "Language: et_EE\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumendid" msgstr "Dokumendid"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Väärtus peab olema lubatav JSON." msgstr "Väärtus peab olema lubatav JSON."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Vigane kohandatud välja päringu avaldis" msgstr "Vigane kohandatud välja päringu avaldis"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Vigane avaldiste loend. Peab olema mittetühi." msgstr "Vigane avaldiste loend. Peab olema mittetühi."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Vigane loogikaoperaator {op!r}" msgstr "Vigane loogikaoperaator {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Päringutingimuste suurim hulk on ületatud." msgstr "Päringutingimuste suurim hulk on ületatud."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} ei ole lubatud kohandatud väli." msgstr "{name!r} ei ole lubatud kohandatud väli."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} ei toeta päringu avaldist {expr!r}." msgstr "{data_type} ei toeta päringu avaldist {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Suurim pesastamis sügavus ületatud." msgstr "Suurim pesastamis sügavus ületatud."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Kohandatud välja ei leitud" msgstr "Kohandatud välja ei leitud"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1912,59 +1912,44 @@ msgstr ""
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "" msgstr ""
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2256,151 +2241,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Persian\n" "Language-Team: Persian\n"
"Language: fa_IR\n" "Language: fa_IR\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "اسناد و مدارک" msgstr "اسناد و مدارک"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "مقدار باید JSON معتبر باشد." msgstr "مقدار باید JSON معتبر باشد."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Invalid custom field query expression" msgstr "Invalid custom field query expression"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "لیست عبارت‌ها نامعتبر است. نباید خالی باشد." msgstr "لیست عبارت‌ها نامعتبر است. نباید خالی باشد."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "" msgstr ""
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "حداکثر تعداد شرایط پرس و جو از آن فراتر رفته است." msgstr "حداکثر تعداد شرایط پرس و جو از آن فراتر رفته است."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{نام! R} یک زمینه سفارشی معتبر نیست." msgstr "{نام! R} یک زمینه سفارشی معتبر نیست."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "" msgstr ""
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "حداکثر عمق تودرتویی بیش از حد مجاز است." msgstr "حداکثر عمق تودرتویی بیش از حد مجاز است."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "زمینه سفارشی یافت نشد" msgstr "زمینه سفارشی یافت نشد"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "گردش کار اجرا می شود" msgstr "گردش کار اجرا می شود"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "متغیر نامعتبر شناسایی شده است."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1912,59 +1912,44 @@ msgstr ""
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "به عنوان مرحله آخر ، لطفاً فرم زیر را تکمیل کنید:" msgstr "به عنوان مرحله آخر ، لطفاً فرم زیر را تکمیل کنید:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2256,151 +2241,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "تنظیمات برنامه بدون کاغذ" msgstr "تنظیمات برنامه بدون کاغذ"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "انگلیسی (ایالات متحده)" msgstr "انگلیسی (ایالات متحده)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "عربی" msgstr "عربی"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "افریکان" msgstr "افریکان"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "وابسته به بلروس" msgstr "وابسته به بلروس"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "وابسته به بلغاری" msgstr "وابسته به بلغاری"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "کاتالان" msgstr "کاتالان"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "وابسته به چک" msgstr "وابسته به چک"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "وابسته به دانمارکی" msgstr "وابسته به دانمارکی"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "آلمانی" msgstr "آلمانی"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "یونانی" msgstr "یونانی"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "انگلیسی (GB)" msgstr "انگلیسی (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "اسپانیایی" msgstr "اسپانیایی"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "فارسی" msgstr "فارسی"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "فنلاندی" msgstr "فنلاندی"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "فرانسوی" msgstr "فرانسوی"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "مجارستانی" msgstr "مجارستانی"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "ایتالیایی" msgstr "ایتالیایی"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "وابسته به ژاپنی" msgstr "وابسته به ژاپنی"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "کره ای" msgstr "کره ای"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "وابسته به لوکس" msgstr "وابسته به لوکس"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "نروژی" msgstr "نروژی"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "هلندی" msgstr "هلندی"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "صیقل دادن" msgstr "صیقل دادن"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "پرتغالی (برزیل)" msgstr "پرتغالی (برزیل)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "وابسته به پرتغالی" msgstr "وابسته به پرتغالی"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "وابسته به رومانی" msgstr "وابسته به رومانی"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "روسی" msgstr "روسی"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "لگن" msgstr "لگن"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "وابسته به اسلوونی" msgstr "وابسته به اسلوونی"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "صرب" msgstr "صرب"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "سوئدی" msgstr "سوئدی"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "ترکی" msgstr "ترکی"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "وابسته به اوکراینی" msgstr "وابسته به اوکراینی"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "چینی ساده شد" msgstr "چینی ساده شد"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "چینی سنتی" msgstr "چینی سنتی"
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Finnish\n" "Language-Team: Finnish\n"
"Language: fi_FI\n" "Language: fi_FI\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Asiakirjat" msgstr "Asiakirjat"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Arvon on oltava kelvollista JSON:ia." msgstr "Arvon on oltava kelvollista JSON:ia."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "" msgstr ""
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "" msgstr ""
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "" msgstr ""
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "" msgstr ""
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "" msgstr ""
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "" msgstr ""
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "" msgstr ""
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "" msgstr ""
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Virheellinen muuttuja havaittu."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1913,59 +1913,44 @@ msgstr ""
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "" msgstr ""
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2257,151 +2242,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Englanti (US)" msgstr "Englanti (US)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arabialainen" msgstr "Arabialainen"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "valkovenäjä" msgstr "valkovenäjä"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Katalaani" msgstr "Katalaani"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Tšekki" msgstr "Tšekki"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Tanska" msgstr "Tanska"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Saksa" msgstr "Saksa"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Kreikka" msgstr "Kreikka"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Englanti (US)" msgstr "Englanti (US)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Espanja" msgstr "Espanja"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Suomi" msgstr "Suomi"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Ranska" msgstr "Ranska"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italia" msgstr "Italia"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luxemburg" msgstr "Luxemburg"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norja" msgstr "Norja"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Hollanti" msgstr "Hollanti"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "puola" msgstr "puola"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "portugali (Brasilia)" msgstr "portugali (Brasilia)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "portugali" msgstr "portugali"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "romania" msgstr "romania"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "venäjä" msgstr "venäjä"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Slovakia" msgstr "Slovakia"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Slovenia" msgstr "Slovenia"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbia" msgstr "Serbia"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "ruotsi" msgstr "ruotsi"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turkki" msgstr "Turkki"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukraina" msgstr "Ukraina"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Kiina (yksinkertaistettu)" msgstr "Kiina (yksinkertaistettu)"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: French\n" "Language-Team: French\n"
"Language: fr_FR\n" "Language: fr_FR\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Documents" msgstr "Documents"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "La valeur doit être un JSON valide." msgstr "La valeur doit être un JSON valide."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Requête de champ personnalisé invalide" msgstr "Requête de champ personnalisé invalide"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Liste d'expressions invalide. Doit être non vide." msgstr "Liste d'expressions invalide. Doit être non vide."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Opérateur logique {op!r} invalide" msgstr "Opérateur logique {op!r} invalide"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Nombre maximum de conditions dans la requête dépassé." msgstr "Nombre maximum de conditions dans la requête dépassé."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} n'est pas un champ personnalisé valide." msgstr "{name!r} n'est pas un champ personnalisé valide."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} ne supporte pas l'expression {expr!r}." msgstr "{data_type} ne supporte pas l'expression {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Profondeur de récursion maximale dépassée." msgstr "Profondeur de récursion maximale dépassée."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Champ personnalisé non trouvé" msgstr "Champ personnalisé non trouvé"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "le flux de travail s'exécute" msgstr "le flux de travail s'exécute"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Droits insuffisants." msgstr "Droits insuffisants."
@@ -1676,7 +1676,7 @@ msgstr "Variable invalide détectée."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "Les identificateurs de document en double ne sont pas autorisés." msgstr "Les identificateurs de document en double ne sont pas autorisés."
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "Documents introuvables : %(ids)s" msgstr "Documents introuvables : %(ids)s"
@@ -1912,59 +1912,44 @@ msgstr "Vous vous apprêtez à utiliser votre compte %(provider_name)s pour vous
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Pour finir, veuillez remplir le formulaire suivant :" msgstr "Pour finir, veuillez remplir le formulaire suivant :"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "Impossible d'analyser l'URI {value}, schéma manquant"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "Impossible d'analyser l'URI {value}, emplacement réseau ou chemin manquant"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "Le schéma d'URI « {parts.scheme} » n'est pas autorisé. Schémas autorisés : {', '.join(allowed_schemes)}" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "Impossible d'analyser l'URI {value}"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "More_like_id invalide" msgstr "More_like_id invalide"
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "Configuration IA invalide." msgstr "Configuration IA invalide."
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "La requête d'arrière-plan IA a expiré." msgstr "La requête d'arrière-plan IA a expiré."
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Spécifiez seulement un texte, titre, recherche ou more_like_id." msgstr "Spécifiez seulement un texte, titre, recherche ou more_like_id."
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Droits d'accès insuffisant pour partager %(id)s document." msgstr "Droits d'accès insuffisant pour partager %(id)s document."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "Le paquet est déjà en cours de traitement." msgstr "Le paquet est déjà en cours de traitement."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Le lot de liens de partage est en cours de préparation. Veuillez réessayer plus tard." msgstr "Le lot de liens de partage est en cours de préparation. Veuillez réessayer plus tard."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "Le lot de liens de partage n'est pas disponible." msgstr "Le lot de liens de partage n'est pas disponible."
@@ -2256,151 +2241,151 @@ msgstr "Définit le délai d'attente du LLM en secondes"
msgid "paperless application settings" msgid "paperless application settings"
msgstr "paramètres de l'application paperless" msgstr "paramètres de l'application paperless"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Anglais (US)" msgstr "Anglais (US)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arabe" msgstr "Arabe"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Afrikaans" msgstr "Afrikaans"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Biélorusse" msgstr "Biélorusse"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Bulgare" msgstr "Bulgare"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Catalan" msgstr "Catalan"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Tchèque" msgstr "Tchèque"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Danois" msgstr "Danois"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Allemand" msgstr "Allemand"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Grec" msgstr "Grec"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Anglais (GB)" msgstr "Anglais (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Espagnol" msgstr "Espagnol"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Persan" msgstr "Persan"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Finnois" msgstr "Finnois"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Français" msgstr "Français"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Hongrois" msgstr "Hongrois"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indonésien" msgstr "Indonésien"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italien" msgstr "Italien"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japonais" msgstr "Japonais"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Coréen" msgstr "Coréen"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luxembourgeois" msgstr "Luxembourgeois"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norvégien" msgstr "Norvégien"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Néerlandais" msgstr "Néerlandais"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polonais" msgstr "Polonais"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugais (Brésil)" msgstr "Portugais (Brésil)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugais" msgstr "Portugais"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Roumain" msgstr "Roumain"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Russe" msgstr "Russe"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Solvaque" msgstr "Solvaque"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Slovène" msgstr "Slovène"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbe" msgstr "Serbe"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Suédois" msgstr "Suédois"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turc" msgstr "Turc"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukrainien" msgstr "Ukrainien"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Vietnamien" msgstr "Vietnamien"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Chinois simplifié" msgstr "Chinois simplifié"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Chinois traditionnel" msgstr "Chinois traditionnel"
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Hebrew\n" "Language-Team: Hebrew\n"
"Language: he_IL\n" "Language: he_IL\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "מסמכים" msgstr "מסמכים"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "ערך חייב להיות JSON תקין." msgstr "ערך חייב להיות JSON תקין."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "ביטוי שאילתה לא חוקי של שדה מותאם אישית" msgstr "ביטוי שאילתה לא חוקי של שדה מותאם אישית"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "רשימת ביטויים לא חוקית. חייב לכלול ערך." msgstr "רשימת ביטויים לא חוקית. חייב לכלול ערך."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "סימן פעולה לוגית לא חוקי {op!r}" msgstr "סימן פעולה לוגית לא חוקי {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "חריגה ממספר תנאי השאילתה המרבי." msgstr "חריגה ממספר תנאי השאילתה המרבי."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} הוא לא שדה מותאם אישית חוקי." msgstr "{name!r} הוא לא שדה מותאם אישית חוקי."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} לא תומך בביטוי שאילתה {expr!r}." msgstr "{data_type} לא תומך בביטוי שאילתה {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "חריגה מעומק הקינון המרבי." msgstr "חריגה מעומק הקינון המרבי."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "שדה מותאם אישית לא נמצא" msgstr "שדה מותאם אישית לא נמצא"
@@ -1637,7 +1637,7 @@ msgid "workflow runs"
msgstr "הרצות זרימת עבודה" msgstr "הרצות זרימת עבודה"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "הרשאות אינן מספיקות." msgstr "הרשאות אינן מספיקות."
@@ -1677,7 +1677,7 @@ msgstr "משתנה לא חוקי זוהה."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "מזהי מסמכים כפולים אינם מורשים." msgstr "מזהי מסמכים כפולים אינם מורשים."
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "מסמכים לא נמצאו: %(ids)s" msgstr "מסמכים לא נמצאו: %(ids)s"
@@ -1914,59 +1914,44 @@ msgstr "שלך כדי להתחבר %(provider_name)s אתה עומד להשתמ
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr ": כצעד אחרון, אנא מלא את הטופס הבא:" msgstr ": כצעד אחרון, אנא מלא את הטופס הבא:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "לא ניתן לפענח את ה-URI ‏{value}, חסר פרוטוקול (scheme)"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "לא ניתן לפענח את ה-URI ‏{value}, חסר מיקום רשת (net location) או נתיב"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "פרוטוקול ה-URI ‏'{parts.scheme}' אינו מורשה. הפרוטוקולים המורשים הם: {', '.join(allowed_schemes)}" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "לא ניתן לפענח את ה URI ‏{value}"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "מזהה more_like_id אינו תקין" msgstr "מזהה more_like_id אינו תקין"
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "הגדרות בינה מלאכותית שגויות." msgstr "הגדרות בינה מלאכותית שגויות."
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "יש לציין רק אחד מהבאים: text‏, title_search‏, query או more_like_id." msgstr "יש לציין רק אחד מהבאים: text‏, title_search‏, query או more_like_id."
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "הרשאות לא מספיקות לשיתוף מסמך %(id)s." msgstr "הרשאות לא מספיקות לשיתוף מסמך %(id)s."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "החבילה (Bundle) כבר נמצאת בתהליך עיבוד." msgstr "החבילה (Bundle) כבר נמצאת בתהליך עיבוד."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "חבילת קישור השיתוף עדיין בהכנה. נא לנסות שוב מאוחר יותר." msgstr "חבילת קישור השיתוף עדיין בהכנה. נא לנסות שוב מאוחר יותר."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "חבילת קישור השיתוף אינה זמינה." msgstr "חבילת קישור השיתוף אינה זמינה."
@@ -2258,151 +2243,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "הגדרות יישום paperless" msgstr "הגדרות יישום paperless"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "אנגלית (ארה״ב)" msgstr "אנגלית (ארה״ב)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "ערבית" msgstr "ערבית"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "אפריקאנס" msgstr "אפריקאנס"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "בלרוסית" msgstr "בלרוסית"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "בולגרית" msgstr "בולגרית"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "קטלאנית" msgstr "קטלאנית"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "צ'כית" msgstr "צ'כית"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "דנית" msgstr "דנית"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "גרמנית" msgstr "גרמנית"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "יוונית" msgstr "יוונית"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "אנגלית (בריטניה)" msgstr "אנגלית (בריטניה)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "ספרדית" msgstr "ספרדית"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "פרסית" msgstr "פרסית"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "פינית" msgstr "פינית"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "צרפתית" msgstr "צרפתית"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "הונגרית" msgstr "הונגרית"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "אינדונזית" msgstr "אינדונזית"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "איטלקית" msgstr "איטלקית"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "יפנית" msgstr "יפנית"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "קוריאנית" msgstr "קוריאנית"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "לוקסמבורגית" msgstr "לוקסמבורגית"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "נורווגית" msgstr "נורווגית"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "הולנדית" msgstr "הולנדית"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "פולנית" msgstr "פולנית"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "פורטוגלית ברזילאית" msgstr "פורטוגלית ברזילאית"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "פורטוגלית" msgstr "פורטוגלית"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "רומנית" msgstr "רומנית"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "רוסית" msgstr "רוסית"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "סלובקית" msgstr "סלובקית"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "סלובנית" msgstr "סלובנית"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "סרבית" msgstr "סרבית"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "שוודית" msgstr "שוודית"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "טורקית" msgstr "טורקית"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "אוקראינית" msgstr "אוקראינית"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "ווייטנאמית" msgstr "ווייטנאמית"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "סינית מפושטת" msgstr "סינית מפושטת"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "סינית מסורתית" msgstr "סינית מסורתית"
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Hindi\n" "Language-Team: Hindi\n"
"Language: hi_IN\n" "Language: hi_IN\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "दस्तावेज़" msgstr "दस्तावेज़"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "मान वैध JSON होना चाहिए." msgstr "मान वैध JSON होना चाहिए."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "अमान्य कस्टम फ़ील्ड क्वेरी एक्सप्रेशन" msgstr "अमान्य कस्टम फ़ील्ड क्वेरी एक्सप्रेशन"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "अमान्य एक्सप्रेशन सूची। खाली नहीं होनी चाहिए।" msgstr "अमान्य एक्सप्रेशन सूची। खाली नहीं होनी चाहिए।"
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "अमान्य लॉजिकल ऑपरेटर {op!r}" msgstr "अमान्य लॉजिकल ऑपरेटर {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "क्वेरी शर्तों की अधिकतम संख्या पार हो गई है।" msgstr "क्वेरी शर्तों की अधिकतम संख्या पार हो गई है।"
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} यह एक वैध कस्टम फ़ील्ड नहीं है।" msgstr "{name!r} यह एक वैध कस्टम फ़ील्ड नहीं है।"
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} क्वेरी एक्सप्रेशन {expr!r} का समर्थन नहीं करता है।" msgstr "{data_type} क्वेरी एक्सप्रेशन {expr!r} का समर्थन नहीं करता है।"
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "अधिकतम नेस्टिंग डेप्थ पार हो गई है।" msgstr "अधिकतम नेस्टिंग डेप्थ पार हो गई है।"
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "कस्टम फ़ील्ड नहीं मिला" msgstr "कस्टम फ़ील्ड नहीं मिला"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1912,59 +1912,44 @@ msgstr ""
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "" msgstr ""
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2256,151 +2241,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Croatian\n" "Language-Team: Croatian\n"
"Language: hr_HR\n" "Language: hr_HR\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumenti" msgstr "Dokumenti"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Vrijednost mora biti važeći JSON." msgstr "Vrijednost mora biti važeći JSON."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Nevažeći izraz upita prilagođenog polja" msgstr "Nevažeći izraz upita prilagođenog polja"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Nevažeći popis izraza. Ne smije biti prazno." msgstr "Nevažeći popis izraza. Ne smije biti prazno."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Nevažeći logički operator {op!r}" msgstr "Nevažeći logički operator {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Previše uvjeta u upitu." msgstr "Previše uvjeta u upitu."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} nije važeće prilagođeno polje." msgstr "{name!r} nije važeće prilagođeno polje."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} ne podržava upit izraz {expr!r}." msgstr "{data_type} ne podržava upit izraz {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Premašena je najveća razina ugniježđivanja." msgstr "Premašena je najveća razina ugniježđivanja."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Prilagođeno polje nije pronađeno" msgstr "Prilagođeno polje nije pronađeno"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "tijek rada pokrenut" msgstr "tijek rada pokrenut"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Nedovoljne ovlasti." msgstr "Nedovoljne ovlasti."
@@ -1676,7 +1676,7 @@ msgstr "Otkrivena je nevaljana vrsta datoteke."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "Duplicirani identifikatori dokumenata nisu dopušteni." msgstr "Duplicirani identifikatori dokumenata nisu dopušteni."
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "Dokumenti nisu pronađeni: %(ids)s" msgstr "Dokumenti nisu pronađeni: %(ids)s"
@@ -1913,59 +1913,44 @@ msgstr "Upravo ćete koristiti svoj račun %(provider_name)s za prijavu."
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Kao posljednji korak ispunite sljedeći obrazac:" msgstr "Kao posljednji korak ispunite sljedeći obrazac:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "Nije moguće analizirati URI {value}, nedostaje shema"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "Nije moguće analizirati URI {value}, nedostaje mrežna lokacija ili put"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "URI shema '{parts.scheme}' nije dopuštena. Dopuštene sheme: {', '.join(allowed_schemes)}" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "Nije moguće raščlaniti URI {value}"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "Nevažeći more_like_id" msgstr "Nevažeći more_like_id"
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "Nevažeća AI konfiguracija." msgstr "Nevažeća AI konfiguracija."
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "Zahtjev prema AI pozadinskom sustavu je istekao." msgstr "Zahtjev prema AI pozadinskom sustavu je istekao."
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "AI pozadinski sustav je odbio zahtjev. Provjerite zapise (logs) za više detalja." msgstr "AI pozadinski sustav je odbio zahtjev. Provjerite zapise (logs) za više detalja."
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Navedite samo jedno od: text, title_search, query ili more_like_id." msgstr "Navedite samo jedno od: text, title_search, query ili more_like_id."
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Nedovoljne ovlasti za dijeljenje dokumenta %(id)s." msgstr "Nedovoljne ovlasti za dijeljenje dokumenta %(id)s."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "Paket se već obrađuje." msgstr "Paket se već obrađuje."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Paket linka za dijeljenje se još priprema. Pokušajte ponovo kasnije." msgstr "Paket linka za dijeljenje se još priprema. Pokušajte ponovo kasnije."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "Paket linka za dijeljenje nije dostupan." msgstr "Paket linka za dijeljenje nije dostupan."
@@ -2257,151 +2242,151 @@ msgstr "LLM timeout (u sekundama)"
msgid "paperless application settings" msgid "paperless application settings"
msgstr "postavke paperless aplikacije" msgstr "postavke paperless aplikacije"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Engleski (US)" msgstr "Engleski (US)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arapski" msgstr "Arapski"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "afrički" msgstr "afrički"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Bjeloruski" msgstr "Bjeloruski"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "bugarski" msgstr "bugarski"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "katalonski" msgstr "katalonski"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Češki" msgstr "Češki"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Danski" msgstr "Danski"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Njemački" msgstr "Njemački"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "grčki" msgstr "grčki"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Engleski (GB)" msgstr "Engleski (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Španjolski" msgstr "Španjolski"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "perzijski" msgstr "perzijski"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "finski" msgstr "finski"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Francuski" msgstr "Francuski"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "mađarski" msgstr "mađarski"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Iidonezijski" msgstr "Iidonezijski"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Talijanski" msgstr "Talijanski"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "japanski" msgstr "japanski"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "koreanski" msgstr "koreanski"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luksemburški" msgstr "Luksemburški"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "norveški" msgstr "norveški"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Nizozemski" msgstr "Nizozemski"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Poljski" msgstr "Poljski"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugalski (Brazil)" msgstr "Portugalski (Brazil)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugalski" msgstr "Portugalski"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Rumunjski" msgstr "Rumunjski"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Ruski" msgstr "Ruski"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "slovački" msgstr "slovački"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Slovenski" msgstr "Slovenski"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Srpski" msgstr "Srpski"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Švedski" msgstr "Švedski"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turski" msgstr "Turski"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "ukrajinski" msgstr "ukrajinski"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "vijetnamski" msgstr "vijetnamski"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Pojednostavljeni kineski" msgstr "Pojednostavljeni kineski"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Kineski tradicionalni" msgstr "Kineski tradicionalni"
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Hungarian\n" "Language-Team: Hungarian\n"
"Language: hu_HU\n" "Language: hu_HU\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumentumok" msgstr "Dokumentumok"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Érvényes JSON érték szükséges." msgstr "Érvényes JSON érték szükséges."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Érvénytelen egyéni mező lekérdezési kifejezés" msgstr "Érvénytelen egyéni mező lekérdezési kifejezés"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Érvénytelen kifejezéslista. Nem lehet üres." msgstr "Érvénytelen kifejezéslista. Nem lehet üres."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Érvénytelen logikai operátor {op!r}" msgstr "Érvénytelen logikai operátor {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Maximum lekérdezési feltételszám átlépve." msgstr "Maximum lekérdezési feltételszám átlépve."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} nem érvényes egyéni mező." msgstr "{name!r} nem érvényes egyéni mező."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "A(z) {data_type} nem támogatja a {expr!r} kifejezés lekérdezést." msgstr "A(z) {data_type} nem támogatja a {expr!r} kifejezés lekérdezést."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Maximum beágyazási mélység túllépve." msgstr "Maximum beágyazási mélység túllépve."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Az egyéni mező nem található" msgstr "Az egyéni mező nem található"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "munkafolyamat futtatások" msgstr "munkafolyamat futtatások"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Nincs jogosúltsága." msgstr "Nincs jogosúltsága."
@@ -1676,7 +1676,7 @@ msgstr "Érvénytelen változó észlelve."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "A dokumentumazonosítók duplikálása nem megengedett." msgstr "A dokumentumazonosítók duplikálása nem megengedett."
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "Dokumentumok nem találhatók: %(ids)s" msgstr "Dokumentumok nem találhatók: %(ids)s"
@@ -1913,59 +1913,44 @@ msgstr "A %(provider_name)s felhasználói fiókkal fogsz belépni."
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Utolsó lépésként kérjük válaszoljon az alábbi kérdésekre:" msgstr "Utolsó lépésként kérjük válaszoljon az alábbi kérdésekre:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "A {value} URI értelmezése sikertelen, hiányzó séma"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "A {value} URI értelmezése sikertelen, hiányzó hálózati vagy elérési útvonal"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "A '{parts.scheme}' séma nem engedélyezett. Engedélyezett sémák: {', '.join(allowed_schemes)}" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "A {value} URI értelmezése sikertelen"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "Érvénytelen more_like_id" msgstr "Érvénytelen more_like_id"
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "Érvénytelen MI konfiguráció." msgstr "Érvénytelen MI konfiguráció."
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "A text, title_search, query, vagy more_like_id közül csak egyet adjon meg." msgstr "A text, title_search, query, vagy more_like_id közül csak egyet adjon meg."
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Nincs megfelelő jogosultság a %(id)s dokumentum megosztásához." msgstr "Nincs megfelelő jogosultság a %(id)s dokumentum megosztásához."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "A csomag feldolgozása már folyamatban van." msgstr "A csomag feldolgozása már folyamatban van."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "A megosztási linkcsomag készítése folyamatban. Kérjük, próbálja meg később." msgstr "A megosztási linkcsomag készítése folyamatban. Kérjük, próbálja meg később."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "A megosztási linkcsomag nem elérhető." msgstr "A megosztási linkcsomag nem elérhető."
@@ -2257,151 +2242,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "paperless alkalmazás beállítások" msgstr "paperless alkalmazás beállítások"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Angol (US)" msgstr "Angol (US)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arab" msgstr "Arab"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Afrikai" msgstr "Afrikai"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Fehérorosz" msgstr "Fehérorosz"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Bolgár" msgstr "Bolgár"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Katalán" msgstr "Katalán"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Cseh" msgstr "Cseh"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Dán" msgstr "Dán"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Német" msgstr "Német"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Görög" msgstr "Görög"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Angol (GB)" msgstr "Angol (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Spanyol" msgstr "Spanyol"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Perzsa" msgstr "Perzsa"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Finn" msgstr "Finn"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Francia" msgstr "Francia"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Magyar" msgstr "Magyar"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indonéz" msgstr "Indonéz"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Olasz" msgstr "Olasz"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japán" msgstr "Japán"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Koreai" msgstr "Koreai"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luxemburgi" msgstr "Luxemburgi"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norvég" msgstr "Norvég"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Holland" msgstr "Holland"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Lengyel" msgstr "Lengyel"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugál (Brazília)" msgstr "Portugál (Brazília)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugál" msgstr "Portugál"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Román" msgstr "Román"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Orosz" msgstr "Orosz"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Szlovák" msgstr "Szlovák"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Szlovén" msgstr "Szlovén"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Szerb" msgstr "Szerb"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Svéd" msgstr "Svéd"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Török" msgstr "Török"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukrán" msgstr "Ukrán"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Vietnámi" msgstr "Vietnámi"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Kínai egyszerűsített" msgstr "Kínai egyszerűsített"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Tradicionális kínai" msgstr "Tradicionális kínai"
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Indonesian\n" "Language-Team: Indonesian\n"
"Language: id_ID\n" "Language: id_ID\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumen" msgstr "Dokumen"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Nilai harus berupa JSON yang valid." msgstr "Nilai harus berupa JSON yang valid."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Ekspresi pencarian bidang khusus tidak valid" msgstr "Ekspresi pencarian bidang khusus tidak valid"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Daftar ekspresi tidak valid. Tidak boleh kosong." msgstr "Daftar ekspresi tidak valid. Tidak boleh kosong."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Operator logika {op!r} tidak valid" msgstr "Operator logika {op!r} tidak valid"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Jumlah maksimal kondisi pencarian terlampaui." msgstr "Jumlah maksimal kondisi pencarian terlampaui."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} bukan bidang khusus yang valid." msgstr "{name!r} bukan bidang khusus yang valid."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} tidak mendukung ekspresi pencarian expr {expr!r}." msgstr "{data_type} tidak mendukung ekspresi pencarian expr {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Kedalaman susunan maksimal terlampaui." msgstr "Kedalaman susunan maksimal terlampaui."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Bidang khusus tidak ditemukan" msgstr "Bidang khusus tidak ditemukan"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "daftar jalankan alur kerja" msgstr "daftar jalankan alur kerja"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Izin tidak mencukupi" msgstr "Izin tidak mencukupi"
@@ -1676,7 +1676,7 @@ msgstr "Variabel ilegal terdeteksi."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "Penggunaan pengenal dokumen ganda tidak diperbolehkan." msgstr "Penggunaan pengenal dokumen ganda tidak diperbolehkan."
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "Dokumen tidak ditemukan: %(ids)s" msgstr "Dokumen tidak ditemukan: %(ids)s"
@@ -1913,59 +1913,44 @@ msgstr "Anda akan menggunakan akun %(provider_name)s Anda untuk masuk."
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Sebagai langkah terakhir, mohon formulir berikut:" msgstr "Sebagai langkah terakhir, mohon formulir berikut:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "Gagal membaca URI {value}, skema tidak ditemukan"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "Gagal membaca URI {value}, lokasi jaringan atau path tidak ditemukan"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "Skema URI '{parts.scheme}' tidak diizinkan. Skema yang diizinkan: {', '.join(allowed_schemes)}" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "Gagal membaca URI {value}"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Izin tidak mencukupi untuk berbagi dokumen %(id)s" msgstr "Izin tidak mencukupi untuk berbagi dokumen %(id)s"
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "Paket sedang diproses." msgstr "Paket sedang diproses."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Bundel tautan berbagi masih dalam proses persiapan. Silakan coba lagi nanti." msgstr "Bundel tautan berbagi masih dalam proses persiapan. Silakan coba lagi nanti."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "Bundel tautan berbagi tidak tersedia." msgstr "Bundel tautan berbagi tidak tersedia."
@@ -2257,151 +2242,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "pengaturan aplikasi paperless" msgstr "pengaturan aplikasi paperless"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Inggris (AS)" msgstr "Inggris (AS)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arab" msgstr "Arab"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Bahasa Afrika" msgstr "Bahasa Afrika"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Belarusia" msgstr "Belarusia"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Bahasa Bulgaria" msgstr "Bahasa Bulgaria"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Katalan" msgstr "Katalan"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Bahasa Ceko" msgstr "Bahasa Ceko"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Denmark" msgstr "Denmark"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Jerman" msgstr "Jerman"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Yunani" msgstr "Yunani"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Inggris (GB)" msgstr "Inggris (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Spanyol" msgstr "Spanyol"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Persia" msgstr "Persia"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Finlandia" msgstr "Finlandia"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Prancis" msgstr "Prancis"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Hungaria" msgstr "Hungaria"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indonesia" msgstr "Indonesia"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italia" msgstr "Italia"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Bahasa Jepang" msgstr "Bahasa Jepang"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Bahasa Korea" msgstr "Bahasa Korea"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luksemburg" msgstr "Luksemburg"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norwegia" msgstr "Norwegia"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Belanda" msgstr "Belanda"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polandia" msgstr "Polandia"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugis (Brasil)" msgstr "Portugis (Brasil)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugis" msgstr "Portugis"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Rumania" msgstr "Rumania"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Rusia" msgstr "Rusia"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Slovakia" msgstr "Slovakia"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Slovenia" msgstr "Slovenia"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbia" msgstr "Serbia"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Swedia" msgstr "Swedia"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turki" msgstr "Turki"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukraina" msgstr "Ukraina"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Vietnam" msgstr "Vietnam"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Mandarin Sederhana" msgstr "Mandarin Sederhana"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Tionghoa Tradisional" msgstr "Tionghoa Tradisional"
+70 -85
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Italian\n" "Language-Team: Italian\n"
"Language: it_IT\n" "Language: it_IT\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Documenti" msgstr "Documenti"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Il valore deve essere un JSON valido." msgstr "Il valore deve essere un JSON valido."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Campo personalizzato della query non valido" msgstr "Campo personalizzato della query non valido"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Elenco delle espressioni non valido. Deve essere non vuoto." msgstr "Elenco delle espressioni non valido. Deve essere non vuoto."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Operatore logico non valido {op!r}" msgstr "Operatore logico non valido {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Numero massimo di condizioni di query superato." msgstr "Numero massimo di condizioni di query superato."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} non è un campo personalizzato valido." msgstr "{name!r} non è un campo personalizzato valido."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} Non supporta la jQuery Expo {Expo!r}." msgstr "{data_type} Non supporta la jQuery Expo {Expo!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Profondità massima di nidificazione superata." msgstr "Profondità massima di nidificazione superata."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Campo personalizzato non trovato" msgstr "Campo personalizzato non trovato"
@@ -253,7 +253,7 @@ msgstr "documento radice per questa versione"
#: documents/models.py:325 #: documents/models.py:325
msgid "version index" msgid "version index"
msgstr "indice della versione" msgstr "indice versione"
#: documents/models.py:329 #: documents/models.py:329
msgid "Index of this version within the root document." msgid "Index of this version within the root document."
@@ -261,11 +261,11 @@ msgstr "Indice di questa versione all'interno del documento radice."
#: documents/models.py:333 #: documents/models.py:333
msgid "version label" msgid "version label"
msgstr "etichetta della versione" msgstr "etichetta versione"
#: documents/models.py:337 #: documents/models.py:337
msgid "Optional short label for a document version." msgid "Optional short label for a document version."
msgstr "Etichetta breve facoltativa per una versione del documento." msgstr "Etichetta abbreviata facoltativa per una versione del documento."
#: documents/models.py:342 documents/models.py:961 documents/models.py:992 #: documents/models.py:342 documents/models.py:961 documents/models.py:992
#: documents/models.py:1096 documents/models.py:2126 #: documents/models.py:1096 documents/models.py:2126
@@ -1006,23 +1006,23 @@ msgstr "note"
#: documents/models.py:996 #: documents/models.py:996
msgid "page" msgid "page"
msgstr "" msgstr "pagina"
#: documents/models.py:997 #: documents/models.py:997
msgid "Page of the original file, starting at 1" msgid "Page of the original file, starting at 1"
msgstr "" msgstr "Pagina del file originale, partendo da 1"
#: documents/models.py:1052 #: documents/models.py:1052
msgid "format" msgid "format"
msgstr "" msgstr "formato"
#: documents/models.py:1056 #: documents/models.py:1056
msgid "document barcode" msgid "document barcode"
msgstr "" msgstr "codice a barre del documento"
#: documents/models.py:1057 #: documents/models.py:1057
msgid "document barcodes" msgid "document barcodes"
msgstr "" msgstr "codici a barre del documento"
#: documents/models.py:1066 #: documents/models.py:1066
msgid "Original" msgid "Original"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "esecuzioni del flusso di lavoro" msgstr "esecuzioni del flusso di lavoro"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Autorizzazioni insufficienti." msgstr "Autorizzazioni insufficienti."
@@ -1676,7 +1676,7 @@ msgstr "Variabile non valida rilevata."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "Non sono consentiti identificatori di documenti duplicati." msgstr "Non sono consentiti identificatori di documenti duplicati."
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "Documenti non trovati: %(ids)s" msgstr "Documenti non trovati: %(ids)s"
@@ -1913,59 +1913,44 @@ msgstr "Stai per utilizzare il tuo account %(provider_name)s per effettuare il l
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Come ultimo passo, compila il seguente modulo:" msgstr "Come ultimo passo, compila il seguente modulo:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "Impossibile analizzare l'URI {value}, schema mancante"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "Impossibile analizzare l'URI {value}, posizione o percorso di rete mancante"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "Lo schema URI '{parts.scheme}' non è consentito. Schemi consentiti: {', '.join(allowed_schemes)}" msgstr ", "
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "Impossibile analizzare l'URI {value}"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "more_like_id non valido" msgstr "more_like_id non valido"
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "Configurazione AI non valida." msgstr "Configurazione AI non valida."
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "Richiesta di backend AI scaduta." msgstr "Richiesta di backend AI scaduta."
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "Il backend IA ha rifiutato la richiesta. Controlla i log per i dettagli." msgstr "Il backend IA ha rifiutato la richiesta. Controlla i log per i dettagli."
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Specificare solo uno tra text, title_search, query o more_like_id." msgstr "Specificare solo uno tra text, title_search, query o more_like_id."
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Autorizzazioni insufficienti per condividere il documento %(id)s." msgstr "Autorizzazioni insufficienti per condividere il documento %(id)s."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "Il pacchetto è già in fase di elaborazione." msgstr "Il pacchetto è già in fase di elaborazione."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Il pacchetto di link di condivisione è ancora in fase di preparazione. Riprova più tardi." msgstr "Il pacchetto di link di condivisione è ancora in fase di preparazione. Riprova più tardi."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "Il pacchetto di link di condivisione non è disponibile." msgstr "Il pacchetto di link di condivisione non è disponibile."
@@ -2183,7 +2168,7 @@ msgstr "Abilita la suddivisione sui codici a barre dei tag"
#: paperless/models.py:308 #: paperless/models.py:308
msgid "Stores the values of detected barcodes" msgid "Stores the values of detected barcodes"
msgstr "" msgstr "Memorizza i valori dei codici a barre individuati"
#: paperless/models.py:318 #: paperless/models.py:318
msgid "Sets the remote OCR engine" msgid "Sets the remote OCR engine"
@@ -2257,151 +2242,151 @@ msgstr "Impostare il timeout dell’LLM in secondi"
msgid "paperless application settings" msgid "paperless application settings"
msgstr "impostazioni dell'applicazione paperless" msgstr "impostazioni dell'applicazione paperless"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Inglese (US)" msgstr "Inglese (US)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arabo" msgstr "Arabo"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Afrikaans" msgstr "Afrikaans"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Bielorusso" msgstr "Bielorusso"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Bulgaro" msgstr "Bulgaro"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Catalano" msgstr "Catalano"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Ceco" msgstr "Ceco"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Danese" msgstr "Danese"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Tedesco" msgstr "Tedesco"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Greco" msgstr "Greco"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Inglese (GB)" msgstr "Inglese (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Spagnolo" msgstr "Spagnolo"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Persiano" msgstr "Persiano"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Finlandese" msgstr "Finlandese"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Francese" msgstr "Francese"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Ungherese" msgstr "Ungherese"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indonesiano" msgstr "Indonesiano"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italiano" msgstr "Italiano"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Giapponese" msgstr "Giapponese"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Coreano" msgstr "Coreano"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Lussemburghese" msgstr "Lussemburghese"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norvegese" msgstr "Norvegese"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Olandese" msgstr "Olandese"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polacco" msgstr "Polacco"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portoghese (Brasile)" msgstr "Portoghese (Brasile)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portoghese" msgstr "Portoghese"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Rumeno" msgstr "Rumeno"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Russo" msgstr "Russo"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Slovacco" msgstr "Slovacco"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Sloveno" msgstr "Sloveno"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbo" msgstr "Serbo"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Svedese" msgstr "Svedese"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turco" msgstr "Turco"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ucraino" msgstr "Ucraino"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Vietnamita" msgstr "Vietnamita"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Cinese semplificato" msgstr "Cinese semplificato"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Cinese tradizionale" msgstr "Cinese tradizionale"
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Japanese\n" "Language-Team: Japanese\n"
"Language: ja_JP\n" "Language: ja_JP\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "ドキュメント" msgstr "ドキュメント"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "値は有効なJSONである必要があります。" msgstr "値は有効なJSONである必要があります。"
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "無効なカスタムフィールドクエリ式" msgstr "無効なカスタムフィールドクエリ式"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "無効な式リストです。空であってはなりません。" msgstr "無効な式リストです。空であってはなりません。"
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "無効な論理演算子 {op!r}" msgstr "無効な論理演算子 {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "クエリ条件の最大数を超えました。" msgstr "クエリ条件の最大数を超えました。"
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} は有効なカスタムフィールドではありません。" msgstr "{name!r} は有効なカスタムフィールドではありません。"
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} はクエリ expr {expr!r} をサポートしていません。" msgstr "{data_type} はクエリ expr {expr!r} をサポートしていません。"
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "最大ネストの深さを超えました。" msgstr "最大ネストの深さを超えました。"
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "カスタムフィールドが見つかりません" msgstr "カスタムフィールドが見つかりません"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "ワークフローの実行" msgstr "ワークフローの実行"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "無効な変数を検出しました"
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1913,59 +1913,44 @@ msgstr "%(provider_name)s アカウントでログインしようとしていま
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "最後のステップとして、以下のフォームに記入してください" msgstr "最後のステップとして、以下のフォームに記入してください"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2257,151 +2242,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "Paperless アプリケーションの設定" msgstr "Paperless アプリケーションの設定"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "英語 (米国)" msgstr "英語 (米国)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "アラビア語" msgstr "アラビア語"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "アフリカーンス語" msgstr "アフリカーンス語"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "ベラルーシ語" msgstr "ベラルーシ語"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "ブルガリア語" msgstr "ブルガリア語"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "カタロニア語" msgstr "カタロニア語"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "チェコ語" msgstr "チェコ語"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "デンマーク語" msgstr "デンマーク語"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "ドイツ語" msgstr "ドイツ語"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "ギリシャ語" msgstr "ギリシャ語"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "英語 (英国)" msgstr "英語 (英国)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "スペイン語" msgstr "スペイン語"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "フィンランド語" msgstr "フィンランド語"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "フランス語" msgstr "フランス語"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "ハンガリー語" msgstr "ハンガリー語"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "イタリア語" msgstr "イタリア語"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "日本語" msgstr "日本語"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "韓国語" msgstr "韓国語"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "ルクセンブルク語" msgstr "ルクセンブルク語"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "ノルウェー語" msgstr "ノルウェー語"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "オランダ語" msgstr "オランダ語"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "ポーランド語" msgstr "ポーランド語"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "ポルトガル語 (ブラジル)" msgstr "ポルトガル語 (ブラジル)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "ポルトガル語" msgstr "ポルトガル語"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "ルーマニア語" msgstr "ルーマニア語"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "ロシア語" msgstr "ロシア語"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "スロバキア語" msgstr "スロバキア語"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "スロベニア語" msgstr "スロベニア語"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "セルビア語" msgstr "セルビア語"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "スウェーデン語" msgstr "スウェーデン語"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "トルコ語" msgstr "トルコ語"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "ウクライナ語" msgstr "ウクライナ語"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "中国語 (簡体字)" msgstr "中国語 (簡体字)"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "中国語 (繁体)" msgstr "中国語 (繁体)"
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Korean\n" "Language-Team: Korean\n"
"Language: ko_KR\n" "Language: ko_KR\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "문서" msgstr "문서"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "값은 유효한 JSON이어야 합니다." msgstr "값은 유효한 JSON이어야 합니다."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "잘못된 사용자 정의 필드 쿼리 표현식" msgstr "잘못된 사용자 정의 필드 쿼리 표현식"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "잘못된 표현식 목록입니다. 비어 있지 않아야 합니다." msgstr "잘못된 표현식 목록입니다. 비어 있지 않아야 합니다."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "잘못된 논리 연산자 {op!r}" msgstr "잘못된 논리 연산자 {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "쿼리 조건의 최대 개수를 초과했습니다." msgstr "쿼리 조건의 최대 개수를 초과했습니다."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} 은 잘못된 사용자 정의 필드입니다." msgstr "{name!r} 은 잘못된 사용자 정의 필드입니다."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type}은 쿼리 표현식 {expr!r}을(를) 지원하지 않습니다." msgstr "{data_type}은 쿼리 표현식 {expr!r}을(를) 지원하지 않습니다."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "최대 중첩 깊이를 초과했습니다." msgstr "최대 중첩 깊이를 초과했습니다."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "사용자 지정 필드를 찾을 수 없음" msgstr "사용자 지정 필드를 찾을 수 없음"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "워크플로우 실행" msgstr "워크플로우 실행"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "잘못된 변수가 감지되었습니다."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1913,59 +1913,44 @@ msgstr "%(provider_name)s 의 계정을 사용하여 로그인하려고 합니
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "마지막 단계로 다음 양식을 작성해 주세요:" msgstr "마지막 단계로 다음 양식을 작성해 주세요:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "{value} URI를 파싱할 수 없습니다: 스킴이 누락되었습니다."
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "{value} URI를 파싱할 수 없습니다: 네트워크 위치 또는 경로가 누락되었습니다."
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "URI 스킴 '{parts.scheme}'는 허용되지 않습니다. 허용된 스킴: {', '.join(allowed_schemes)}" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "{value} URI를 파싱할 수 없음"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2257,151 +2242,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "페이퍼리스 애플리케이션 설정" msgstr "페이퍼리스 애플리케이션 설정"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "영어 (미국)" msgstr "영어 (미국)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "아랍어" msgstr "아랍어"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "아프리칸스어" msgstr "아프리칸스어"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "벨라루스어" msgstr "벨라루스어"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "불가리어" msgstr "불가리어"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "카탈로니아어" msgstr "카탈로니아어"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "체코어" msgstr "체코어"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "덴마크어" msgstr "덴마크어"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "독일어" msgstr "독일어"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "그리스어" msgstr "그리스어"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "영어 (영국)" msgstr "영어 (영국)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "스페인어" msgstr "스페인어"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "페르시아어" msgstr "페르시아어"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "핀란드어" msgstr "핀란드어"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "프랑스어" msgstr "프랑스어"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "헝가리어" msgstr "헝가리어"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "이탈리아어" msgstr "이탈리아어"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "일본어" msgstr "일본어"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "한국어" msgstr "한국어"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "룩셈부르크어" msgstr "룩셈부르크어"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "노르웨이어" msgstr "노르웨이어"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "네덜란드어" msgstr "네덜란드어"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "폴란드어" msgstr "폴란드어"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "포르투갈어 (브라질)" msgstr "포르투갈어 (브라질)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "포르투갈어" msgstr "포르투갈어"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "루마니아어" msgstr "루마니아어"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "러시아어" msgstr "러시아어"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "슬로바키아어" msgstr "슬로바키아어"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "슬로베니아어" msgstr "슬로베니아어"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "세르비아어" msgstr "세르비아어"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "스웨덴어" msgstr "스웨덴어"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "튀르키예어" msgstr "튀르키예어"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "우크라이나어" msgstr "우크라이나어"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "베트남어" msgstr "베트남어"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "중국어 간체" msgstr "중국어 간체"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "중국어 번체" msgstr "중국어 번체"
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Luxembourgish\n" "Language-Team: Luxembourgish\n"
"Language: lb_LU\n" "Language: lb_LU\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumenter" msgstr "Dokumenter"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "" msgstr ""
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "" msgstr ""
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "" msgstr ""
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "" msgstr ""
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "" msgstr ""
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "" msgstr ""
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "" msgstr ""
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "" msgstr ""
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "" msgstr ""
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Ongëlteg Zeechen detektéiert."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1912,59 +1912,44 @@ msgstr ""
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "" msgstr ""
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2256,151 +2241,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Englesch (USA)" msgstr "Englesch (USA)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Belarusesch" msgstr "Belarusesch"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Tschechesch" msgstr "Tschechesch"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Dänesch" msgstr "Dänesch"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Däitsch" msgstr "Däitsch"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Englesch (GB)" msgstr "Englesch (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Spuenesch" msgstr "Spuenesch"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Franséisch" msgstr "Franséisch"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italienesch" msgstr "Italienesch"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Lëtzebuergesch" msgstr "Lëtzebuergesch"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Hollännesch" msgstr "Hollännesch"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polnesch" msgstr "Polnesch"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugisesch (Brasilien)" msgstr "Portugisesch (Brasilien)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugisesch" msgstr "Portugisesch"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Rumänesch" msgstr "Rumänesch"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Russesch" msgstr "Russesch"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Slowenesch" msgstr "Slowenesch"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbesch" msgstr "Serbesch"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Schwedesch" msgstr "Schwedesch"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Tierkesch" msgstr "Tierkesch"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Chinesesch (Vereinfacht)" msgstr "Chinesesch (Vereinfacht)"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Lithuanian\n" "Language-Team: Lithuanian\n"
"Language: lt_LT\n" "Language: lt_LT\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumentai" msgstr "Dokumentai"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Reikšmė turi būti galiojantis JSON." msgstr "Reikšmė turi būti galiojantis JSON."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Neteisinga pasirinktinio lauko užklausos išraiška" msgstr "Neteisinga pasirinktinio lauko užklausos išraiška"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Neteisingas išraiškos sąrašas. Jis turi būti netuščias." msgstr "Neteisingas išraiškos sąrašas. Jis turi būti netuščias."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Neteisingas loginis operatorius {op!r}" msgstr "Neteisingas loginis operatorius {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Viršytas maksimalus užklausos sąlygų skaičius." msgstr "Viršytas maksimalus užklausos sąlygų skaičius."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} nėra galiojantis pasirinktas laukas." msgstr "{name!r} nėra galiojantis pasirinktas laukas."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} nepalaiko užklausos išraiškos {expr!r}." msgstr "{data_type} nepalaiko užklausos išraiškos {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Viršytas maksimalus įdėjimo gylis." msgstr "Viršytas maksimalus įdėjimo gylis."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Pasirinktinis laukas nerastas" msgstr "Pasirinktinis laukas nerastas"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1912,59 +1912,44 @@ msgstr "Ketinate prisijungimui naudoti savo %(provider_name)s paskyrą."
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Galiausiai užpildykite šią formą:" msgstr "Galiausiai užpildykite šią formą:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "Nepavyko apdoroti URI {value}, trūksta schemos"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "Nepavyko apdoroti URI {value}, trūksta tinklo vietos arba kelio"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "URI schema '{parts.scheme}' nėra leistina. Galimos schemos: {', '.join(allowed_schemes)}" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "Nepavyko apdoroti URI {value}"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Nepakanka leidimų bendrinti dokumentą %(id)s." msgstr "Nepakanka leidimų bendrinti dokumentą %(id)s."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "Rinkinys jau apdorojamas." msgstr "Rinkinys jau apdorojamas."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Bendrinimo nuorodų rinkinys vis dar ruošiamas. Bandykite dar kartą vėliau." msgstr "Bendrinimo nuorodų rinkinys vis dar ruošiamas. Bandykite dar kartą vėliau."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "Dalinimos nuorodų rinkinys yra nepasiekiamas." msgstr "Dalinimos nuorodų rinkinys yra nepasiekiamas."
@@ -2256,151 +2241,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Daniškai" msgstr "Daniškai"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Vokiškai" msgstr "Vokiškai"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Graikų" msgstr "Graikų"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Anglų (GB)" msgstr "Anglų (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Ispanų" msgstr "Ispanų"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Persų" msgstr "Persų"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Suomių" msgstr "Suomių"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Prancūzų" msgstr "Prancūzų"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Vengrų" msgstr "Vengrų"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indoneziečių" msgstr "Indoneziečių"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italų" msgstr "Italų"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japonų" msgstr "Japonų"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Korėjiečių" msgstr "Korėjiečių"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Liuksemburgiečių" msgstr "Liuksemburgiečių"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norvegų" msgstr "Norvegų"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Olandų" msgstr "Olandų"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Lenkų" msgstr "Lenkų"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugalų (Brazilija)" msgstr "Portugalų (Brazilija)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugalų" msgstr "Portugalų"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Rumunų" msgstr "Rumunų"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Rusų" msgstr "Rusų"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Slovakų" msgstr "Slovakų"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Slovėnų" msgstr "Slovėnų"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbų" msgstr "Serbų"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Švedų" msgstr "Švedų"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turkų" msgstr "Turkų"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukrainiečių" msgstr "Ukrainiečių"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Vietnamiečių" msgstr "Vietnamiečių"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Kinų supaprastinta" msgstr "Kinų supaprastinta"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Kinų tradicinė" msgstr "Kinų tradicinė"
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Latvian\n" "Language-Team: Latvian\n"
"Language: lv_LV\n" "Language: lv_LV\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokuments" msgstr "Dokuments"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "" msgstr ""
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "" msgstr ""
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "" msgstr ""
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "" msgstr ""
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "" msgstr ""
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "" msgstr ""
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "" msgstr ""
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "" msgstr ""
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "" msgstr ""
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1912,59 +1912,44 @@ msgstr ""
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "" msgstr ""
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2256,151 +2241,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Angļu (ASV)" msgstr "Angļu (ASV)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arābu" msgstr "Arābu"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Āfrikāņu" msgstr "Āfrikāņu"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Baltkrievu" msgstr "Baltkrievu"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Bulgāru" msgstr "Bulgāru"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Kataloniešu" msgstr "Kataloniešu"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Čehu" msgstr "Čehu"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Dāņu" msgstr "Dāņu"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Vācu" msgstr "Vācu"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Grieķu" msgstr "Grieķu"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Angļu (ASV)" msgstr "Angļu (ASV)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Spāņu" msgstr "Spāņu"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Somu" msgstr "Somu"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Franču" msgstr "Franču"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Ungāru" msgstr "Ungāru"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Itāļu" msgstr "Itāļu"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japāņu" msgstr "Japāņu"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luksemburgiešu" msgstr "Luksemburgiešu"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norvēģu" msgstr "Norvēģu"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Holandiešu" msgstr "Holandiešu"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Poļu" msgstr "Poļu"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugāļu (Brazīlija)" msgstr "Portugāļu (Brazīlija)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugāļu" msgstr "Portugāļu"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Rumāņu" msgstr "Rumāņu"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Krievu" msgstr "Krievu"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Slovāku" msgstr "Slovāku"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Slovēņu" msgstr "Slovēņu"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Sērbu" msgstr "Sērbu"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Zviedru" msgstr "Zviedru"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turku" msgstr "Turku"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukraiņu" msgstr "Ukraiņu"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Ķīniešu (vienkāršota)" msgstr "Ķīniešu (vienkāršota)"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Macedonian\n" "Language-Team: Macedonian\n"
"Language: mk_MK\n" "Language: mk_MK\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "" msgstr ""
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "" msgstr ""
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "" msgstr ""
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "" msgstr ""
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "" msgstr ""
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "" msgstr ""
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "" msgstr ""
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "" msgstr ""
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "" msgstr ""
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "" msgstr ""
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1912,59 +1912,44 @@ msgstr ""
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "" msgstr ""
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2256,151 +2241,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Malay\n" "Language-Team: Malay\n"
"Language: ms_MY\n" "Language: ms_MY\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumen" msgstr "Dokumen"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "" msgstr ""
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "" msgstr ""
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "" msgstr ""
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "" msgstr ""
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "" msgstr ""
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "" msgstr ""
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "" msgstr ""
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "" msgstr ""
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "" msgstr ""
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr ""
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1912,59 +1912,44 @@ msgstr ""
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "" msgstr ""
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2256,151 +2241,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+60 -75
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Dutch\n" "Language-Team: Dutch\n"
"Language: nl_NL\n" "Language: nl_NL\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Documenten" msgstr "Documenten"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Waarde moet een geldige JSON zijn." msgstr "Waarde moet een geldige JSON zijn."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Ongeldige aangepaste veld query expressie" msgstr "Ongeldige aangepaste veld query expressie"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Ongeldige expressielijst mag niet leeg zijn." msgstr "Ongeldige expressielijst mag niet leeg zijn."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Ongeldige logische operator {op!r}" msgstr "Ongeldige logische operator {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Maximum aantal query voorwaarden overschreden." msgstr "Maximum aantal query voorwaarden overschreden."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} is geen geldig aangepast veld." msgstr "{name!r} is geen geldig aangepast veld."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} ondersteunt geen query expr {expr!r}." msgstr "{data_type} ondersteunt geen query expr {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Maximale nestdiepte overschreden." msgstr "Maximale nestdiepte overschreden."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Aangepast veld niet gevonden" msgstr "Aangepast veld niet gevonden"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "" msgstr ""
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "" msgstr ""
@@ -1676,7 +1676,7 @@ msgstr "Ongeldige variabele ontdekt."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "" msgstr ""
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "" msgstr ""
@@ -1913,59 +1913,44 @@ msgstr "Je staat op het punt om met je %(provider_name)s account in te loggen."
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Vul als laatste stap het volgende formulier in:" msgstr "Vul als laatste stap het volgende formulier in:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr ""
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr ""
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr ""
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "" msgstr ""
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "" msgstr ""
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "" msgstr ""
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "" msgstr ""
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "" msgstr ""
@@ -2257,151 +2242,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "paperless applicatie instellingen" msgstr "paperless applicatie instellingen"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Engels (US)" msgstr "Engels (US)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arabisch" msgstr "Arabisch"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Afrikaans" msgstr "Afrikaans"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Wit-Russisch" msgstr "Wit-Russisch"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Bulgaars" msgstr "Bulgaars"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Catalaans" msgstr "Catalaans"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Tsjechisch" msgstr "Tsjechisch"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Deens" msgstr "Deens"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Duits" msgstr "Duits"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Grieks" msgstr "Grieks"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Engels (Brits)" msgstr "Engels (Brits)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Spaans" msgstr "Spaans"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Perzisch" msgstr "Perzisch"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Fins" msgstr "Fins"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Frans" msgstr "Frans"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Hongaars" msgstr "Hongaars"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "" msgstr ""
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italiaans" msgstr "Italiaans"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japans" msgstr "Japans"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Koreaans" msgstr "Koreaans"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luxemburgs" msgstr "Luxemburgs"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Noors" msgstr "Noors"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Nederlands" msgstr "Nederlands"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Pools" msgstr "Pools"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugees (Brazilië)" msgstr "Portugees (Brazilië)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugees" msgstr "Portugees"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Roemeens" msgstr "Roemeens"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Russisch" msgstr "Russisch"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Slowaaks" msgstr "Slowaaks"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Sloveens" msgstr "Sloveens"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Servisch" msgstr "Servisch"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Zweeds" msgstr "Zweeds"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turks" msgstr "Turks"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Oekraïens" msgstr "Oekraïens"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Vietnamees" msgstr "Vietnamees"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Chinees (vereenvoudigd)" msgstr "Chinees (vereenvoudigd)"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "" msgstr ""
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Norwegian\n" "Language-Team: Norwegian\n"
"Language: no_NO\n" "Language: no_NO\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumenter" msgstr "Dokumenter"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Verdien må være gyldig JSON." msgstr "Verdien må være gyldig JSON."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Ugyldig spørringsuttrykk for egendefinerte felt" msgstr "Ugyldig spørringsuttrykk for egendefinerte felt"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Ugyldig uttrykksliste. Kan ikke være tom." msgstr "Ugyldig uttrykksliste. Kan ikke være tom."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Ugyldig logiske operator {op!r}" msgstr "Ugyldig logiske operator {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Maksimalt antall spørringsbetingelser er overskredet." msgstr "Maksimalt antall spørringsbetingelser er overskredet."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} er ikke et gyldig egendefinert felt." msgstr "{name!r} er ikke et gyldig egendefinert felt."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} støtter ikke spørring expr {expr!r}." msgstr "{data_type} støtter ikke spørring expr {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "For mange nivåer med nøsting." msgstr "For mange nivåer med nøsting."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Egendefinert felt ble ikke funnet" msgstr "Egendefinert felt ble ikke funnet"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "workflow runs (NO)" msgstr "workflow runs (NO)"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Utilstrekkelige tillatelser." msgstr "Utilstrekkelige tillatelser."
@@ -1676,7 +1676,7 @@ msgstr "Ugyldig variabel oppdaget."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "Duplicate document identifiers are not allowed. (NO)" msgstr "Duplicate document identifiers are not allowed. (NO)"
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "Documents not found: %(ids)s (NO)" msgstr "Documents not found: %(ids)s (NO)"
@@ -1913,59 +1913,44 @@ msgstr "You are about to use your %(provider_name)s account to login. (NO)"
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "As a final step, please complete the following form: (NO)" msgstr "As a final step, please complete the following form: (NO)"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "Unable to parse URI {value}, missing scheme (NO)"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "Unable to parse URI {value}, missing net location or path (NO)"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)} (NO)" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "Unable to parse URI {value} (NO)"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "Invalid more_like_id (NO)" msgstr "Invalid more_like_id (NO)"
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "Invalid AI configuration. (NO)" msgstr "Invalid AI configuration. (NO)"
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "Forespørselen til KI-tjenesten fikk tidsavbrudd." msgstr "Forespørselen til KI-tjenesten fikk tidsavbrudd."
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Specify only one of text, title_search, query, or more_like_id. (NO)" msgstr "Specify only one of text, title_search, query, or more_like_id. (NO)"
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Insufficient permissions to share document %(id)s. (NO)" msgstr "Insufficient permissions to share document %(id)s. (NO)"
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "Bundle is already being processed. (NO)" msgstr "Bundle is already being processed. (NO)"
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "The share link bundle is still being prepared. Please try again later. (NO)" msgstr "The share link bundle is still being prepared. Please try again later. (NO)"
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "The share link bundle is unavailable. (NO)" msgstr "The share link bundle is unavailable. (NO)"
@@ -2257,151 +2242,151 @@ msgstr "Setter tidsavbrudd for LLM i sekunder"
msgid "paperless application settings" msgid "paperless application settings"
msgstr "paperless application settings (NO)" msgstr "paperless application settings (NO)"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Engelsk (US)" msgstr "Engelsk (US)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Arabisk" msgstr "Arabisk"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Afrikansk" msgstr "Afrikansk"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Hviterussisk" msgstr "Hviterussisk"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Bulgarian (NO)" msgstr "Bulgarian (NO)"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Katalansk" msgstr "Katalansk"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Tsjekkisk" msgstr "Tsjekkisk"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Dansk" msgstr "Dansk"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Tysk" msgstr "Tysk"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Gresk" msgstr "Gresk"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Engelsk (GB)" msgstr "Engelsk (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Spansk" msgstr "Spansk"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Persian (NO)" msgstr "Persian (NO)"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Finsk" msgstr "Finsk"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Fransk" msgstr "Fransk"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Hungarian (NO)" msgstr "Hungarian (NO)"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indonesian (NO)" msgstr "Indonesian (NO)"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italiensk" msgstr "Italiensk"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japanese (NO)" msgstr "Japanese (NO)"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Korean (NO)" msgstr "Korean (NO)"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luxembourgsk" msgstr "Luxembourgsk"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norsk" msgstr "Norsk"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Nederlandsk" msgstr "Nederlandsk"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polsk" msgstr "Polsk"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugisisk (Brasil)" msgstr "Portugisisk (Brasil)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugisisk" msgstr "Portugisisk"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Rumensk" msgstr "Rumensk"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Russisk" msgstr "Russisk"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Slovakisk" msgstr "Slovakisk"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Slovenian (NO)" msgstr "Slovenian (NO)"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbisk" msgstr "Serbisk"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Svensk" msgstr "Svensk"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Tyrkisk" msgstr "Tyrkisk"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukrainsk" msgstr "Ukrainsk"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Vietnamese (NO)" msgstr "Vietnamese (NO)"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Kinesisk forenklet" msgstr "Kinesisk forenklet"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Chinese Traditional (NO)" msgstr "Chinese Traditional (NO)"
+61 -76
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Polish\n" "Language-Team: Polish\n"
"Language: pl_PL\n" "Language: pl_PL\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Dokumenty" msgstr "Dokumenty"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "Wartość musi być poprawnym JSON-em." msgstr "Wartość musi być poprawnym JSON-em."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Nieprawidłowe wyrażenie zapytania pola niestandardowego" msgstr "Nieprawidłowe wyrażenie zapytania pola niestandardowego"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Nieprawidłowa lista wyrażeń. Nie może być pusta." msgstr "Nieprawidłowa lista wyrażeń. Nie może być pusta."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Nieprawidłowy operator logiczny {op!r}" msgstr "Nieprawidłowy operator logiczny {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Maksymalna liczba warunków zapytania została przekroczona." msgstr "Maksymalna liczba warunków zapytania została przekroczona."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} nie jest prawidłowym polem niestandardowym." msgstr "{name!r} nie jest prawidłowym polem niestandardowym."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} nie obsługuje wyrażenia zapytania {expr!r}." msgstr "{data_type} nie obsługuje wyrażenia zapytania {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Przekroczono maksymalną głębokość zagnieżdżenia." msgstr "Przekroczono maksymalną głębokość zagnieżdżenia."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Nie znaleziono pola niestandardowego" msgstr "Nie znaleziono pola niestandardowego"
@@ -1636,7 +1636,7 @@ msgid "workflow runs"
msgstr "uruchomienia przepływu pracy" msgstr "uruchomienia przepływu pracy"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Niewystarczające uprawnienia." msgstr "Niewystarczające uprawnienia."
@@ -1676,7 +1676,7 @@ msgstr "Wykryto nieprawidłową zmienną."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "Nie dopuszcza się powielania identyfikatorów dokumentów." msgstr "Nie dopuszcza się powielania identyfikatorów dokumentów."
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "Nie znaleziono dokumentów: %(ids)s" msgstr "Nie znaleziono dokumentów: %(ids)s"
@@ -1913,59 +1913,44 @@ msgstr "Zamierzasz użyć konta %(provider_name)s aby się zalogować."
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Na koniec należy wypełnić poniższy formularz:" msgstr "Na koniec należy wypełnić poniższy formularz:"
#: documents/validators.py:24
#, python-brace-format
msgid "Unable to parse URI {value}, missing scheme"
msgstr "Nie można przeanalizować URI {value}, brak schematu"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "Nie można przeanalizować URI {value}, brak lokalizacji sieciowej lub ścieżki"
#: documents/validators.py:36 #: documents/validators.py:36
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}" msgid ", "
msgstr "Schemat URI '{parts.scheme}' jest niedozwolony. Dozwolone schematy: {', '.join(allowed_schemes)}" msgstr ""
#: documents/validators.py:45 #: documents/views.py:337 documents/views.py:2749
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "Nie można przetworzyć URI {value}"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "Nieprawidłowy more_like_id" msgstr "Nieprawidłowy more_like_id"
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "" msgstr ""
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "" msgstr ""
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "" msgstr ""
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Brak uprawnień do udostępnienia dokumentu %(id)s." msgstr "Brak uprawnień do udostępnienia dokumentu %(id)s."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "Pakiet jest już przetwarzany." msgstr "Pakiet jest już przetwarzany."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "Pakiet do udostępnienia jest nadal przygotowywany. Spróbuj ponownie później." msgstr "Pakiet do udostępnienia jest nadal przygotowywany. Spróbuj ponownie później."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "Pakiet do udostępnienia jest niedostępny." msgstr "Pakiet do udostępnienia jest niedostępny."
@@ -2257,151 +2242,151 @@ msgstr ""
msgid "paperless application settings" msgid "paperless application settings"
msgstr "paperless ustawienia aplikacji" msgstr "paperless ustawienia aplikacji"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Angielski (USA)" msgstr "Angielski (USA)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "arabski" msgstr "arabski"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Afrykanerski" msgstr "Afrykanerski"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Białoruski" msgstr "Białoruski"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Bułgarski" msgstr "Bułgarski"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Kataloński" msgstr "Kataloński"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Czeski" msgstr "Czeski"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Duński" msgstr "Duński"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Niemiecki" msgstr "Niemiecki"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Grecki" msgstr "Grecki"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Angielski (Wielka Brytania)" msgstr "Angielski (Wielka Brytania)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Hiszpański" msgstr "Hiszpański"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "perski" msgstr "perski"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Fiński" msgstr "Fiński"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Francuski" msgstr "Francuski"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Węgierski" msgstr "Węgierski"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indonezyjski" msgstr "Indonezyjski"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Włoski" msgstr "Włoski"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japoński" msgstr "Japoński"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Koreański" msgstr "Koreański"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luksemburski" msgstr "Luksemburski"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norweski" msgstr "Norweski"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Holenderski" msgstr "Holenderski"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polski" msgstr "Polski"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Portugalski (Brazylia)" msgstr "Portugalski (Brazylia)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Portugalski" msgstr "Portugalski"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Rumuński" msgstr "Rumuński"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Rosyjski" msgstr "Rosyjski"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Słowacki" msgstr "Słowacki"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Słoweński" msgstr "Słoweński"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Serbski" msgstr "Serbski"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Szwedzki" msgstr "Szwedzki"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turecki" msgstr "Turecki"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ukraiński" msgstr "Ukraiński"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "Wietnamski" msgstr "Wietnamski"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Chiński uproszczony" msgstr "Chiński uproszczony"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "chiński tradycyjny" msgstr "chiński tradycyjny"
+61 -77
View File
@@ -2,8 +2,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: paperless-ngx\n" "Project-Id-Version: paperless-ngx\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-10-05 16:26+0000\n" "POT-Creation-Date: 2026-10-12 19:12+0000\n"
"PO-Revision-Date: 2026-10-05 16:27\n" "PO-Revision-Date: 2026-10-12 19:13\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: Portuguese, Brazilian\n" "Language-Team: Portuguese, Brazilian\n"
"Language: pt_BR\n" "Language: pt_BR\n"
@@ -21,39 +21,39 @@ msgstr ""
msgid "Documents" msgid "Documents"
msgstr "Documentos" msgstr "Documentos"
#: documents/filters.py:463 #: documents/filters.py:464
msgid "Value must be valid JSON." msgid "Value must be valid JSON."
msgstr "O valor deve ser um JSON válido." msgstr "O valor deve ser um JSON válido."
#: documents/filters.py:482 #: documents/filters.py:483
msgid "Invalid custom field query expression" msgid "Invalid custom field query expression"
msgstr "Expressão de consulta de campo personalizado inválida" msgstr "Expressão de consulta de campo personalizado inválida"
#: documents/filters.py:492 #: documents/filters.py:493
msgid "Invalid expression list. Must be nonempty." msgid "Invalid expression list. Must be nonempty."
msgstr "Lista de expressões inválida. Deve estar não vazia." msgstr "Lista de expressões inválida. Deve estar não vazia."
#: documents/filters.py:513 #: documents/filters.py:514
msgid "Invalid logical operator {op!r}" msgid "Invalid logical operator {op!r}"
msgstr "Operador lógico inválido {op!r}" msgstr "Operador lógico inválido {op!r}"
#: documents/filters.py:527 #: documents/filters.py:528
msgid "Maximum number of query conditions exceeded." msgid "Maximum number of query conditions exceeded."
msgstr "Número máximo de condições de consulta excedido." msgstr "Número máximo de condições de consulta excedido."
#: documents/filters.py:591 #: documents/filters.py:592
msgid "{name!r} is not a valid custom field." msgid "{name!r} is not a valid custom field."
msgstr "{name!r} não é um campo personalizado válido." msgstr "{name!r} não é um campo personalizado válido."
#: documents/filters.py:628 #: documents/filters.py:629
msgid "{data_type} does not support query expr {expr!r}." msgid "{data_type} does not support query expr {expr!r}."
msgstr "{data_type} não suporta a consulta expr {expr!r}." msgstr "{data_type} não suporta a consulta expr {expr!r}."
#: documents/filters.py:747 documents/models.py:137 #: documents/filters.py:748 documents/models.py:137
msgid "Maximum nesting depth exceeded." msgid "Maximum nesting depth exceeded."
msgstr "Profundidade máxima do aninhamento excedida." msgstr "Profundidade máxima do aninhamento excedida."
#: documents/filters.py:1109 #: documents/filters.py:1125
msgid "Custom field not found" msgid "Custom field not found"
msgstr "Campo personalizado não encontrado" msgstr "Campo personalizado não encontrado"
@@ -1637,7 +1637,7 @@ msgid "workflow runs"
msgstr "execução de fluxo de trabalho" msgstr "execução de fluxo de trabalho"
#: documents/serialisers.py:515 documents/serialisers.py:872 #: documents/serialisers.py:515 documents/serialisers.py:872
#: documents/serialisers.py:2902 documents/views.py:343 documents/views.py:2732 #: documents/serialisers.py:2902 documents/views.py:344 documents/views.py:2752
#: paperless_mail/serialisers.py:156 #: paperless_mail/serialisers.py:156
msgid "Insufficient permissions." msgid "Insufficient permissions."
msgstr "Permissões insuficientes." msgstr "Permissões insuficientes."
@@ -1677,7 +1677,7 @@ msgstr "Variável inválida detectada."
msgid "Duplicate document identifiers are not allowed." msgid "Duplicate document identifiers are not allowed."
msgstr "Não são permitidos identificadores de documento duplicados." msgstr "Não são permitidos identificadores de documento duplicados."
#: documents/serialisers.py:2988 documents/views.py:4787 #: documents/serialisers.py:2988 documents/views.py:4799
#, python-format #, python-format
msgid "Documents not found: %(ids)s" msgid "Documents not found: %(ids)s"
msgstr "Documentos não encontrados: %(ids)s" msgstr "Documentos não encontrados: %(ids)s"
@@ -1914,60 +1914,44 @@ msgstr "Você está prestes a usar sua conta %(provider_name)s para entrar."
msgid "As a final step, please complete the following form:" msgid "As a final step, please complete the following form:"
msgstr "Como etapa final, por favor preencha o seguinte formulário:" msgstr "Como etapa final, por favor preencha o seguinte formulário:"
#: documents/validators.py:24 #: documents/validators.py:36
#, python-brace-format msgid ", "
msgid "Unable to parse URI {value}, missing scheme"
msgstr "Não é possível analisar o URI {value}, esquema ausente"
#: documents/validators.py:29
#, python-brace-format
msgid "Unable to parse URI {value}, missing net location or path"
msgstr "" msgstr ""
#: documents/validators.py:36 #: documents/views.py:337 documents/views.py:2749
msgid "URI scheme '{parts.scheme}' is not allowed. Allowed schemes: {', '.join(allowed_schemes)}"
msgstr "Esquema URI '{parts.scheme}' não é permitido. Esquemas permitidos:\n"
"{', '.join(allowed_schemes)}"
#: documents/validators.py:45
#, python-brace-format
msgid "Unable to parse URI {value}"
msgstr "Não é possível analisar o URI {value}"
#: documents/views.py:336 documents/views.py:2729
msgid "Invalid more_like_id" msgid "Invalid more_like_id"
msgstr "more_like_id inválido" msgstr "more_like_id inválido"
#: documents/views.py:1676 #: documents/views.py:1684
msgid "Invalid AI configuration." msgid "Invalid AI configuration."
msgstr "Configuração de IA inválida." msgstr "Configuração de IA inválida."
#: documents/views.py:1687 #: documents/views.py:1695
msgid "AI backend request timed out." msgid "AI backend request timed out."
msgstr "A requisição backend da IA expirou." msgstr "A requisição backend da IA expirou."
#: documents/views.py:1699 #: documents/views.py:1707
msgid "AI backend rejected the request. Check logs for details." msgid "AI backend rejected the request. Check logs for details."
msgstr "" msgstr ""
#: documents/views.py:2554 documents/views.py:2870 #: documents/views.py:2574 documents/views.py:2890
msgid "Specify only one of text, title_search, query, or more_like_id." msgid "Specify only one of text, title_search, query, or more_like_id."
msgstr "Especifique apenas um text, title_search, consulta ou mais_like_id." msgstr "Especifique apenas um text, title_search, consulta ou mais_like_id."
#: documents/views.py:4800 #: documents/views.py:4815
#, python-format #, python-format
msgid "Insufficient permissions to share document %(id)s." msgid "Insufficient permissions to share document %(id)s."
msgstr "Permissões insuficientes para compartilhar o documento %(id)s." msgstr "Permissões insuficientes para compartilhar o documento %(id)s."
#: documents/views.py:4846 #: documents/views.py:4862
msgid "Bundle is already being processed." msgid "Bundle is already being processed."
msgstr "Pacote já está sendo processado." msgstr "Pacote já está sendo processado."
#: documents/views.py:4910 #: documents/views.py:4926
msgid "The share link bundle is still being prepared. Please try again later." msgid "The share link bundle is still being prepared. Please try again later."
msgstr "O pacote de link de compartilhamento ainda está sendo preparado. Tente novamente mais tarde." msgstr "O pacote de link de compartilhamento ainda está sendo preparado. Tente novamente mais tarde."
#: documents/views.py:4924 #: documents/views.py:4940
msgid "The share link bundle is unavailable." msgid "The share link bundle is unavailable."
msgstr "O pacote de link de compartilhamento não está disponível." msgstr "O pacote de link de compartilhamento não está disponível."
@@ -2259,151 +2243,151 @@ msgstr "Define o tempo limite do LLM em segundos"
msgid "paperless application settings" msgid "paperless application settings"
msgstr "configurações do aplicativo paperless" msgstr "configurações do aplicativo paperless"
#: paperless/settings/__init__.py:561 #: paperless/settings/__init__.py:562
msgid "English (US)" msgid "English (US)"
msgstr "Inglês (EUA)" msgstr "Inglês (EUA)"
#: paperless/settings/__init__.py:562 #: paperless/settings/__init__.py:563
msgid "Arabic" msgid "Arabic"
msgstr "Árabe" msgstr "Árabe"
#: paperless/settings/__init__.py:563 #: paperless/settings/__init__.py:564
msgid "Afrikaans" msgid "Afrikaans"
msgstr "Africanês" msgstr "Africanês"
#: paperless/settings/__init__.py:564 #: paperless/settings/__init__.py:565
msgid "Belarusian" msgid "Belarusian"
msgstr "Bielorrusso" msgstr "Bielorrusso"
#: paperless/settings/__init__.py:565 #: paperless/settings/__init__.py:566
msgid "Bulgarian" msgid "Bulgarian"
msgstr "Búlgaro" msgstr "Búlgaro"
#: paperless/settings/__init__.py:566 #: paperless/settings/__init__.py:567
msgid "Catalan" msgid "Catalan"
msgstr "Catalão" msgstr "Catalão"
#: paperless/settings/__init__.py:567 #: paperless/settings/__init__.py:568
msgid "Czech" msgid "Czech"
msgstr "Tcheco" msgstr "Tcheco"
#: paperless/settings/__init__.py:568 #: paperless/settings/__init__.py:569
msgid "Danish" msgid "Danish"
msgstr "Dinamarquês" msgstr "Dinamarquês"
#: paperless/settings/__init__.py:569 #: paperless/settings/__init__.py:570
msgid "German" msgid "German"
msgstr "Alemão" msgstr "Alemão"
#: paperless/settings/__init__.py:570 #: paperless/settings/__init__.py:571
msgid "Greek" msgid "Greek"
msgstr "Grego" msgstr "Grego"
#: paperless/settings/__init__.py:571 #: paperless/settings/__init__.py:572
msgid "English (GB)" msgid "English (GB)"
msgstr "Inglês (GB)" msgstr "Inglês (GB)"
#: paperless/settings/__init__.py:572 #: paperless/settings/__init__.py:573
msgid "Spanish" msgid "Spanish"
msgstr "Espanhol" msgstr "Espanhol"
#: paperless/settings/__init__.py:573 #: paperless/settings/__init__.py:574
msgid "Persian" msgid "Persian"
msgstr "Persa" msgstr "Persa"
#: paperless/settings/__init__.py:574 #: paperless/settings/__init__.py:575
msgid "Finnish" msgid "Finnish"
msgstr "Finlandês" msgstr "Finlandês"
#: paperless/settings/__init__.py:575 #: paperless/settings/__init__.py:576
msgid "French" msgid "French"
msgstr "Francês" msgstr "Francês"
#: paperless/settings/__init__.py:576 #: paperless/settings/__init__.py:577
msgid "Hungarian" msgid "Hungarian"
msgstr "Húngaro" msgstr "Húngaro"
#: paperless/settings/__init__.py:577 #: paperless/settings/__init__.py:578
msgid "Indonesian" msgid "Indonesian"
msgstr "Indonésio" msgstr "Indonésio"
#: paperless/settings/__init__.py:578 #: paperless/settings/__init__.py:579
msgid "Italian" msgid "Italian"
msgstr "Italiano" msgstr "Italiano"
#: paperless/settings/__init__.py:579 #: paperless/settings/__init__.py:580
msgid "Japanese" msgid "Japanese"
msgstr "Japonês" msgstr "Japonês"
#: paperless/settings/__init__.py:580 #: paperless/settings/__init__.py:581
msgid "Korean" msgid "Korean"
msgstr "Coreano" msgstr "Coreano"
#: paperless/settings/__init__.py:581 #: paperless/settings/__init__.py:582
msgid "Luxembourgish" msgid "Luxembourgish"
msgstr "Luxemburguês" msgstr "Luxemburguês"
#: paperless/settings/__init__.py:582 #: paperless/settings/__init__.py:583
msgid "Norwegian" msgid "Norwegian"
msgstr "Norueguês" msgstr "Norueguês"
#: paperless/settings/__init__.py:583 #: paperless/settings/__init__.py:584
msgid "Dutch" msgid "Dutch"
msgstr "Holandês" msgstr "Holandês"
#: paperless/settings/__init__.py:584 #: paperless/settings/__init__.py:585
msgid "Polish" msgid "Polish"
msgstr "Polonês" msgstr "Polonês"
#: paperless/settings/__init__.py:585 #: paperless/settings/__init__.py:586
msgid "Portuguese (Brazil)" msgid "Portuguese (Brazil)"
msgstr "Português (Brasil)" msgstr "Português (Brasil)"
#: paperless/settings/__init__.py:586 #: paperless/settings/__init__.py:587
msgid "Portuguese" msgid "Portuguese"
msgstr "Português" msgstr "Português"
#: paperless/settings/__init__.py:587 #: paperless/settings/__init__.py:588
msgid "Romanian" msgid "Romanian"
msgstr "Romeno" msgstr "Romeno"
#: paperless/settings/__init__.py:588 #: paperless/settings/__init__.py:589
msgid "Russian" msgid "Russian"
msgstr "Russo" msgstr "Russo"
#: paperless/settings/__init__.py:589 #: paperless/settings/__init__.py:590
msgid "Slovak" msgid "Slovak"
msgstr "Eslovaco" msgstr "Eslovaco"
#: paperless/settings/__init__.py:590 #: paperless/settings/__init__.py:591
msgid "Slovenian" msgid "Slovenian"
msgstr "Esloveno" msgstr "Esloveno"
#: paperless/settings/__init__.py:591 #: paperless/settings/__init__.py:592
msgid "Serbian" msgid "Serbian"
msgstr "Sérvio" msgstr "Sérvio"
#: paperless/settings/__init__.py:592 #: paperless/settings/__init__.py:593
msgid "Swedish" msgid "Swedish"
msgstr "Sueco" msgstr "Sueco"
#: paperless/settings/__init__.py:593 #: paperless/settings/__init__.py:594
msgid "Turkish" msgid "Turkish"
msgstr "Turco" msgstr "Turco"
#: paperless/settings/__init__.py:594 #: paperless/settings/__init__.py:595
msgid "Ukrainian" msgid "Ukrainian"
msgstr "Ucraniano" msgstr "Ucraniano"
#: paperless/settings/__init__.py:595 #: paperless/settings/__init__.py:596
msgid "Vietnamese" msgid "Vietnamese"
msgstr "vietnamita" msgstr "vietnamita"
#: paperless/settings/__init__.py:596 #: paperless/settings/__init__.py:597
msgid "Chinese Simplified" msgid "Chinese Simplified"
msgstr "Chinês Simplificado" msgstr "Chinês Simplificado"
#: paperless/settings/__init__.py:597 #: paperless/settings/__init__.py:598
msgid "Chinese Traditional" msgid "Chinese Traditional"
msgstr "Chinês Tradicional" msgstr "Chinês Tradicional"
Loaded 100 of 130 files, more files were not shown because too many files have changed in this diff. Show more