Source code for betty.gui.locale

"""
Provide locale management for the Graphical User Interface.
"""

from __future__ import annotations

from typing import Any, TYPE_CHECKING

from PyQt6.QtWidgets import QComboBox, QLabel, QWidget
from typing_extensions import override

from betty.asyncio import wait_to_thread
from betty.gui.text import Caption
from betty.locale import negotiate_locale, get_display_name

if TYPE_CHECKING:
    from betty.app import App
    from PyQt6 import QtGui


[docs] class LocalizedObject: """ A `PyQt6.QtWidgets.QWidget` mix-in to localize widgets, and re-localize them when the locale changes. """
[docs] def __init__(self, app: App, *args: Any, **kwargs: Any): super().__init__(*args, **kwargs) self._app = app
[docs] @override def showEvent( # type: ignore[misc] self: LocalizedObject & QWidget, a0: QtGui.QShowEvent | None, ) -> None: super().showEvent(a0) # type: ignore[misc] self._set_translatables() self._app.configuration.on_change(self._set_translatables)
def _set_translatables(self) -> None: pass
[docs] class TranslationsLocaleCollector(LocalizedObject): """ Helps users select a locale for which translations are available. """
[docs] def __init__(self, app: App, allowed_locales: set[str]): super().__init__(app) self._allowed_locales = allowed_locales allowed_locale_names: list[tuple[str, str]] = [] for allowed_locale in allowed_locales: locale_name = get_display_name(allowed_locale) if locale_name is not None: allowed_locale_names.append( ( allowed_locale, locale_name, ) ) allowed_locale_names = sorted(allowed_locale_names, key=lambda x: x[1]) def _update_configuration_locale() -> None: self._app.configuration.locale = self._configuration_locale.currentData() self._configuration_locale = QComboBox() for i, (locale, locale_name) in enumerate(allowed_locale_names): self._configuration_locale.addItem(locale_name, locale) if locale == self._app.configuration.locale: self._configuration_locale.setCurrentIndex(i) self._configuration_locale.currentIndexChanged.connect( _update_configuration_locale ) self._configuration_locale_label = QLabel() self._configuration_locale_caption = Caption() self._set_translatables() self._app.configuration.on_change(self._set_translatables)
@property def locale(self) -> QComboBox: """ The selected locale. """ return self._configuration_locale @property def rows(self) -> list[list[QWidget]]: """ The :py:class:`PyQt6.QtWidgets.QFormLayout` rows provided by the collector. """ return [ [self._configuration_locale_label, self._configuration_locale], [self._configuration_locale_caption], ] @override def _set_translatables(self) -> None: super()._set_translatables() localizer = self._app.localizer localizers = self._app.localizers self._configuration_locale_label.setText(localizer._("Locale")) locale = self.locale.currentData() if locale: translations_locale = negotiate_locale( locale, list(localizers.locales), ) if translations_locale is None: self._configuration_locale_caption.setText( localizer._("There are no translations for {locale_name}.").format( locale_name=get_display_name(locale, localizer.locale), ) ) else: negotiated_locale_translations_coverage = wait_to_thread( localizers.coverage(translations_locale) ) if negotiated_locale_translations_coverage[1] == 0: negotiated_locale_translations_coverage_percentage = 0 else: negotiated_locale_translations_coverage_percentage = round( 100 / ( negotiated_locale_translations_coverage[1] / negotiated_locale_translations_coverage[0] ) ) self._configuration_locale_caption.setText( localizer._( "The translations for {locale_name} are {coverage_percentage}%% complete." ).format( locale_name=get_display_name( translations_locale, localizer.locale ), coverage_percentage=round( negotiated_locale_translations_coverage_percentage ), ) )