Toggle Light / Dark / Auto color theme
Toggle table of contents sidebar
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
),
)
)