Browse documentation
Metadata, provenance & plurals
Reference every non-text field in a translation unit and understand how status, history, context, plurals, and extensions survive each processing step.
The integrity payload around text
Data is more than a source/target pair. Unit metadata is divided by responsibility so a missing value stays distinguishable from an empty value and target-specific facts do not overwrite source-level facts until a target is deliberately selected.
Data├── plural: Plural | None├── meta: Meta├── status: TranslationStatus├── comments: list[Comment]│ └── origin: Origin | None├── previous_context: AdjacentContext | None├── next_context: AdjacentContext | None├── extensions: dict[str, str]└── targets[locale]: TargetData ├── plural / meta / status / comments / extensions └── tags: TargetTags | NoneMeta field reference
| Field | Type | Meaning |
|---|---|---|
usage_count | `int | None` |
last_used | `str | None` |
first_used | `str | None` |
created | `str | None` |
updated | `str | None` |
max_length | `int | None` |
min_length | `int | None` |
extensions | dict[str, str] | Extra metadata such as TMX change IDs or source attributes without a common field. |
Timestamp fields are strings because source formats use different lexical conventions. Lokit preserves supplied values; applications that need chronological comparison should parse and normalize them at their boundary.
Adjacent context and extensions
AdjacentContext holds optional unit_id, source, target, and extensions for the previous or next unit. TMX reads and writes the x-previous-* and x-next-* property families; database matching can use adjacent source strings for contextual ranking. extensions exists at document, unit, target, plural, metadata, comment, origin, and context levels. Keys and values are strings: copy unknown entries forward, namespace application-owned keys, and do not assume a key from one file type has meaning in every writer.
TranslationStatus normalization
The public values are new, draft, translated, reviewed, approved, rejected, and unknown.
| Source/output | Normalization |
|---|---|
| TMX input | Approved aliases include approved, signed-off, and final; reviewed includes reviewed/review; translated includes translated/complete; draft includes draft, notapproved, not-approved, and unapproved. Unknown values remain unknown. |
| TMX output | A known value is written as an x-status property. |
| XLIFF input | final/signed-off → approved; translated/review-translation states → translated; review-adaptation/localization states → reviewed; new/needs-translation → new. |
| XLIFF 1.2 output | Approved → final; reviewed → needs-review-l10n; translated → translated; new → new; draft/rejected → needs-translation; unknown is omitted. |
| PO input | Fuzzy → draft; a non-empty translation → translated; missing translation → new. |
For a selected TargetData, a non-unknown target status overrides the base unit status. An unknown target status leaves the base value intact.
Plural field reference
| Field | Type | Meaning |
|---|---|---|
variant | str | Plural source variant, such as Gettext msgid_plural. |
count | `int | None` |
category | `PluralCategory | None` |
extensions | dict[str, str] | Source-specific identity such as the original Gettext plural index. |
Data.plural describes the unit-level form. TargetData.plural can carry a locale-specific override; target splitting promotes that override into the resulting single-target Data.
How Gettext plurals are processed
PO parsing emits the index-0 form at the base unit ID and additional forms as unit_id[n]. Every form carries the same singular source and Plural.variant from msgid_plural; extensions["gettext_index"] preserves the original numeric index. When a Plural-Forms rule and locale allow one unambiguous CLDR category, Lokit also assigns it. PO export groups plural-marked units again, preferring the retained Gettext index, then a locale category mapping, then the [n] suffix. The .lokit format and PostgreSQL serialization preserve plural fields directly; other writers may write the unit text but do not promise reconstruction of a destination-native plural construct.
import lokit
document = lokit.parse.po( "messages.fr.po", source_locale="en-US", target_locale="fr-FR",)
for unit_id, unit in document.data.items(): if unit.plural is not None: print( unit_id, unit.plural.variant, unit.plural.category, unit.plural.extensions.get("gettext_index"), )Target overlay rules during splitting
A split document is a deep independent copy. The selected TargetData.text becomes Data.target; a known target status overrides the base status; target plural replaces base plural when present; non-None target Meta fields merge over base metadata and extension dictionaries merge by key. Non-empty target comments replace base comments. Target tags become the selected target side of Data.tags, while source tags remain. Data.targets is cleared so the result is an unambiguous single-target document.
Flat projection fields and precedence
TranslationRow is dict[str, str]; the available DictField values are source_language, target_language, source, target, domain, unit_id, source_locale, target_locale, status, resource, and project. Source language resolves from an explicit argument, then document metadata, then the base source locale. An explicit domain wins, otherwise Lokit reads extensions["domain"] and then extensions["property.domain"]. Project resolves from selected-target extensions, selected-target comment origins, unit extensions, then unit comment origins. Target status wins only when it is not unknown; resource comes from unit extensions. A flat projection intentionally omits nested metadata—use the model, .lokit, or database rows when those details must remain structured.
Preservation depends on the destination
| Destination | Integrity scope |
|---|---|
| .lokit | Round-trips the complete documented model within v1 representational bounds. |
| PostgreSQL | Serializes units, plurals, metadata, contexts, tags/parts, comments/origins, project/domain, and extensions into typed rows. |
| TMX | Rich subset: multilingual text, inline codes, statuses, notes/origin summaries, adjacent context, selected properties, usage count, creation/update/change metadata, and header fields. |
| XLIFF | Text, target status, inline codes, notes, original resource, unit/segment IDs, datatype, and whitespace hints; generic Meta/Origin fields have no universal XLIFF mapping. |
| PO / POT | Gettext context, comments, flags, references, headers, status semantics, and plural forms. |
| CSV / XLSX | Configured ID/source/target/status/comment columns; generic nested metadata and tags are not a tabular interchange contract. |
| JSON i18n | Nested keys and strings; generic metadata is outside the format. |
| HTML / IDML / Office | Text plus format-specific inline/layout references; regeneration preserves unrelated source-package content rather than encoding every common-model metadata field. |
Conversion therefore preserves every fact the destination can represent, not an impossible union of all source-format features. Use .lokit or the database as the lossless intermediate when metadata must survive a multi-step pipeline.
Construct rich metadata explicitly
All values below use the stable public types. Empty optional fields remain distinguishable from absent fields, and custom source/application facts belong in the nearest extensions mapping.
from lokit.types import ( AdjacentContext, Comment, Data, Meta, Origin, Plural, PluralCategory, TranslationStatus,)
unit = Data( source="{count} file", target="{count} fichiers", plural=Plural( variant="{count} files", category=PluralCategory.OTHER, extensions={"gettext_index": "1"}, ), meta=Meta(usage_count=12, max_length=80), status=TranslationStatus.REVIEWED, comments=[ Comment( context="Approved terminology", timestamp="2026-08-10T10:30:00Z", origin=Origin( system="TMS", project="desktop-app", creator_id="reviewer-17", ), ) ], previous_context=AdjacentContext(unit_id="files.heading"), extensions={"domain": "storage"},)
Comments, origins, and project history
A
Commenthas requiredcontexttext plus optionaltimestamp,context_key,origin, and stringextensions.Origincontains optionalsystem,project,creator_id, and extensions. Lokit does not invent a separate project-history object: provenance/history is represented by the ordered comments, their timestamps and origins, document-levelexport_origin/export_timestamp, and source-specific extensions. TMX maps notes and comment-like properties into comments,creationidintocreator_id, andx-project/x-systeminto the origin. PO maps translator/extracted comments andmsgctxt; XLIFF maps notes. Preserve list order when an upstream tool treats it as history order.