diff --git a/src-ui/src/app/components/document-detail/document-detail.component.html b/src-ui/src/app/components/document-detail/document-detail.component.html
index 64646c0b1..fa77df0c0 100644
--- a/src-ui/src/app/components/document-detail/document-detail.component.html
+++ b/src-ui/src/app/components/document-detail/document-detail.component.html
@@ -199,6 +199,10 @@
+ Translation
+
+
+
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts
index b26ad9024..637514900 100644
--- a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts
+++ b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts
@@ -86,6 +86,7 @@ const doc: Document = {
storage_path: 31,
tags: [41, 42, 43],
content: 'text content',
+ translation: 'text content',
added: new Date('May 4, 2014 03:24:00'),
created: new Date('May 4, 2014 03:24:00'),
modified: new Date('May 4, 2014 03:24:00'),
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts
index d8f63faf2..cba8579f9 100644
--- a/src-ui/src/app/components/document-detail/document-detail.component.ts
+++ b/src-ui/src/app/components/document-detail/document-detail.component.ts
@@ -138,6 +138,7 @@ export class DocumentDetailComponent
documentForm: FormGroup = new FormGroup({
title: new FormControl(''),
content: new FormControl(''),
+ translation: new FormControl(''),
created_date: new FormControl(),
correspondent: new FormControl(),
document_type: new FormControl(),
@@ -404,6 +405,7 @@ export class DocumentDetailComponent
this.store = new BehaviorSubject({
title: doc.title,
content: doc.content,
+ translation: doc.translation,
created_date: doc.created_date,
correspondent: doc.correspondent,
document_type: doc.document_type,
diff --git a/src-ui/src/app/data/document.ts b/src-ui/src/app/data/document.ts
index 910666f10..a667f496a 100644
--- a/src-ui/src/app/data/document.ts
+++ b/src-ui/src/app/data/document.ts
@@ -32,6 +32,8 @@ export interface Document extends ObjectWithPermissions {
content?: string
+ translation?: string
+
tags$?: Observable
tags?: number[]
diff --git a/src/documents/filters.py b/src/documents/filters.py
index 891f20dde..873a00ab0 100644
--- a/src/documents/filters.py
+++ b/src/documents/filters.py
@@ -104,7 +104,11 @@ class InboxFilter(Filter):
class TitleContentFilter(Filter):
def filter(self, qs, value):
if value:
- return qs.filter(Q(title__icontains=value) | Q(content__icontains=value))
+ return qs.filter(
+ Q(title__icontains=value)
+ | Q(translation__icontains=value)
+ | Q(content__icontains=value),
+ )
else:
return qs
diff --git a/src/documents/migrations/1047_document_translation.py b/src/documents/migrations/1047_document_translation.py
new file mode 100644
index 000000000..09c2cf5bf
--- /dev/null
+++ b/src/documents/migrations/1047_document_translation.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.11 on 2024-04-11 18:24
+
+from django.db import migrations
+from django.db import models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("documents", "1046_workflowaction_remove_all_correspondents_and_more"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="document",
+ name="translation",
+ field=models.TextField(
+ blank=True,
+ help_text="The translated version of the content field. This field can also be used for searching.",
+ verbose_name="translation",
+ ),
+ ),
+ ]
diff --git a/src/documents/models.py b/src/documents/models.py
index 8e7a16a60..682979062 100644
--- a/src/documents/models.py
+++ b/src/documents/models.py
@@ -176,6 +176,15 @@ class Document(ModelWithOwner):
),
)
+ translation = models.TextField(
+ _("translation"),
+ blank=True,
+ help_text=_(
+ "The translated version of the content field. This field can "
+ "also be used for searching.",
+ ),
+ )
+
mime_type = models.CharField(_("mime type"), max_length=256, editable=False)
tags = models.ManyToManyField(
diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py
index 26930ccec..042c17a30 100644
--- a/src/documents/serialisers.py
+++ b/src/documents/serialisers.py
@@ -772,6 +772,7 @@ class DocumentSerializer(
"storage_path",
"title",
"content",
+ "translation",
"tags",
"created",
"created_date",
diff --git a/src/documents/tasks.py b/src/documents/tasks.py
index 1bc812bfd..775ab017f 100644
--- a/src/documents/tasks.py
+++ b/src/documents/tasks.py
@@ -6,6 +6,7 @@ from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Optional
+import requests
import tqdm
from celery import Task
from celery import shared_task
@@ -49,6 +50,29 @@ if settings.AUDIT_LOG_ENABLED:
logger = logging.getLogger("paperless.tasks")
+def translate_content(content):
+
+ headers = {
+ "Authorization": "DeepL-Auth-Key " + settings.DEEPL_TOKEN,
+ "Content-Type": "application/json",
+ }
+
+ json_data = {
+ "text": [
+ content,
+ ],
+ "target_lang": settings.TRANSLATION_TARGET,
+ }
+
+ response = requests.post(
+ "https://api-free.deepl.com/v2/translate",
+ headers=headers,
+ json=json_data,
+ )
+
+ return response.json()["translations"][0]["text"]
+
+
@shared_task
def index_optimize():
ix = index.open_index()
@@ -243,9 +267,11 @@ def update_document_archive_file(document_id):
archive_filename=True,
)
oldDocument = Document.objects.get(pk=document.pk)
+ content = parser.get_text()
Document.objects.filter(pk=document.pk).update(
archive_checksum=checksum,
- content=parser.get_text(),
+ content=content,
+ translation=translate_content(content),
archive_filename=document.archive_filename,
)
newDocument = Document.objects.get(pk=document.pk)