没有不值得去解决的问题,也没有不值得去学习的技术!

If the Original File Is Unchanged, Why Retranslate? Semantic Input Expansion in books.yaml 10→12

If the Original File Is Unchanged, Why Retranslate? Semantic Input Expansion in books.yaml 10→12

作者:

在

The previous articles kept discussing one question:

When content changes, which existing results can still be reused, and which must be invalidated?

But recently, _content/learn/books.yaml encountered an even more specific situation.

This time, the original file did not change.

No heading was modified.

No new book was added.

No English content was updated.

In terms of file bytes, it remained the exact same books.yaml.

However, after the workflow reread it, it found:

Previously, only 10 semantic units were identified as needing to enter the language workflow; now there are 12.

The two new additions are display text slots that previously did not fall within the scope of formal language semantic inputs. Their current source values are PDF and GitHub.

This easily raises a question:

PDF and GitHub were likely to remain unchanged in Simplified Chinese anyway, so why add them to the language input?

This question actually reveals exactly what this design truly aims to solve.

The point of the change is not:

These two words must now be translated into Chinese.

Rather, it is:

These two user-facing display texts have now formally entered the language workflow.

In other words:

Entering the language workflow does not mean the text must change.

For Simplified Chinese, the final result could very well still be:

Plaintext
PDF → PDF
GitHub → GitHub

But now, this “unchanged” state is the result of formal language processing, rather than being coincidentally left as-is simply because the parser failed to recognize them.

This article organizes this issue, along with what was later added to address it:

A re-entry mechanism for structured semantic input expansion.


At first, I also placed too much importance on “whether the source file changed”

Many incremental workflows first check:

Plaintext
Source file unchanged → reuse existing results
Source file changed → recalculate affected scope

This logic is usually fine.

For example, after a Markdown page is actually modified, you can compare source identities and then determine which translation units need to re-enter the workflow.

But structured content adds an extra layer:

The original file does not directly equal the translation input the model actually receives.

There is a parser in between.

Take books.yaml as an example: the workflow does not just throw the entire YAML as-is to the model.

It first reads the structure, then identifies which fields belong to:

  • User-facing language content;
  • Machine structure;
  • URLs;
  • Identifiers;
  • Technical values that should not be modified;
  • Display text that can enter the formal language processing scope.

Ultimately, what actually enters Generation is the language semantics extracted by the parser from the original structure.

This can be simply understood as:

Plaintext
Original YAML
→ Parser parses structure
→ Extracts formal language semantic inputs
→ Generation
→ Rebuilds complete YAML according to original structure

So what determines the model’s task is not only:

What the original file is.

It also includes:

What the current parser considers to be formal language input.


This time, the change is not in the raw source, but in the semantic input

The actual state of books.yaml this time is clear.

The old state can be summarized as:

Plaintext
Original file: unchanged
Old semantic units: 10
Current semantic units: 12
Added: 2
Removed: 0
Old unit changes: 0

The additions are not two “words that must be translated into another language”.

More accurately, they are:

Two display text slots that previously did not enter the formal language semantic input, but are now recognized by the parser.

Their current source values are:

Plaintext
PDF
GitHub

The original 10 semantic units are all still present.

Their:

  • Unit ID;
  • locator;
  • source;
  • source SHA;
  • coordinates;
  • protection metadata;

have not changed.

What actually happened is simply:

The current parser recognizes more display content that should be explicitly handled by the language workflow than before.


Why PDF and GitHub also deserve to enter the language workflow

This is the most confusing part this time.

For Simplified Chinese:

Plaintext
PDF
GitHub

Most likely do not need modification.

So why not just leave them outside the language workflow?

The reason is:

The parser should determine whether a field is user-facing language content based on its structural semantics, rather than deciding based on whether the current English value “looks like it needs translation”.

Suppose a structured field has the value today:

Plaintext
PDF

Tomorrow, upstream might change it to:

Plaintext
Download PDF

Another one displays today:

Plaintext
GitHub

Later it might become:

Plaintext
View on GitHub

If the parser’s logic is:

“This field looks like a technical term right now, so it doesn’t enter the language workflow.”

Then it might continue to be overlooked when the field content changes in the future.

A more robust design should be:

Is this field itself user-visible display text?

If the answer is “yes”, then it should enter the language semantic input.

Whether the final target language changes the text is decided by subsequent language processing.

Therefore:

Plaintext
Entering the language workflow ≠ must rewrite the string

This is a very important distinction in this design.


“Keeping it as-is” can itself be a correct localization result

Previously, it was easy to understand “localization” as:

English must be changed into another language.

But in reality, not all user-visible content must change.

For example:

  • Brand names;
  • Product names;
  • Abbreviations;
  • Standard names;
  • API names;
  • Certain common technical terms;

In many locales, these should remain as-is.

So:

Plaintext
source = PDF
target = PDF

does not automatically mean:

Not translated.

It could also mean:

This display text has entered the formal language workflow, and it has been confirmed that it should remain as-is.

The real question is not whether the characters changed.

But rather:

Whether it has been formally processed.

This is actually very similar to the issue encountered in the previous article about code comment completeness.

The final page “looking fine” does not mean the workflow has proven:

All content that should have been processed actually went through the workflow.


If you only look at the source SHA, you will reach the wrong conclusion

Suppose the system only checks:

Plaintext
old source SHA == current source SHA

The result holds.

Then the easiest conclusion to make is:

The file hasn’t changed, so the old translation remains valid.

But the old Generation actually only faced 10 formal semantic inputs.

The current formal inputs have become 12.

Thus:

Plaintext
Current formal semantic inputs: 12
Old Generation coverage: 10
Current new inputs: 2

At this point, if the old Generation is still deemed the current complete result, it is equivalent to saying:

An output that does not cover all current formal inputs can continue to be recognized as current.

This is clearly not true.

Therefore:

Currentness cannot be bound solely to the original file; it must also be bound to the formal semantic inputs produced by the parser.


The original file hasn’t changed, so why do the parser results change?

Because the parser itself is also part of the workflow.

Early implementations might have considered certain fields to be:

Technical tags that do not need to enter localization.

Later, during actual page and structural semantic checks, it was discovered that:

These fields are actually display text that users will see.

As a result, the parser’s formal semantic rules changed.

The same:

Plaintext
_content/learn/books.yaml

The old parser yielded:

Plaintext
10 formal language semantic units

The current canonical parser yields:

Plaintext
12 formal language semantic units

So:

Unchanged raw source bytes do not mean the Generation’s semantic inputs haven’t changed.

Furthermore:

How the parser interprets the source is itself part of the model’s task.


This is not an ordinary “file invalidation”

At first, this situation is easily classified simply as:

Plaintext
stale

This is not entirely wrong, of course.

The old result has indeed become invalid relative to the current semantic inputs.

But just saying it’s “invalid” is not enough.

Because there could be many different reasons for the old result becoming invalid:

  • Source change;
  • Glossary change;
  • Unified Review change;
  • Parser protection rule change;
  • Authority change;
  • Expansion of the formal semantic input scope.

These situations cannot all use the same recovery method.

The most important fact this time is:

The old 10 semantic units are not wrong, nor have they changed.

They are simply:

No longer the entirety of the current complete input.

This requires a more accurate expression than “invalidate everything”.


But you also can’t just carry over the old result as current

Since the old 10 units have all remained unchanged, another natural thought is:

Then just inherit the old 10 results directly, and add the 2 new ones, right?

That sounds reasonable.

But there is another important boundary in the formal workflow.

books.yaml is a complete structured asset.

The formal Generation corresponds to:

The language output of the current complete structured asset.

The internal 10 or 12 slots are mainly used for:

  • parser;
  • protection;
  • comparison;
  • reconstruction;
  • impact analysis.

They are not 12 independent formal TranslationUnits.

So you cannot secretly turn the workflow into:

Plaintext
Old 10 results
+ Newly generated 2 results
→ Automatically stitched into a new formal Generation

Otherwise, a problem arises:

The new formal result does not correspond to a real, complete Generation input and output relationship.

This makes the provenance inaccurate.

Therefore:

The old result can serve as predecessor context, but it cannot masquerade as the current complete Generation.


This is also not ordinary structured recovery

The project already has structured recovery.

It is suited for handling another class of problems.

For example:

The model has actually completed Generation for the correct, complete semantic inputs, but the Bundle later became stale due to transport, authority representation, or compatibility changes.

If it can be strictly proven that:

  • The original model input truly exists;
  • The original output is complete;
  • The current semantic inputs have not changed;
  • The current validator still accepts them;
  • The change is only in transport or compatibility authority;

Then that already completed real work can be recovered.

The essence of this recovery is:

Proving that work done in the past still holds true today.

But books.yaml 10→12 is not this situation.

Because the old model invocation never faced the two newly added formal semantic inputs at all.

So:

You cannot use structured recovery to patch an old Generation that truly lacks current inputs into being current.

This is not a transport issue.

It is:

semantic input expansion.


So later a new formal state was added: semantic input expansion

What ultimately needs to be expressed is this kind of situation:

Plaintext
raw source unchanged
→ old semantic units all exactly preserved
→ current semantic inputs are a strict superset of the old set
→ at least one new formal language semantic unit added
→ need to re-execute complete structured Generation

The project later explicitly defined this as:

Structured semantic-input-expansion reopen

Which is:

Structured semantic input expansion re-entry.

The “re-entry” here is very important.

It is not declaring:

The old translation was wrong.

Nor is it declaring:

The old Generation was invalid at the time.

What it truly expresses is:

The old Generation remains a valid historical fact for the 10 formal inputs at that time, but it can no longer prove the complete result of today’s 12 formal inputs.


Why it must be proven to be a “strict superset”

Suppose the old state was:

Plaintext
A
B
C

And the current state becomes:

Plaintext
A
B
C
D

This can be considered a pure expansion.

All old content is preserved, only D is added.

But if the current state becomes:

Plaintext
A
B'
C
D

Then the situation is different.

Because B has changed.

Or for instance, if the current state becomes:

Plaintext
A
C
D

Then the old B has disappeared.

None of these can be called:

Pure semantic input expansion.

So the formal check requires:

The old set must be entirely exact preserved, and the current set can only be added to the original basis.

Expressed as a set relationship:

Plaintext
old semantic inputs ⊂ current semantic inputs

And it must be a true strict superset.

It cannot be just:

  • ID algorithm changes;
  • Coordinate changes;
  • Parser remap;
  • Renumbering of old units;
  • Unprovable old-new mappings.

If it cannot be strictly proven, stop.


Why a read-only preflight must be done first

This kind of re-entry cannot just see:

Plaintext
10 → 12

and immediately create a new task.

Because first it must be proven that:

This is truly a semantic input expansion, and not some other change misjudged as an expansion.

So the formal mechanism first does a read-only preflight.

It needs to confirm at least:

  • historical completion is still verifiable;
  • baseline Generation is still verifiable;
  • baseline closure is still verifiable;
  • accepted output still exists;
  • locale, package, asset, path bindings are correct;
  • raw source bytes have not changed;
  • contributor identity has not changed;
  • non-slot Document context and dependencies have not changed.

Most critically:

Every old Unit must exist exactly as it was.

Including:

  • ID;
  • locator;
  • source;
  • source SHA;
  • coordinate;
  • protection metadata.

At the same time, it must be confirmed that:

Plaintext
removed = 0
changed = 0
added > 0

Only then can it be formally classified as:

semantic-input-expansion.


Why books.yaml meets these conditions

The actual result this time exactly meets these requirements.

It can be condensed into:

Plaintext
raw source bytes changed: no
old semantic units: 10
current semantic units: 12
added: 2
removed: 0
old unit changes: 0

The two display text slots newly entering the formal language semantic input have current source values of:

Plaintext
PDF
GitHub

It needs to be specially emphasized that:

“Added” here means newly added to the language workflow, not newly added to the original YAML file.

It also does not mean:

PDF and GitHub must be translated into Chinese.

For zh-CN, they can perfectly well continue to remain:

Plaintext
PDF
GitHub

The real change is:

They are now explicitly handled by the formal language workflow, and are no longer in an area uncovered by the parser.


Why this “unchanged” state is more reliable than before

In the old state, PDF and GitHub might have ultimately appeared on the page just the same.

On the surface, it looked like:

There is no problem with the page.

But what the system could actually prove at that time was only:

The parser did not send them into the language workflow.

It could not answer:

Should they have remained as-is in the first place?

Are they user-visible text?

Were they missed?

After the new parser incorporates these display texts into the formal semantic input, the system can finally express:

Plaintext
This field belongs to the language workflow
→ After processing by the current target language, it still retains its original value

This is different from:

Plaintext
This field never entered the language workflow
→ Therefore it coincidentally retained its original value

These are two completely different states.

The final bytes might be the same.

But the strength of evidence is completely different.


Why generate a reopen receipt

The read-only preflight can prove that:

The current state indeed matches semantic-input-expansion.

But subsequent Generation Bundles, closures, and other readers still need to know:

Why is this structured asset, which had already become stale, now formally allowed to re-enter Generation?

So the apply phase will generate a formal reopen receipt.

It will bind:

  • Old Document identity;
  • Current Document identity;
  • structure/source/raw SHA;
  • Old Unit inventory;
  • Current Unit inventory;
  • Complete newly added semantic atoms;
  • accepted target SHA;
  • baseline Generation;
  • baseline closure;
  • baseline Surface;
  • Current glossary;
  • Current Unified Review.

This receipt is not a manual note saying:

“This can be retranslated here.”

It is machine-verifiable causal evidence:

Why re-entry is needed, and what exactly the newly added formal inputs are.


The receipt does not translate anything for you

This is also very important.

Having a reopen receipt does not mean:

Plaintext
PDF processed
GitHub processed

It will not:

  • write target;
  • automatically fill slots;
  • create language quality levels;
  • create package closure;
  • create Surface PASS;
  • execute activation;
  • enter Production.

It only provides a formal authority:

This structured asset indeed needs to re-enter Generation now, and the reason for re-entry has been strictly proven.

After that, the real language work still has to continue.


Why the new Generation still requires a complete output of 12 units

Here is another question that easily arises:

Since the old 10 have not changed, why can’t the new Generation just return the two items PDF and GitHub?

The reason is still the formal asset boundary.

The formal object of books.yaml is:

The complete structured asset.

Not two patches.

So the new Generation Bundle will carry:

  • Current complete Document;
  • Current raw source;
  • baseline accepted output;
  • predecessor context;
  • reopen receipt;
  • current glossary;
  • current relevant authority.

The model can see the old state as context.

But ultimately it still needs to output:

The target corresponding to the complete current asset.

This ensures that:

  • provenance is authentic;
  • reconstruction is complete;
  • schema still holds;
  • current exact set is fully covered.

Why not upgrade all 12 slots to formal TranslationUnits

This is very similar to the issue mentioned in the previous article about code comment completeness.

Just because the internal granularity is fine, does not mean the formal governance unit should also become finer.

For books.yaml:

Plaintext
12 semantic slots

are mainly used for:

  • parser;
  • protection;
  • exact comparison;
  • reconstruction;
  • stale detection;
  • impact analysis.

But the formal asset is still:

Plaintext
_content/learn/books.yaml

If every display field were upgraded to an independent TranslationUnit, it would introduce:

  • more Generation states;
  • more review states;
  • more closure combinations;
  • more recovery branches;
  • more complex currentness relationships.

And these fields are inherently part of the same structured resource.

So the current design still insists that:

Internal granularity is for proof, while the formal asset boundary is for governance.


After re-Generation, not a single subsequent gate can be skipped

semantic-input-expansion reopen is easily misunderstood as:

Since it’s been proven that only two fields were added, the regenerated output should be able to go live directly.

That is not actually the case.

After the new complete Generation is finished, it still has to go through:

Plaintext
Structured Generation
→ import
→ current validation
→ package closure
→ Integrated Surface Review
→ activation
→ preview
→ Production

Because the new output is, after all, a new formal target.

It must re-prove that:

  • current schema is correct;
  • glossary is valid;
  • reconstruction is correct;
  • structured output is complete;
  • there are no site integration issues after page assembly;
  • activation binds to current authority;
  • Preview behavior is correct;
  • Production display is normal.

So:

“The reason for re-entering Generation has been proven” does not equal “the new Generation has been proven correct”.


This also made me rethink stale

Before, when I saw stale, it was easy to understand it as:

The old thing is now wrong.

But now I prefer to understand it as:

The old evidence is no longer sufficient to prove the current state.

The difference between these two concepts is significant.

The old Generation of books.yaml did not suddenly become a wrong translation.

What it faced at that time was:

Plaintext
10 formal semantic inputs

For those 10 inputs, it remains a valid historical fact.

It cannot continue to be treated as current today because:

Plaintext
Current formal inputs = 12
Old Generation proof scope = 10

So the old result is not:

Wrong.

But rather:

The scope of proof is no longer sufficient.


Historical validity does not mean it is still complete today

This is another boundary that v2 has been emphasizing.

Now that the parser has become more complete today, you cannot retroactively say:

The old Generation was invalid at the time.

That is using today’s rules to retroactively rewrite history.

The old Generation was bound at the time to:

The semantic inputs formally accepted at that time.

It should continue to exist as a historical fact.

The new parser can only show that:

From the current state onwards, the old result can no longer satisfy current eligibility.

So we need to distinguish between:

historical validity: whether it was valid historically.

And:

current eligibility: whether it is still sufficient as a current result today.

Historical validity does not mean it is still complete today.

Being incomplete today also does not mean history should be deleted.


This is very similar to database schema migration

Later, I felt this problem could be understood using database migration.

Suppose an old record previously only had:

Plaintext
name
url
description

The system correctly processed these three fields at the time.

Later, the business model formally added:

Plaintext
pdf
github

The old record would not suddenly become:

Wrong data.

But it can no longer fully satisfy the new schema.

At this point, the system needs to:

Clearly identify the schema expansion, and then enter migration.

Instead of:

Plaintext
Database original file unchanged
→ So nothing needs to be done

The semantic input expansion in structured translation is essentially a similar issue.


The parser itself is actually also a formal dependency

This issue further exposed a fact that was not obvious enough before:

Translation results depend not only on source and glossary, but also on how the parser interprets the source.

The same source bytes:

Plaintext
parser v1 → 10 formal semantic inputs
parser v2 → 12 formal semantic inputs

For the model, these are already two different tasks.

So currentness cannot just look at:

Plaintext
source SHA

It must also be able to answer:

Is the formal semantic projection of this source by the current canonical parser still exactly the same as the old Generation?

This is also why “dependencies” in modern incremental workflows cannot be understood merely as:

File dependencies.

They also include:

Dependencies on the rules that interpret these files.


But you also can’t redo everything just because there is any code change in the parser

Here we also cannot go to the other extreme.

If the rule becomes:

Plaintext
parser code changed
→ all structured translations invalidated

That is equally too crude.

The parser internals might just be:

  • performance optimizations;
  • refactoring;
  • error message improvements;
  • implementation adjustments that do not affect a certain asset.

What should really be judged is:

Whether the final formal semantic inputs have changed.

If, after the parser update, an asset’s:

  • Unit set;
  • source;
  • coordinates;
  • protection metadata;

all remain exactly the same, then there is no need to force a re-translation just because the implementation code changed.

What should truly trigger invalidation propagation is:

A change in the formal semantic results.

Not:

The parser file has a diff.


So what really matters is not “whether the file changed”, but “whether the model’s task changed”

Writing up to here, this article can actually be condensed into one question:

Is the task required of the model today still the same as that original model task?

For this books.yaml, the answer is:

No.

Although:

Plaintext
raw source = same

But:

Plaintext
old semantic input count = 10
current semantic input count = 12

The scope of formal inputs the model actually faces has changed.

So the old Generation cannot continue to serve as current.


A more accurate currentness model

If understood in a very simplified way, it might have started as:

Plaintext
current =
source unchanged

Later it gradually became:

Plaintext
current =
source correct
+ semantic inputs correct
+ glossary correct
+ review authority correct
+ output corresponds correctly
+ current validator still accepts

If a real semantic change occurs in any of these layers, it could cause the old result to lose its current validity.

This design is indeed more complex.

But it avoids many:

The page looks fine, so the workflow is assumed to be complete.

false positives like this.


The most worthwhile principles to retain from this time

Through books.yaml 10→12, I think this design can be condensed into a few principles.

First:

Unchanged raw source does not mean unchanged Generation semantic inputs.

Second:

Entering the language workflow does not mean the target string must change.

Display text like PDF and GitHub can perfectly well remain as-is after formal processing in Simplified Chinese.

Third:

The parser should determine whether a field is language input based on its structural semantics, rather than deciding based on whether the current English value looks like it needs translation.

Fourth:

If the current semantic inputs are a strict superset of the old inputs, it can be clearly identified as semantic-input-expansion, rather than broadly categorized as ordinary stale.

Fifth:

The old units must all be exact preserved; when there are deletions, modifications, or ambiguous mappings, it cannot masquerade as a pure expansion.

Sixth:

The old Generation is retained as a historical fact, but being historically valid does not mean it is still sufficient to prove completeness today.

Seventh:

The reopen receipt is only responsible for proving the authority to re-enter the workflow; it is not responsible for generating translations, nor does it grant closure, Surface, activation, or Production permissions.

Eighth:

structured recovery can only recover real work that has already been completed for complete and correct inputs; it cannot be used to patch truly newly added semantic inputs.

Ninth:

Internal semantic slots are used for proof and reconstruction, and are therefore not upgraded into new formal TranslationUnits.

Ultimately, I think the most memorable sentence from this time is:

To determine whether a translation can still be reused, you cannot just ask “has the source file changed”; you must also ask “is the task required of the model today the same task as the original one”.

And this seemingly small case of PDF and GitHub exactly proves that:

Even if the final text has not changed by a single character, as long as it previously did not enter the formal language workflow and has formally entered today, then the workflow semantics itself has already changed.

系列导航

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系