From b02a5904c60563559ae051c9cfe10ad691046774 Mon Sep 17 00:00:00 2001 From: gersona Date: Tue, 8 Oct 2024 13:54:54 +0300 Subject: [PATCH 1/3] bilingual file upload implementation --- weblate/trans/forms.py | 7 +++++++ weblate/trans/tests/test_create.py | 23 ++++++++++++++++++++++- weblate/trans/views/create.py | 6 ++++-- weblate/utils/views.py | 17 +++++++++++------ 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/weblate/trans/forms.py b/weblate/trans/forms.py index d3e00b2bbe40..2f9541be1e07 100644 --- a/weblate/trans/forms.py +++ b/weblate/trans/forms.py @@ -1850,6 +1850,13 @@ class ComponentDocCreateForm(ComponentProjectForm): validators=[validate_file_extension], ) + target_language = forms.ModelChoiceField( + widget=SortedSelect, + label=gettext_lazy("Target language"), + help_text=gettext_lazy("Target language of the document for bilingual files"), + queryset=Language.objects.all(), + required=False, + ) field_order = ["docfile", "project", "name", "slug"] def __init__(self, *args, **kwargs) -> None: diff --git a/weblate/trans/tests/test_create.py b/weblate/trans/tests/test_create.py index 5504425ea412..b63f7fb71053 100644 --- a/weblate/trans/tests/test_create.py +++ b/weblate/trans/tests/test_create.py @@ -8,13 +8,14 @@ from django.test.utils import modify_settings, override_settings from django.urls import reverse -from weblate.lang.models import get_default_lang +from weblate.lang.models import Language, get_default_lang from weblate.trans.tests.test_views import ViewTestCase from weblate.trans.tests.utils import create_test_billing, get_test_file from weblate.vcs.git import GitRepository TEST_ZIP = get_test_file("translations.zip") TEST_HTML = get_test_file("cs.html") +TEST_PO = get_test_file("cs.po") class CreateTest(ViewTestCase): @@ -370,6 +371,26 @@ def test_create_doc_category(self) -> None: self.assertContains(response, "Adding new translation") self.assertContains(response, "*.html") + @modify_settings(INSTALLED_APPS={"remove": "weblate.billing"}) + def test_create_doc_bilingual(self) -> None: + self.user.is_superuser = True + self.user.save() + + with open(TEST_PO) as handle, override_settings(CREATE_GLOSSARIES=False): + response = self.client.post( + reverse("create-component-doc"), + { + "docfile": handle, + "name": "Bilingual Component From Doc", + "slug": "bilingual-component-from-doc", + "project": self.project.pk, + "source_language": get_default_lang(), + "target_language": Language.objects.get(code="cs").id, + }, + ) + self.assertContains(response, "Choose translation files to import") + self.assertNotContains(response, "gettext PO file (monolingual)") + @modify_settings(INSTALLED_APPS={"remove": "weblate.billing"}) def test_create_scratch(self) -> None: @override_settings(CREATE_GLOSSARIES=self.CREATE_GLOSSARIES) diff --git a/weblate/trans/views/create.py b/weblate/trans/views/create.py index a28b39e2680f..b22b50a54765 100644 --- a/weblate/trans/views/create.py +++ b/weblate/trans/views/create.py @@ -415,9 +415,11 @@ def form_valid(self, form): return super().form_valid(form) fake = create_component_from_doc( - form.cleaned_data, form.cleaned_data.pop("docfile") + form.cleaned_data, + form.cleaned_data.pop("docfile"), + form.cleaned_data.pop("target_language", None), ) - + # QUESTION: POP target_language from request here ? or put in initial ? or from cleand_data # Move to discover phase self.stage = "discover" self.initial = form.cleaned_data diff --git a/weblate/utils/views.py b/weblate/utils/views.py index 5f3e43dbca11..567d6f2c8c66 100644 --- a/weblate/utils/views.py +++ b/weblate/utils/views.py @@ -386,26 +386,31 @@ def guess_filemask_from_doc(data, docfile=None) -> None: data["filemask"] = "{}/{}{}".format(data.get("slug", "translations"), "*", ext) -def create_component_from_doc(data, docfile): +def create_component_from_doc(data, docfile, target_language: Language | None = None): # Calculate filename uploaded = docfile or data["docfile"] guess_filemask_from_doc(data, uploaded) filemask = data["filemask"] - filename = filemask.replace( - "*", - data["source_language"].code + file_language_code = ( + target_language.code + if target_language # bilingual file + else data["source_language"].code if "source_language" in data - else settings.DEFAULT_LANGUAGE, + else settings.DEFAULT_LANGUAGE ) + filename = filemask.replace("*", file_language_code) # Create fake component (needed to calculate path) fake = Component( project=data["project"], slug=data["slug"], name=data["name"], category=data.get("category", None), - template=filename, filemask=filemask, ) + + if not target_language: + fake.template = filename + # Create repository LocalRepository.from_files(fake.full_path, {filename: uploaded.read()}) return fake From 5c5322c8f98c133dd3853f3b471d8d3217f59c37 Mon Sep 17 00:00:00 2001 From: gersona Date: Tue, 8 Oct 2024 13:56:15 +0300 Subject: [PATCH 2/3] document update --- docs/changes.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changes.rst b/docs/changes.rst index 68b057617aa9..fc6e0ec47f2b 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -22,6 +22,7 @@ Not yet released. * :kbd:`?` now displays available :ref:`keyboard`. * Translation and language view in the project now include basic information about the language and plurals. * :ref:`bulk-edit` shows a preview of matched strings. +* Creating component via file upload (Translate document) now supports bilingual files. **Bug fixes** From afca3de175d6408400262d28be57572d3891d133 Mon Sep 17 00:00:00 2001 From: gersona Date: Tue, 8 Oct 2024 14:10:33 +0300 Subject: [PATCH 3/3] clean debugging comment --- weblate/trans/views/create.py | 1 - 1 file changed, 1 deletion(-) diff --git a/weblate/trans/views/create.py b/weblate/trans/views/create.py index b22b50a54765..9510a624baf3 100644 --- a/weblate/trans/views/create.py +++ b/weblate/trans/views/create.py @@ -419,7 +419,6 @@ def form_valid(self, form): form.cleaned_data.pop("docfile"), form.cleaned_data.pop("target_language", None), ) - # QUESTION: POP target_language from request here ? or put in initial ? or from cleand_data # Move to discover phase self.stage = "discover" self.initial = form.cleaned_data