bacommon.langstr package

Language-agnostic complex strings – the Lstr2 runtime model.

Pure-Python prototype of the language-string-context system (see docs/initiatives/language-string-context.md in ballistica-internal). The C++ Lstr2 is a later optimized port of this proven model.

The model lets us pass around minimal, language-independent representations of a complex string (substitutions, plurals, nesting) and resolve to a flat string in a particular language only at display – so one representation serves clients of any language.

type bacommon.langstr.EncodedLstr = list[str | int | EncodedLstr]
class bacommon.langstr.LangStrDir(apverid: str, tree: WrapperTree, prefix: str = '')[source]

Bases: object

Runtime root/subdir accessor for a generated wrapper package.

exception bacommon.langstr.LangStrError[source]

Bases: Exception

A malformed language-string or encode-context operation.

class bacommon.langstr.LanguageStringDecodeContext(package_index_map: dict[int, str], structures: dict[str, PackageStructure], language: dict[str, dict[str, str | StringSelector]], locale: Locale)[source]

Bases: object

Decodes chunks into flat strings for one target locale.

Holds the {pkg_int: apverid} map (from the encoder), the package structures, and the per-apverid string values for a single locale. decode() resolves a chunk (recursively rendering nested values) via bacommon.loctext.evaluate().

decode(encoded: EncodedLstr) str[source]

Resolve a chunk to a flat string in this context’s locale.

Fail-visible: any structural problem yields an LSTR_ERROR:… sentinel (and a logged warning) rather than crashing the caller.

class bacommon.langstr.LanguageStringEncodeContext(lstrs: list[Lstr], structures: dict[str, PackageStructure])[source]

Bases: object

Encodes Lstr values into minimal language-free chunks.

Built from the batch of values to send: it computes the union of apverids they reference (recursively – nested values know their own apverid) and assigns each a stable integer index. encode() then emits [pkg_int, str_int, …subs]; package_index_map is the only mapping the consumer needs (string indices resolve from the content-pinned apverid itself).

encode(lstr: Lstr) EncodedLstr[source]

Encode one value (recursively) into a minimal chunk.

property package_index_map: dict[int, str]

The {pkg_int: apverid} map a decoder needs.

class bacommon.langstr.LanguageStringNameDecodeContext(language: dict[str, dict[str, str | StringSelector]], locale: Locale)[source]

Bases: object

Decodes Lstr values directly, by name, for one locale.

The name-based counterpart to LanguageStringDecodeContext: it resolves an in-memory Lstr (carrying its apverid, string name, and keyword subs) straight against per-apverid per-locale values – no integer indices, package-index-map, or PackageStructure needed, since the subs are self-describing keyword->value pairs. This is the client’s primary path: resolve the referenced packages, gather their per-locale values, then decode each Lstr in the client’s locale.

Fail-visible like LanguageStringDecodeContext – any structural problem yields an LSTR_ERROR:… sentinel (and a logged warning) rather than crashing the caller.

decode(lstr: Lstr) str[source]

Resolve an Lstr to a flat string in this context’s locale.

Fail-visible: any structural problem yields an LSTR_ERROR:… sentinel (and a logged warning) rather than crashing the caller.

class bacommon.langstr.Lstr(apverid: str, name: str, subs: dict = <factory>)[source]

Bases: object

A deferred, language-agnostic complex string.

subs maps each substitution keyword to its value – a flat str / int or a nested Lstr. A no-arg string has empty subs. The value carries its own exact apverid (so an encode context can discover the package union from the values themselves) and the string’s logical name (mapped to its integer index at encode time).

This is @ioprepped so it can be sent directly on the wire (the name-based docui-v2 form). Flat str/int subs serialize directly; nested-Lstr subs are exercised by the in-memory encode / name-decode paths but are not yet directly JSON-serializable here (they graduate with the integer-indexed EncodedLstr form).

apverid: str
name: str
subs: dict
class bacommon.langstr.PackageDef(apverid: str, strings: tuple[StringDef, ...])[source]

Bases: object

Language-free definition of one asset-package-version’s strings.

The shared source the encode/decode PackageStructure and the type-safe wrapper codegen both derive from (in the real system, from an apverid’s resolved listing; in tests, hand-built).

apverid: str
strings: tuple[StringDef, ...]
class bacommon.langstr.PackageStructure(apverid: str, strings: dict[str, tuple[str, ...]])[source]

Bases: object

Language-free structure of one asset-package-version.

Maps string names <-> integer indices (assigned in canonical sorted-name order so both ends agree without shipping the mapping) and holds each string’s ordered substitution-keyword list. Carries no translations – encoding needs only this.

apverid

strings maps each logical name to its ordered substitution keywords (() for a no-arg string).

classmethod from_def(pkgdef: PackageDef) PackageStructure[source]

Build the encode/decode structure from a package definition.

index_of(name: str) int[source]

Return the integer index for a string name.

name_of(index: int) str[source]

Return the string name for an integer index.

params_of(name: str) tuple[str, ...][source]

Return the ordered substitution keywords for a string name.

class bacommon.langstr.StringDef(path: str, params: tuple[tuple[str, str], ...] = ())[source]

Bases: object

One string’s language-free definition.

params is the ordered list of (keyword, kind) where kind is 'text' (a text sub -> str | Lstr) or 'count' (the plural pivot -> int); () for a no-arg string. The canonical ordering (sorted keyword) is what fixes the positional substitution order.

params: tuple[tuple[str, str], ...] = ()
path: str
type bacommon.langstr.WrapperTree = dict[str, tuple[str, ...] | WrapperTree]
bacommon.langstr.generate_wrapper_module(pkgdef: PackageDef) str[source]

Return the .py source for a package’s type-safe wrapper.

The output is valid but not auto-formatted (the long _TREE literal in particular); a caller writing it to the tree should run the formatter before committing.

bacommon.langstr.package_structure(apverid: str, tree: WrapperTree) PackageStructure[source]

Build a PackageStructure from a wrapper’s runtime _TREE.

Flattens the nested tree into the {logical-path: param-keywords} map the encode/decode contexts need – so a consumer of a vendored package just passes module.APVERID, module._TREE (both module-level).

bacommon.langstr.parse_language_blob(text: str) dict[str, str | StringSelector][source]

Parse a canonical language blob into a {name: value} map.

The exact inverse of serialize_language_blob(): reads the top-level strings object, turning each value back into a str (plain) or a StringSelector (a dict). A blob with no strings key (e.g. a legacy-only package) yields an empty map; malformed values are skipped (fail-soft on the consumer side).

bacommon.langstr.serialize_language_blob(values: dict[str, str | StringSelector]) str[source]

Serialize a per-locale value map to the canonical language blob.

values maps each string’s logical name to its value – a plain str or a StringSelector. Output is deterministic (sorted keys, fixed formatting) for cache stability and diffability.