feat(translate): apply vars presets from .yfm behind --presets - #2297
Open
martyanovandrey wants to merge 7 commits into
Open
martyanovandrey wants to merge 7 commits into
martyanovandrey wants to merge 7 commits into
Conversation
martyanovandrey
requested review from
a team and
diplodoc-bot
as code owners
September 22, 2026 18:47
martyanovandrey
requested review from
separatrixxx and
stenin-nikita
and removed request for
a team
September 22, 2026 18:47
`yfm translate` took variables only from `--vars` and ignored
presets.yaml, so a project whose build selects its variables with
`varsPreset` in .yfm had to repeat them for translation by hand, and a
run without them evaluated every condition on an unknown variable as
true: on YT the merge of `yandex-specific/toc-internal.yaml` behind
`when: audience == "internal"` was applied to the public translation.
Presets now apply as for build. The run loads every presets.yaml under
the input; a file takes the `varsPreset` section of each presets.yaml on
its path merged with the `default` section, and `--vars` overrides them.
The preset comes from `--vars-preset`, then the command's own config
section, then the enclosing sections of the same .yfm (the root for
translate, `translate` and the root for seed), then `default`. The
presets of the source file apply to both sides of a seed alignment, so
the units stay in parity with the translate run.
`translate extract` and `translate compose` are untouched: the XLIFF for
external tools keeps taking variables from `--vars` only.
Checked on yt/docs with its `.yfm` (`varsPreset: public`) and no `--vars`:
`ru/user-guide/problems/mapreduce-debug.md` translates as the public page
(16 units), the internal merge no longer leaks; `--vars '{"audience":
"internal"}'` still selects the internal page (57 units).
…from the raw .yfm
Two review findings on the presets change:
- `extract` shares the `Run` with `translate`, so the presets started to
reach the XLIFF for external tools: with `default: {audience: internal}`
in presets.yaml a block behind `audience == "public"` and a toc item
behind the same `when` vanished from the extraction. `Run` takes
`usePresets` as an option now and `extract` turns it off; an e2e case
extracts a fixture where either preset would drop one of the blocks and
checks that both stay.
- The command's own `varsPreset` was read from the resolved config, where
the config defaults already put `default`, so a section could not select
`default` over a root preset: `varsPreset: internal` at the root with
`translate: {varsPreset: default}` still gave `internal`. The sections
are now read from the .yfm itself, the command's own section first, with
a strict lookup that skips a missing section instead of falling back to
the root out of order.
Regular translation runs must not change: presets now apply only with `--presets` (or `presets: true` in the translate section of .yfm), so a run without it evaluates conditions exactly as before, presets.yaml or not. `seed` follows the translate section when its own is silent, as with the code mode, so both commands split files the same way. `--vars-preset` selects the preset for `--presets`; the .yfm root still supplies the default. Extract stays off regardless.
martyanovandrey
force-pushed
the
feat/translate-vars-presets
branch
from
September 23, 2026 08:47
4e0e7e6 to
bbc4723
Compare
…ead the translate section in seed
Two review findings on --presets:
- Conditions took the presets of the source file, so `lang: ru` from
ru/presets.yaml kept the Russian branch of `{% if lang == "ru" %}` and
the English page got the link to the Russian chat (10 pages on YT).
A file is now judged under the presets of its translation, ru/x.md as
en/x.md, the way the build of the target language sees it. The seed
takes the same vars on both sides, so its units stay in parity with
the translate run.
- `translate seed` lost the path of a .yfm without a `translate.seed`
section (the strict scope falls back to the defaults), so it ignored
`presets: true` of the translate section, and `code` the same way:
the seed split files differently from the translate run. YT has
exactly such a .yfm. The seed now reads the translate section from
the file itself.
| varsFor(path: string, targetLanguage: string) { | ||
| const file = normalizePath(path); | ||
|
|
||
| return this.vars.for(file, languagePath(file, this.config.source.language, targetLanguage)); |
Contributor
There was a problem hiding this comment.
The content is processed using the target language presets, while dependencies use the source language presets. When --presets and translate.filter: true are used, a file included within a {% if lang == "en" %} block is excluded from the translation—even though the include statement itself remains—resulting in a link to a missing file.
On Windows the temp dir comes as an 8.3 name (RUNNER~1). The run takes its scope from realpathSync, which keeps the short name, and checks each read against the async realpath, which expands it, so presets.yaml of the project was refused as out of scope and the presets case failed.
Review finding: the content was judged under the presets of the
translation, while the files of the run - the toc items and merges and
the includes that `translate.filter` follows - were still judged under
the source ones. With `--presets` and `filter: true` a file included under
`{% if lang == "en" %}` stayed out of the translation while the English
page kept its include, a link to a missing file; a toc item under the same
condition pointed to a page that was not translated.
With presets on, the run now takes a vars service that answers every
lookup with the presets on the path of the target file, so tocs,
includes, merges and the content agree. That needs one target language
per run: `translate` and `seed` refuse several with `--presets`, the
neurotranslate cube calls them per language anyway. Providers go back to
plain per-file vars.
Liquid re-serializes the frontmatter of a document through YAML: indentation, quotes and a no-break space written as `\_`. A translation is composed from that text, so with --presets, where every file gets vars, each translated file got a reformatted frontmatter; and the escaped space broke the extraction of a heading that repeats the title, which failed the seed of the existing translation (Tracker docs: 11 more files unseeded with presets). `applyConditions` keeps the frontmatter as written unless a condition in it changed a value; both the units loader and the yandex provider use it. An e2e case guards the parity of seed and translate under presets taken from the translate section of .yfm, the way the neurotranslate cube runs: every unit comes from the seed, one page seeded from a translation that kept its conditions and one from a translation without them.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What
yfm translatetakes variables only from--varsand ignorespresets.yaml(the option says so). A project whose build selects its variables withvarsPresetin.yfmhas to repeat them for translation by hand, and a run without them evaluates every condition on an unknown variable as true. On YT that meant the merge ofyandex-specific/toc-internal.yamlbehindwhen: audience == "internal"was applied to the public translation (DOCSTOOLS-6814); the current workaround duplicatesaudience: publicfrompresets.yamlinto the cube config.How
An opt-in switch, so regular runs do not change:
--presets(orpresets: truein thetranslatesection of.yfm) makestranslateandseedapply presets as build does: the run loads everypresets.yamlunder the input (the sameVarsServicebuild uses); a file takes thevarsPresetsection of eachpresets.yamlon the path of its translation, merged with thedefaultsection;--varsoverrides both;translate.filterfollows, the content - takes the presets on the path of the target file,ru/x.mdtranslated into English asen/x.md. The source presets describe the source build: theirlang: ruwould keep the Russian branch of{% if lang == "ru" %}in the English page (10 pages on YT), and a file selection left on them would drop an include the English page keeps. A file outside a language directory (the input is that directory itself) keeps its own path;translateandseedrefuse several with a config error (the neurotranslate cube calls them per language anyway);presets.yamlis read, conditions are evaluated exactly as before, presets.yaml or not;--vars-preset, then the sections of the same.yfmin order -translateand the root fortranslate;translate.seed,translateand the root forseed- thendefault. The sections are read from the file itself, not from the resolved config (which already carriesdefaultfrom the config defaults), so a section can selectdefaultover a root preset. A.yfmwithout atranslatesection still contributes its rootvarsPreset;seedtakes the switch from thetranslatesection when its own is silent, as with the code mode, so both commands split files the same way (the unit texts are the cache keys); both sides of an alignment take the vars the translate run gives the source file. Thetranslatesection is read from the file itself: a.yfmwithout atranslate.seedsection (YT has exactly that) resolves the seed scope to the defaults and loses its path, and the seed ignoredpresetsandcodeof thetranslatesection - thecodepart of it predates this PR;\_), and with presets every file gets vars, so every translated file would get a reformatted frontmatter, and the escaped space broke the extraction of a heading that repeats the title - the seed of such a file failed (11 more unseeded files on the Tracker docs). The re-serialized frontmatter stays only when a condition in it changed a value; the units loader and the yandex provider share this;translate extractandtranslate composenever apply presets:RuntakesusePresetsas an option andextractkeeps it off, so the XLIFF handed to external tools keeps taking variables from--varsonly. An e2e case guards it with a fixture where either preset would drop a block.Follow-up
The neurotranslate cube copies only the
translate:section of the project's.yfminto its working directory, so the rootvarsPreset: publicof YT does not reach it. For ru -> en that no longer matters:en/presets.yamlof YT keepsaudience: publicandlang: enindefault, andtranslate: {presets: true}alone gives no internal merge, the publicmapreduce-debug.mdand the English chat link intry-yt.md(checked onyt/docs). For en -> ru (release notes)ru/presets.yamlsetsaudienceonly in itspublicsection, so YT needstranslate: {presets: true, varsPreset: public}- without the preset the internal toc merge is applied again. Thenvars: {audience: public}can go from its cube config.