Localization sync — EN+FR deep-dive
This is the deep-dive into how Edoxen keeps English and French renderings of the same Resolution aligned inside a single YAML file. For the field reference, see Localization. For the why of the localizations[] pattern, see Multilingual support.
The shape of a Resolution
Every Resolution carries its language-agnostic admin fields exactly once, then a localizations[] array with one entry per language. The EN and FR entries share the same identifier, the same dates, the same DOI — only the per-language content differs.
Read this as: the parent Resolution owns the admin fields. localizations[] is its children, one per language. EN and FR are siblings — neither knows the other; both know the parent.
Why one file, not two
The natural alternative is one file per language (resolutions.en.yaml, resolutions.fr.yaml) and a join key (CIML/2004/1). Edoxen rejects that because EN+FR pairs drift invisibly the moment a translator edits one file but not the other. One single file, with both languages nested, means a git diff and git blame show exactly the EN line and the FR line that changed together.
Result: translators see EN and FR side by side in any editor. A pull request that updates both blocks sits as one diff. A pull request that updates only one block is visibly suspicious — easy to spot in review.
Validation pipeline
Every .yaml file flows through the same three-stage pipeline. Neither translation drift, nor schema typos, nor model mis-attribution survives all three.
- Stage 1 confirms the file is well-formed YAML — no tabs-as-indent, no duplicated keys.
- Stage 2 walks the hash into
ResolutionSet/Resolution/Localizationvia lutaml-model's declared attributes. Anything lutaml can't bind raises anUnknownAttributeor aKeyError. - Stage 3 runs the JSON Schema.
language_codemust match^[a-z]{3}$,scriptmust match^[A-Z][a-z]{3}$,Action.typemust be in the enum,additionalProperties: falseon every object catches typos at the wire level.
The CLI runs all three:
edoxen validate resolutions/*.yamlTranslator workflow, end-to-end
The translator never has to know which file holds which language. Both languages live in the same file; one PR carries both; one CI run checks both.
Where to next
docs/localization— the field reference.docs/multilingual— the model-level rationale forlocalizations[].docs/source-url— how each resolution remembers where it came from.docs/schema— the JSON Schema, including the per-language regex invariants.