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)