File size: 2,285 Bytes
4107841 d8ebb7a 4107841 d8ebb7a 4107841 d8ebb7a 4107841 4fc023c 4107841 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | """Helpers that keep every visible string in the locale files and switchable at runtime."""
from __future__ import annotations
import io
from collections.abc import Callable, Iterable
from typing import Any, Literal
import gradio as gr
from PIL import Image
from src.i18n import I18n
Block = gr.blocks.Block
Updates = dict[Block, dict[str, Any]]
Props = Callable[[str], dict[str, Any]]
ApiVisibility = Literal["private", "undocumented"]
class Localizer:
"""Creates components from locale-driven props and re-renders them for another language."""
def __init__(self, i18n: I18n) -> None:
self.i18n = i18n
self.default_language = i18n.default_language
self._bindings: list[tuple[Block, Props]] = []
def t(self, lang: str, key: str, **values: object) -> str:
return self.i18n.t(lang, key, **values)
def make(self, cls: type[Block], props: Props, **static: Any) -> Any:
"""Build `cls` with props for the default language and remember how to re-localize it."""
component = cls(**props(self.default_language), **static)
self._bindings.append((component, props))
return component
@property
def components(self) -> list[Block]:
return [component for component, _ in self._bindings]
def props(self, lang: str) -> Updates:
return {component: props(lang) for component, props in self._bindings}
def build_heading(loc: Localizer, title_key: str, hint_key: str) -> Any:
"""A bold section title with a one-line explanation under it."""
return loc.make(
gr.Markdown,
lambda lang: {"value": f"**{loc.t(lang, title_key)}** \n{loc.t(lang, hint_key)}"},
elem_classes=["section-heading"],
)
def merge(*parts: Updates) -> Updates:
merged: Updates = {}
for part in parts:
for component, props in part.items():
merged[component] = {**merged.get(component, {}), **props}
return merged
def pack(order: Iterable[Block], updates: Updates) -> list[Any]:
"""Turn updates into an output list aligned with `order`; untouched components get no-ops."""
return [gr.update(**updates[c]) if c in updates else gr.skip() for c in order]
def to_pil(image: bytes) -> Image.Image:
return Image.open(io.BytesIO(image))
|