410. Ledgers and decisions: recording every change with its reason so the next catalog cannot regress¶
A terminology decision that is not recorded will be unmade. Not by anyone who disagrees with it, but by the next translator who never heard of it, the next engine run that was not given it, or the next catalog merge that brings back an older file. The FontLab localization principles say it in one line: a decision that lives only in a chat message is not a decision. This chapter describes the record that makes a decision stick: the ledger, which logs every change to a shipped translation with the text before and after and the reason; how ledgers chain across reviews; how a decision is kept in agreement across the ledger, the core memory and the language guide; and how retired strings and replayable rules protect the next catalog.
What a ledger is for¶
A ledger answers three questions about any string in a catalog: what did it say before, what does it say now, and why did it change. The FontLab principles make it a condition of finishing a review: every change to a shipped translation is recorded with the previous text, the new text and the reason, keyed by message id. Without that record, a reviewer who finds an odd translation cannot tell whether it is a mistake to fix or a decision to respect, and an automated upgrade cannot tell whether an old translation in a memory is still valid.
The older literature does not use the word, but it describes the need. Esselink (2000) assigns one person to maintain the project glossary and wants every new term or terminology change communicated to the publisher's reviewers throughout the project. Roturier (2015) recommends exporting translation memory metadata, including authors and dates, so that a memory can be maintained. A ledger is the same idea applied to each change rather than to each unit.
Anatomy of a ledger entry¶
The German review of September 2026 wrote its ledgers as JSON files, one per area of the interface, in the review ledger directory of the FontLab localization project. Each file states its scope and the batches it read, with a digest of each request, and then lists the changes:
{
"language": "de",
"id": "FL-a0c27c69c3d3c9fc",
"ordinal": 1174,
"context": "DlgArtworkWarning",
"source": "Scale to UPM, align to descender",
"comment": "",
"before": "Auf UPM skalieren, an Unterlänge ausrichten",
"after": "Auf Gevierthöhe skalieren, an Unterlänge ausrichten",
"why": "PasteUPM_Descender setzt die Zielhöhe auf die Kegelauflösung ..."
}
Every field has a job:
| Field | Job |
|---|---|
id, context, source, comment |
Identify the message, so the entry still resolves after the catalog is regenerated |
before |
The exact prior target; applying the change fails if the catalog no longer contains it |
after |
The exact new target |
why |
The reason, with its evidence: an issue number, a source function, an attestation |
file-level scope and inputs |
What was reviewed and from which inputs, so the review can be reproduced |
The before field is a guard as well as a record. The corrections of the founder's update of 29 September 2026 were applied to the catalogs with exact prior-target checks: a change applies only if the string still says what the reviewer saw. If someone else has changed it in the meantime, the change stops rather than overwriting their work.
Two practical notes from the same data. The German ledgers name the reason field why in some files and reason in others; pick one name and enforce it, because a tool that looks for one misses the other. And the ledger of the 29 September update groups its 746 entries into three lists (35 western corrections, 663 Polish catalog changes, 48 help changes) without a reason per entry, because every entry in it has the same reason: the founder's instructions of 29 September. A per-entry reason is needed when the reasons differ; a file-level reason is enough when they do not, provided the file names its source.
Decisions chain¶
A string can change several times, and each change can be right in its own moment. The ledgers keep the chain. The German label Descender to UPM in the paste preferences shows how:
| Step | Translation | Recorded reason |
|---|---|---|
| Original catalog | Unterlänge bis UPM | |
| German consistency review | Geviert ab Unterlänge | The code sets the target height to one em measured from the descender, which is not the coordinate range from descender to UPM; the ledger names the two functions it read |
| Founder's review | Unterlänge bis Gevierthöhe | The founder: the label names a distance from one line to the other |
| Build pulled for the 29 September update | message retired; new source UPM height from descender translated as Gevierthöhe ab Unterlänge | The English source itself was reworded in the product |
Read without the ledgers, the final German looks like a reversal of the founder's decision. Read with them, it is the consequence of a new English source: the message that the founder's rule applied to no longer exists, and the retired catalog keeps it. The FontLab principles describe this property: provenance follows the chain of ledgers by content, so a decision that returns a string to an earlier form is still traceable.
The same mechanism records supersession of a term rather than a string. An earlier German review chose Kegelauflösung for UPM in German; the founder's review replaced it with Geviertauflösung. The review directory's README states that the founder's explicit instructions take priority over older glossary entries and names this case, so that nobody restores the older term from an older ledger.
One decision, three records that must agree¶
A terminology decision lives in three places, each for a different reader:
- the ledger, for the exact strings that changed;
- the core memory, for every future translation of the term, by person or engine;
- the language guide, for the reasoning behind the less obvious choices.
The FontLab principles say that a review is not complete until all three agree. The Polish core memory shows one way to keep the memory in step: each unit changed in the 29 September update carries a translator's note of the form Przegląd 2026-09-29 (…): decyzja założyciela, poprzednio „…”, "review of 29 September 2026 (…): founder's decision, previously …", so the prior term travels with the new one into every export.
Guides drift more easily, because they are prose. Two examples from the FontLab guides as they stood at the end of September 2026:
- The German guide's list of decisions and the German core memory say Power-Schub for Power Nudge, following the founder's review. A longer section further down the same guide still explains the name as Power-Verschiebung, from an earlier review.
- The principles page still gives Descender to UPM as Unterlänge bis Gevierthöhe, a correct record of the founder's rule for a source string that the product has since retired.
Neither is a catalog error; the strings are right. Both are the kind of stale reasoning that misleads the next reviewer, and both are found the same way: search the guides for every rejected form named in the core memory notes and the ledgers.
The catalogs drift too. A concept search for Power Brush in the German catalog in September 2026 finds Power-Pinsel in two contexts and the English Power Brush in a third, the list of element types. An earlier German ledger records the reason for Power-Pinsel: it is the established product term, consistent with Power-Strich and Power-Hilfslinien. Whether the third string is a deliberate exception or a missed one is exactly the question a ledger entry should answer, and the absence of an entry is the signal to ask.
Retired strings and the next catalog¶
When a new build regenerates the catalog, some messages disappear. They are not deleted from the record. The upgrade that brought in the build for the 29 September update wrote 63 messages per language to a retired catalog. A reviewed translation is never lost because its source changed; if the source returns, the translation is still there to compare.
Retired terms need the opposite treatment. After a terminology change, the old term must not come back through any door: a project memory built from an older catalog, a machine draft primed with an older glossary, a help file translated before the change. The verification of the 29 September update included a retired-term scan of the catalogs and help, which came back clean. The FontLab memory guidance states the general rule: after a decision changes, regenerate the exports and identify the translations that used the superseded form.
Rules that can be replayed¶
A large terminology change is applied by a program, and that program is part of the record. The 29 September update was applied by a script with three modes: report, apply and ledger. Its first run contained a rule that was not idempotent and a stem pattern that missed the genitive plural (chapter 408). An independent review pass caught both, the fix was applied, and the ledger was extended with the repaired strings.
Three properties make such a program safe to run again:
- Idempotence. Running the rules on their own output changes nothing.
- Guards. Every change checks the exact prior text and refuses to apply otherwise.
- A ledger as output. The program writes what it changed, so the review reads the ledger rather than diffing the catalog by eye.
A worked example: making a decision stick¶
A reviewer changes Polish pchnięcie to holowanie for Nudge. To make it stick:
- Core memory. Update the unit: target holowanie, status approved, note naming the issue and the previous form.
- Catalog and help. Apply the change by message with prior-text guards, including derived forms such as superholowanie for Power Nudge.
- Ledger. Write every changed string with context, source, before and after, under the issue.
- Guide. Update the language guide's list of decisions, and search its prose for the old word.
- Project memory. Rebuild it from the corrected catalog.
- Scan. Search catalog, help and memories for pchnięcie, the variant popychanie that one tooltip used, and their inflected forms, and confirm that nothing but the retired catalog and the ledger's
beforefields still contains it.
The next time a catalog is upgraded, the project memory supplies the new strings, the core memory supplies the term to the engine for anything new, and the old word has no way back in.
Sources¶
- Bert Esselink, A Practical Guide to Localization, 2000 (chapter 12: terminology reference materials)
- Johann Roturier, Localizing Apps: A Practical Guide for Translators and Translation Students, 2015 (section 5.3: translation memory)
- The German review ledgers of the FontLab localization project (September 2026): the directory README, the consistency review, the founder's review, the properties and tools review, and the 29 September update
- The German FontLab 9 interface catalog (
fontlab_de.ts) and the retired catalogs of the FontLab localization project - The changelog of the FontLab localization project, the founder's review remarks on the German, Spanish and French catalogs (September 2026) and the founder's update of 29 September 2026
- localization/principles, localization/memories, localization/de and
localization/tm/pl-core.tmxin the vexy-fontlab-writing-styleguide repository