What the two sources actually are, how the join was built, the four defects that had to be fixed before any finding was trustworthy, and what the result does and does not license anyone to say.
Ukrainian is a language with a large body of speakers, an urgent and growing population of adult learners, and, until very recently, almost no open machine-readable infrastructure for teaching it. The Ukrainian Frequency project exists to close pieces of that gap one thread at a time: an open frequency list, a validated CEFR overlay, paradigm tables, a derussification signal, a vocabulary-size test, a text profiler.
Two of those threads brought in resources built by other people. Both joins pointed the same direction: each source read against our spine. Neither read the two sources against each other.
That is the gap this crosswalk fills, and it is a real one, because the two sources are the only two published answers Ukrainian has to the question every beginner course must answer first.
“1000 i 1 slovo”, in two volumes: A1 by Kseniia Borodin and Oksana Turkevych (Slavic Language Education 3, LANGUAGE-LAB, Humboldt-Universitat zu Berlin, 10.18452/28236) and A2 by Kseniia Borodin and Olesia Lazarenko (Europa-Universitat Viadrina Frankfurt (Oder), 2026, 10.11584/opus4-1448).
Both CC-BY-4.0. Selection mixes GRAK and Ukrainian Web 2022 corpus frequency with usefulness at the learning stage. That second criterion is the important one: this is not a frequency list with a cut-off. It is a syllabus, and an expert judgment sits on every entry.
The unit is the published entry line, not the lemma. An entry can be a single word, an aspect pair (бачити – побачити), a gender pair (автор / авторка), a spelling variant set (в, у), or a phrase (день народження). 929 unique A1 entries reach 992 headwords; 1,000 A2 entries reach 1,137.
The Ukrainian Vocabulary Profile on the PULS platform (Roman Synchak, Vasyl Starko, Anastasiia Burak, Iryna Svystun; eLex 2025) is the one corpus-validated CEFR profile for Ukrainian: 10,194 lemma strings carrying a level from A1 to C1. Redistributed here by explicit permission from a co-author.
It is descriptive. It records where a lexeme’s earliest sense first appears in graded use. It is not telling anyone what to teach on Tuesday.
A syllabus and a descriptive profile answer different questions, so a disagreement between them is not an error by either. The whole design follows from that: it classifies disagreements, it does not score them, and the class names are deliberately neutral (earlier, later) rather than evaluative.
The one class that could be read as a criticism, unprofiled, gets an explicit note: those 172 words are mostly proper nouns and the adjectives built on them (Україна, Київ, Польща), which a vocabulary profile does not set out to cover. Scope, not gap.
Frequency ranks come from this project’s own top-12,000 lemma list, derived from UberText 2.0 (Chaplynskyi 2023, lang-uk), CC-BY-4.0.
The spine is a convenience, not evidence. It supplies an ordering so a reader can see whether a disagreement is about a common word or a rare one. It is automatically lemmatized and carries its own errors; the worst is рок at rank 97, which is absorbing tokens of рік (year, rank 13).
6,318 of the 10,366 crosswalk rows fall inside the top 12,000. The rest are off the spine and carry a blank rank, which is why the profile is read from its whole published extent rather than from its top-12,000 slice: a minimum word the corpus never reached can still be compared.
Both sides join by lemma string, folded for exactly two things: the combining stress accent (U+0301), which is pedagogical rather than lexical, and the apostrophe, across all four glyphs the sources use and the one HTML entity the PULS harvest preserved (').
Nothing else is normalized. No stemming, no case folding beyond lowercase, no fuzzy matching. Membership is per headword: every word an entry names counts as carried, so в, у puts both prepositions in A1.
The authors’ typography turns out to be precise and worth following exactly.
| Pattern | Meaning | Example | Headwords |
|---|---|---|---|
| spaced en dash, / , ; = ≠ | alternative words | бачити – побачити | both |
| spaced parenthesis | a separate word | вперед (уперед) | both |
| unspaced parenthesis | continues the word it touches | вмивати(ся) | вмивати, вмиватися |
| semicolon inside a parenthesis | an alternative | іти (йти; іди!) | іти, йти |
| comma inside a parenthesis | a paradigm, not headwords | мій (моя, моє, мої) | мій only |
| no single-word piece | a phrase entry | день народження | none |
The spaced / unspaced distinction is exact across both volumes: A1 has 47 spaced parentheses and 0 unspaced; A2 has 3 spaced and 19 unspaced, and all 19 are the reflexive (ся) plus one Бувай(те)!
An absent row is the one that invites a wrong reading, so each carries a deliberately over-matching false-positive filter. Four tests, strongest first: phrase (the word appears inside a multi-word entry, 19 rows), family (a covered headword shares a Wiktionary derivational family), contains (one string contains the other, both at least 5 characters), and stem (a long shared initial stem).
It over-matches on purpose: закрити is flagged against закричати, процес against процент. The point is that whatever survives unflagged is conservative. Of 84 validated-A1 absences, 40 survive.
In a four-source join, the failure mode is not a crash. It is a plausible sentence about somebody’s professional work that happens to be false.
Every one of these was silently producing findings that were not real.
The PULS harvest kept the platform’s rendering verbatim, so 135 rows spell the apostrophe '. Nothing folded it, so no apostrophe lemma ever joined, and сім’я read as a word the minimums teach and the profile has no level for. It is the third time this class of bug has appeared in this project, which is why the fold now lives in one exported fold() tested against all four spellings of one word.
67 rows of the PULS snapshot quote a lemma field containing a comma:
17,21344,14926,"у, в",,A1,прийменник,значення
Splitting on every comma shifts every column after it, so those rows silently acquired the wrong level. Note what the damaged row was: “у, в” at A1, the exact pair whose treatment turns out to matter most below.
Бувай(те)! was yielding те as a headword, which put те (rank 27, validated A1) into the A2 volume and out of the absent list.
бути (є, був, була, були, буду, будеш …) was yielding є, був, була and the rest as A1 headwords. They are inflected forms, not lemmas.
Not the test suite. Every one was caught by reading the output and finding a claim that looked wrong: сім’я cannot really be missing from an A1 minimum; те cannot really be an A2 headword; an A1 list cannot really omit для.
The last of those turned out to be true, which is the finding below, and the only way to know the difference was to check each one against the raw source. The tests were then written to lock the fixed behavior. They are a ratchet, not a detector.
Rows are the minimum volume that carries the word; columns are the validated level.
| Volume | A1 | A2 | B1 | B2 | C1 | none | total |
|---|---|---|---|---|---|---|---|
| A1 volume | 710 | 197 | 26 | 5 | 1 | 53 | 992 |
| A2 volume | 174 | 490 | 271 | 79 | 4 | 119 | 1,137 |
| neither | 84 | 692 | 2,088 | 3,438 | 1,935 | 0 | 8,237 |
| total | 968 | 1,379 | 2,385 | 3,522 | 1,940 | 172 | 10,366 |
Two efforts built by different methods, one expert and one corpus-validated, place 1,200 words at the same CEFR level. That is a convergence result: the beginner core of Ukrainian is a real object that two independent methods can find.
Of the 174 words the profile validates earlier than the syllabus teaches them, the first by corpus rank are prepositions.
| Word | Rank | Profile | Volume |
|---|---|---|---|
| до | 9 | A1 | A2 |
| для | 21 | A1 | A2 |
| від | 22 | A1 | A2 |
| якщо | 87 | A1 | A2 |
| без | 130 | A1 | A2 |
And the A1 volume does carry по, про and через. So this is not an omission, it is a line: three prepositions at A1, the rest deferred.
The obvious hypothesis is case government. По, про and через are learnable early as fixed collocations; до, для, від and без all govern the genitive, and a learner without the genitive cannot use them productively. If that is the reasoning, it is a good one, and it is invisible in the published list.
A word list records decisions but not the reasons for them. Putting two lists side by side makes the reasons ask to be written down.
583 words go the other way: the syllabus teaches them before the profile validates them. The head of that list is та (11, A1 volume, A2 profile), свій (25, A2 volume, B1 profile), весь (41, A1 volume, A2 profile), по (45, A1 volume, A2 profile), інший (46, A1 volume, A2 profile). These are the syllabus being deliberately ahead, which is what a syllabus is for. A course cannot wait for свій until B1.
84 validated-A1 and 692 validated-A2 words appear in neither volume. After the relative filter, 40 and 416 survive unflagged. The A1 residue, in corpus order, begins: те (27), ситуація (160), кожний (200), момент (449), музей (748), ну (805), мільярд (824), продукт (831), герой (838).
A 1,000-word A1 syllabus omitting ситуація and момент is a defensible choice; omitting them unknowingly is not. The list makes the choice visible.
Restating, because these travel with every number above.
absent column is candidates, never omissions.Limits 1 and 3 could both be reduced by a part-of-speech-tagged edition of the minimums. That is a question for their authors, not something a join can fix.
Three tiers, the pattern the whole surface uses.
committed snapshots src/data/ukrainian-frequency/ -> pure builder src/lib/ukrainian-frequency/minimums.mjs -> generated tier src/lib/ukrainian-frequency/minimums.data.mjs -> frozen hashed release /downloads/ukrainian-frequency/minimums-crosswalk/v1.0.0/ npm run uk-freq:build-minimums rebuild npm run uk-freq:check-minimums verify byte-for-byte, no writes
The builder is pure: it takes parsed files and returns rows. It touches no disk, no network and no clock, so it runs unchanged in the test suite.
18 tests cover the join. They re-derive every published figure from the snapshots, compare the frozen CSV byte for byte, and check each file against its own SHA-256. Marginals must close against the independently-known headword counts, which is what would catch a parser change that quietly moved words between volumes. 6 more cover the hub’s grid and its accessibility.
The reader’s browser then re-derives seven of the figures again on the hub page, including that the grid’s own cells sum to the published class counts, so the picture and the numbers cannot drift apart.
Things this cannot answer that someone should.
I design and ship AI tools, full-stack apps, and data pipelines — end to end, to production. Tell me the problem in a sentence; I'll give you an honest read on fit within a day.
Work with me →