From f699f25419b8eaf270672e3759630561e07a26be Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Wed, 24 Jun 2026 19:40:32 +0300 Subject: [PATCH 1/4] Add boolean fields to Project --- ..._project_set_locales_from_repo_and_more.py | 28 +++++++++++++++++++ pontoon/base/models/project.py | 21 ++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 pontoon/base/migrations/0119_project_set_locales_from_repo_and_more.py diff --git a/pontoon/base/migrations/0119_project_set_locales_from_repo_and_more.py b/pontoon/base/migrations/0119_project_set_locales_from_repo_and_more.py new file mode 100644 index 0000000000..b25855c61c --- /dev/null +++ b/pontoon/base/migrations/0119_project_set_locales_from_repo_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.14 on 2026-06-24 09:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("base", "0118_fix_terminology_entity_value"), + ] + + operations = [ + migrations.AddField( + model_name="project", + name="set_locales_from_repo", + field=models.BooleanField( + default=False, + help_text="\n Control target locales by their directories' presence in the target repository,\n or in the root locales config if `configuration_file` is not None.\n ", + ), + ), + migrations.AddField( + model_name="project", + name="set_translated_resources_from_repo", + field=models.BooleanField( + default=False, + help_text="\n If `set_locales_from_repo` is True and `configuration_file` is None,\n setting `set_translated_resources_from_repo` to True will\n control the availability of each resource for translation in each locale\n according to the presence of a file (even if empty) at its expected target path.\n\n If `set_locales_from_repo` is False or `configuration_file` is not None,\n this control has no effect.\n ", + ), + ), + ] diff --git a/pontoon/base/models/project.py b/pontoon/base/models/project.py index 574ce8b77e..62f6af023e 100644 --- a/pontoon/base/models/project.py +++ b/pontoon/base/models/project.py @@ -237,6 +237,27 @@ class Visibility(models.TextChoices): """, ) + set_locales_from_repo = models.BooleanField( + default=False, + help_text=""" + Control target locales by their directories' presence in the target repository, + or in the root locales config if `configuration_file` is not None. + """, + ) + + set_translated_resources_from_repo = models.BooleanField( + default=False, + help_text=""" + If `set_locales_from_repo` is True and `configuration_file` is None, + setting `set_translated_resources_from_repo` to True will + control the availability of each resource for translation in each locale + according to the presence of a file (even if empty) at its expected target path. + + If `set_locales_from_repo` is False or `configuration_file` is not None, + this control has no effect. + """, + ) + objects = ProjectQuerySet.as_manager() project_locale: "ProjectLocaleQuerySet" From 7706e4c4067721c915f471fc47aa32fe9d99513b Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Wed, 24 Jun 2026 19:42:56 +0300 Subject: [PATCH 2/4] Add new fields to project admin form --- pontoon/administration/forms.py | 5 +- .../static/css/admin_project.css | 4 + .../administration/static/js/admin_project.js | 51 ++++++++++- .../templates/admin_project.html | 24 ++++- pontoon/administration/views.py | 90 ++++++++++++------- 5 files changed, 137 insertions(+), 37 deletions(-) diff --git a/pontoon/administration/forms.py b/pontoon/administration/forms.py index b549daf643..1f9b16989f 100644 --- a/pontoon/administration/forms.py +++ b/pontoon/administration/forms.py @@ -38,9 +38,10 @@ class ProjectForm(forms.ModelForm): def clean(self): cleaned_data = super().clean() + set_locales_from_repo = cleaned_data.get("set_locales_from_repo") locales_readonly = cleaned_data.get("locales_readonly") locales = cleaned_data.get("locales") - if not (locales or locales_readonly): + if not (set_locales_from_repo or locales or locales_readonly): raise ValidationError("At least one locale must be selected.") class Meta: @@ -61,6 +62,8 @@ class Meta: "sync_disabled", "tags_enabled", "pretranslation_enabled", + "set_locales_from_repo", + "set_translated_resources_from_repo", "visibility", ) diff --git a/pontoon/administration/static/css/admin_project.css b/pontoon/administration/static/css/admin_project.css index ad3ba79a78..35db61b90b 100644 --- a/pontoon/administration/static/css/admin_project.css +++ b/pontoon/administration/static/css/admin_project.css @@ -184,6 +184,10 @@ form a:visited { text-transform: uppercase; } +.locales { + margin-top: 40px; +} + .double-list-selector .locale.select .menu { background: transparent; border-bottom: 1px solid var(--main-border-1); diff --git a/pontoon/administration/static/js/admin_project.js b/pontoon/administration/static/js/admin_project.js index 4b19493a2d..0c096873be 100644 --- a/pontoon/administration/static/js/admin_project.js +++ b/pontoon/administration/static/js/admin_project.js @@ -76,8 +76,9 @@ $(function () { button.addClass('in-progress').html('Syncing...'); + const slug = $('#id_slug').val(); $.ajax({ - url: '/admin/projects/' + $('#id_slug').val() + '/sync/', + url: `/admin/projects/${slug}/sync/`, success: function () { button.html('Started'); }, @@ -105,8 +106,9 @@ $(function () { button.addClass('in-progress').html('Pretranslating...'); + const slug = $('#id_slug').val(); $.ajax({ - url: '/admin/projects/' + $('#id_slug').val() + '/pretranslate/', + url: `/admin/projects/${slug}/pretranslate/`, success: function () { button.html('Started'); }, @@ -166,6 +168,51 @@ $(function () { }); }); + const setLocalesCheckbox = $('#id_set_locales_from_repo'); + const configFileInput = $('#id_configuration_file'); + function setLocalesUpdate() { + const configFile = configFileInput.val(); + let reqSync = configFile !== configFileInput.data('init'); + + const controls = $('.repo-locale-control'); + if ($('#id_data_source').val() === 'repository') { + controls.show(); + } else { + controls.hide(); + setLocalesCheckbox.prop('checked', false); + reqSync = false; + } + + const label = $('#id_set_locales_from_repo + span'); + const hint = $('#hint_set_locales_from_repo'); + if (configFile) { + label.text('Read locales from configuration file'); + hint.hide(); + } else { + label.text('Set locales from repository'); + hint.show(); + } + + const setLocalesFromRepo = setLocalesCheckbox.prop('checked'); + const trDiv = $('#translated_resources_from_repo'); + const locales = $('.locales, .locales-toolbar'); + if (setLocalesFromRepo) { + trDiv.toggle(!configFile); + locales.hide(); + reqSync ||= setLocalesFromRepo !== setLocalesCheckbox.data('init'); + } else { + trDiv.hide(); + locales.show(); + } + + $('button.save').text(reqSync ? 'Save & sync project' : 'Save project'); + } + setLocalesCheckbox.data('init', setLocalesCheckbox.prop('checked')); + setLocalesCheckbox.on('change', setLocalesUpdate); + configFileInput.data('init', configFileInput.val()); + configFileInput.on('change', setLocalesUpdate); + setLocalesUpdate(); + self.NProgressUnbind(); // Set locales to existing projects to be copied to the current project diff --git a/pontoon/administration/templates/admin_project.html b/pontoon/administration/templates/admin_project.html index 55dfcddaff..cbcd143f02 100644 --- a/pontoon/administration/templates/admin_project.html +++ b/pontoon/administration/templates/admin_project.html @@ -54,7 +54,29 @@

Locales

-
+
+ + Locale is available if target directory is present. +
+ + + +