diff --git a/app/eventyay/base/configurations/default_setting.py b/app/eventyay/base/configurations/default_setting.py index c3cb1be23e..297947095a 100644 --- a/app/eventyay/base/configurations/default_setting.py +++ b/app/eventyay/base/configurations/default_setting.py @@ -849,7 +849,24 @@ def primary_font_kwargs(): choices=settings.LANGUAGES, widget=MultipleLanguagesWidget, required=True, - label=_('Available languages'), + label=_('Active languages'), + ), + }, + 'content_locales': { + 'default': json.dumps([settings.LANGUAGE_CODE]), + 'type': list, + 'serializer_class': ListMultipleChoiceField, + 'serializer_kwargs': dict( + choices=settings.LANGUAGES, + required=True, + ), + 'form_class': forms.MultipleChoiceField, + 'form_kwargs': dict( + choices=settings.LANGUAGES, + widget=MultipleLanguagesWidget, + required=True, + label=_('Content languages'), + help_text=_('Languages that speakers can select for their submissions. Content languages should be a subset of active languages.'), ), }, 'locale': { diff --git a/app/eventyay/base/models/event.py b/app/eventyay/base/models/event.py index 58d13bde06..1ab227677a 100644 --- a/app/eventyay/base/models/event.py +++ b/app/eventyay/base/models/event.py @@ -2080,12 +2080,53 @@ def clean_presale(presale_start, presale_end): @cached_property def locales(self) -> list[str]: """Is a list of active event locales.""" - return self.locale_array.split(',') + if hasattr(self, 'settings') and 'locales' in self.settings._cache(): + if locales := self.settings.get('locales', as_type=list): + return locales + return [code for code in self.locale_array.split(',') if code] @cached_property def content_locales(self) -> list[str]: """Is a list of active content locales.""" - return self.content_locale_array.split(',') + if hasattr(self, 'settings') and 'content_locales' in self.settings._cache(): + if locales := self.settings.get('content_locales', as_type=list): + return locales + fallback = [code for code in self.content_locale_array.split(',') if code] + return fallback or self.locales + + def _clear_language_caches(self): + for attr in [ + 'locales', + 'content_locales', + 'is_multilingual', + 'named_locales', + 'available_content_locales', + 'named_content_locales', + 'named_plugin_locales', + 'plugin_locales', + ]: + self.__dict__.pop(attr, None) + + def update_language_configuration( + self, + *, + locales: list[str] | None = None, + content_locales: list[str] | None = None, + default_locale: str | None = None, + ) -> None: + locales_list = list(locales or []) + if content_locales is None: + content_locales_list = locales_list + else: + content_locales_list = list(content_locales) + if locales_list: + self.locale_array = ','.join(locales_list) + if content_locales_list: + self.content_locale_array = ','.join(content_locales_list) + if default_locale: + self.locale = default_locale + if locales_list or content_locales_list or default_locale: + self._clear_language_caches() @cached_property def is_multilingual(self) -> bool: diff --git a/app/eventyay/base/settings.py b/app/eventyay/base/settings.py index e7a14c00e1..0fb19379e6 100644 --- a/app/eventyay/base/settings.py +++ b/app/eventyay/base/settings.py @@ -113,8 +113,20 @@ def validate_event_settings(event, settings_dict): default_locale = settings_dict.get('locale') locales = settings_dict.get('locales', []) + if not isinstance(locales, list): + locales = list(locales) if default_locale and default_locale not in locales: raise ValidationError({'locale': _('Your default locale must also be enabled for your event (see box above).')}) + content_locales = settings_dict.get('content_locales') + if content_locales is None: + content_locales = locales + elif not isinstance(content_locales, list): + content_locales = list(content_locales) + if content_locales: + if invalid_content_locales := set(content_locales) - set(locales): + raise ValidationError( + {'content_locales': _('Content languages must be a subset of the active languages.')} + ) if settings_dict.get('attendee_names_required') and not settings_dict.get('attendee_names_asked'): raise ValidationError( {'attendee_names_required': _('You cannot require specifying attendee names if you do not ask for them.')} diff --git a/app/eventyay/config/settings.py b/app/eventyay/config/settings.py index e53cbf36b8..aec71f45f8 100644 --- a/app/eventyay/config/settings.py +++ b/app/eventyay/config/settings.py @@ -455,6 +455,8 @@ def instance_name(request): ('fi', _('Finnish')), ('el', _('Greek')), ('it', _('Italian')), + ('id', _('Indonesian')), + ('ko', _('Korean')), ('lv', _('Latvian')), ('pl', _('Polish')), ('pt-pt', _('Portuguese (Portugal)')), @@ -624,6 +626,12 @@ def instance_name(request): 'official': False, 'percentage': 95, }, + 'id': { + 'name': _('Indonesian'), + 'natural_name': 'Bahasa Indonesia', + 'official': False, + 'percentage': 90, + }, 'ja-jp': { 'name': _('Japanese'), 'natural_name': '日本語', @@ -631,6 +639,12 @@ def instance_name(request): 'percentage': 69, 'public_code': 'jp', }, + 'ko': { + 'name': _('Korean'), + 'natural_name': '한국어', + 'official': False, + 'percentage': 88, + }, 'nl': { 'name': _('Dutch'), 'natural_name': 'Nederlands', diff --git a/app/eventyay/control/views/event.py b/app/eventyay/control/views/event.py index 9ea8199502..34ad9e1c4f 100644 --- a/app/eventyay/control/views/event.py +++ b/app/eventyay/control/views/event.py @@ -191,6 +191,11 @@ def get_context_data(self, *args, **kwargs) -> dict: def form_valid(self, form): self._save_decoupled(self.sform) self.sform.save() + form.instance.update_language_configuration( + locales=self.sform.cleaned_data.get('locales'), + content_locales=self.sform.cleaned_data.get('content_locales'), + default_locale=self.sform.cleaned_data.get('locale'), + ) self.save_meta() self.save_product_meta_property_formset(self.object) self.save_confirm_texts_formset(self.object) diff --git a/app/eventyay/control/views/main.py b/app/eventyay/control/views/main.py index 4156ea78a0..ba7cc206b6 100644 --- a/app/eventyay/control/views/main.py +++ b/app/eventyay/control/views/main.py @@ -282,6 +282,7 @@ def done(self, form_list, form_dict, **kwargs): event.settings.set('timezone', basics_data['timezone']) event.settings.set('locale', basics_data['locale']) event.settings.set('locales', foundation_data['locales']) + event.settings.set('content_locales', foundation_data['locales']) if (copy_data and copy_data['copy_from_event']) or self.clone_from or event.has_subevents: return redirect( diff --git a/app/eventyay/eventyay_common/forms/event.py b/app/eventyay/eventyay_common/forms/event.py index 9dd432e31f..ad8d1de174 100644 --- a/app/eventyay/eventyay_common/forms/event.py +++ b/app/eventyay/eventyay_common/forms/event.py @@ -24,9 +24,10 @@ class EventCommonSettingsForm(SettingsForm): ) auto_fields = [ - "locales", - "locale", - "region", + 'locales', + 'content_locales', + 'locale', + 'region', "contact_mail", "imprint_url", 'logo_image', @@ -57,6 +58,8 @@ def clean(self): def __init__(self, *args, **kwargs): self.event = kwargs['obj'] super().__init__(*args, **kwargs) + if self.event and 'content_locales' in self.fields: + self.fields['content_locales'].initial = self.event.content_locales class EventUpdateForm(I18nModelForm): diff --git a/app/eventyay/eventyay_common/templates/eventyay_common/event/settings.html b/app/eventyay/eventyay_common/templates/eventyay_common/event/settings.html index 772c590a12..b1fa5959b9 100644 --- a/app/eventyay/eventyay_common/templates/eventyay_common/event/settings.html +++ b/app/eventyay/eventyay_common/templates/eventyay_common/event/settings.html @@ -129,6 +129,7 @@