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.

Resolution identifier: CIML/2004/1 doi: 10.63493/... urn: urn:oiml:... dates: [...decision, ...meeting] Localization — eng / Latn title: "Approval of the agenda" actions: - type: approves message: "Approves the agenda..." Localization — fra / Latn title: "Approbation de l'ordre" actions: - type: approves message: "Approuve l'ordre du jour..."

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.

resolutions/ciml-39-decisions.yaml · single file, both languages metadata: { title, dates, venue, city, country_code, source_urls: [...] } resolutions: [ { identifier, doi, urn, dates, localizations: [...] } ] language_code: eng - title: "Approval of the agenda for the 39th CIML Meeting" language_code: fra - title: "Approbation de l'ordre du jour de la 39e réunion du CIML"

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 ResolutionCollection / Resolution / Localization via lutaml-model’s declared attributes. Anything lutaml can’t bind raises an UnknownAttribute or a KeyError.
  • Stage 3 runs the JSON Schema. language_code must match ^[a-z]{3}$, script must match ^[A-Z][a-z]{3}$, Action.type must be in the enum, additionalProperties: false on every object catches typos at the wire level.

The CLI runs all three:

edoxen validate resolutions/*.yaml

Translator workflow, end-to-end

Translator Repo CI (edoxen validate) Docs site

EN + FR edit (1 PR) open PR parse + schema list errors merge site rebuild

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