Localization
ZwyLib provides a small helper for building a multi-language string table for your plugin, with attribute-style access, str.format-like templating, and Russian/Ukrainian-aware pluralization built in — the same mechanism ZwyLib itself uses for its own settings screen strings.
Getting Started
# ... metadata and zwylib import ...
from zwylib import locales_controller
_locales = {
"en": {
"greeting": "Hello, {name}!",
"items_count": ["{n} item", "{n} items"],
},
"ru": {
"greeting": "Привет, {name}!",
"items_count": ["{n} штука", "{n} штуки", "{n} штук"],
},
}
L = locales_controller.generate_locale_class(_locales)
# ...
print(L.greeting(name="World")) # "Hello, World!" (or the matching translation)
print(L.plural("items_count", 3)) # "3 штуки" on a Russian device, "3 items" otherwiseAny string containing { and } becomes callable, so you can fill in placeholders the same way you would with str.format. If a key is missing for the active language, ZwyLib falls back to "en" (or the first available language if "en" isn’t present), and finally to the key itself.
zwylib.locales_controller
zwylib.locales_controller: LocalesControllerThis global object is created during ZwyLib initialization and tracks the app’s current language, notifying subscribers when it changes.
Methods
get_current_locale
LocalesController.get_current_locale() -> strReturns the current plural-aware language code (e.g. "en", "ru"), refreshing it from the client first if it hasn’t been read yet.
Example
zwylib.locales_controller.get_current_locale()refresh_locale
LocalesController.refresh_locale() -> NoneRe-reads the current language from the client and notifies all subscribers registered via subscribe_to_refresh. Called automatically by ZwyLib whenever the app’s language changes.
subscribe_to_refresh
LocalesController.subscribe_to_refresh(sub: () -> None) -> NoneRegisters a callback to run whenever the app’s language changes (e.g. to rebuild cached UI that embeds localized strings). The calling plugin’s ID is inferred automatically from the call stack and used as the subscription key — a plugin can only have one active subscriber at a time. Automatically unsubscribed when the plugin unloads.
Parameters
sub(() ->None): Callback to run on language change.
Example
zwylib.locales_controller.subscribe_to_refresh(lambda: print("Language changed!"))unsubscribe_from_refresh
LocalesController.unsubscribe_from_refresh(plugin_id: Optional[str] = None) -> NoneRemoves a previously registered language-change subscriber.
Parameters
plugin_id(Optional[str], defaultNone): ID of the plugin whose subscriber should be removed. Inferred automatically from the call stack if omitted.
Example
zwylib.locales_controller.unsubscribe_from_refresh(__id__)generate_locale_class
LocalesController.generate_locale_class(dictionaries: Dict[str, Dict[str, str]]) -> LocalesBuilds a Locales lookup object out of a {language_code: {key: value}} mapping. See zwylib.Locales below for how to use the result.
Parameters
dictionaries(Dict[str, Dict[str, str]]): Per-language string tables, keyed by language code (e.g."en","ru","uk").
Example
L = zwylib.locales_controller.generate_locale_class({
"en": {"hello": "Hello!"},
"ru": {"hello": "Привет!"},
})zwylib.Locales
zwylib.locales_controller.generate_locale_class(...): LocalesThe object returned by generate_locale_class. Should only be obtained that way, not constructed directly.
Methods
get
Locales.get(key: str, dictionary: Optional[Dict[str, str]] = None) -> Union[str, SmartString]Looks up key in the current language’s dictionary (or a specific one, if dictionary is provided), falling back to the default language and then to key itself if not found. Equivalent to plain attribute access (L.key). If the resolved value contains { and }, it’s returned as a callable SmartString that can be filled in like str.format.
Parameters
key(str): The string key to look up.dictionary(Optional[Dict[str, str]], defaultNone): Look up in this specific dictionary instead of the current language’s.
Example
L.get("greeting")
L.greeting # equivalent
L.greeting(name="World") # if the string contains "{name}"plural
Locales.plural(key: str, number: int) -> strLooks up key as a list of plural forms and picks the correct one for number in the current language. For "ru"/"uk", uses the standard three-form Slavic pluralization rule (one/few/many); for other languages, falls back to a simple singular/plural (or single-form) rule. If key doesn’t resolve to a list, it’s stringified and returned as-is.
Parameters
key(str): The string key, expected to resolve to a list of plural forms.number(int): The quantity to pick a form for.
Example
L.plural("items_count", 1) # "1 item" / "1 штука"
L.plural("items_count", 3) # "3 items" / "3 штуки"
L.plural("items_count", 5) # "5 items" / "5 штук"