fix: don't create duplicate languages from retired ISO 639-3 codes (BL-15916) - #154
Merged
Conversation
… codes https://issues.bloomlibrary.org/youtrack/issue/BL-15916 langtags.json sometimes keeps a code that ISO has since retired in an entry's iso639_3 field even though the entry's own tag already uses the surviving code, e.g. tag "enm-Latn-IE" with iso639_3 "yol", or tag "jkp-Zyyy-MM" with iso639_3 "kpp". We were filing those entries under the retired code, which minted a second, duplicate card for a language we already list under its current code. Where the surviving code belongs to a *different* language the duplicate also inherited the wrong name and subtag. That was user-visible: yol (Yola, a language of Ireland) was showing up as "Middle English (1100-1500) - A language of Ireland" carrying the subtag enm. Selecting it would have written the tag enm onto a collection, searching "Yola" found nothing, and because the entry was keyed yol it slipped past the deliberate enm historic-language exclusion in defaultExcludedHistoricLanguages.ts, so we showed a historic language we mean to suppress. This surfaced when the langtags.json API 1.4 update brought in an iso-639-3.tab that retires yol. So when an entry's iso639_3 code is no longer in iso-639-3.tab but its tag's language subtag is, trust the tag. Entries whose retired code matches their own tag (33 of them, e.g. aoh/Arma) are left alone - there is no surviving code to fold them into and they behave as before. Regenerating languageData.json removes exactly five duplicates, folding their regions, scripts and tags into the surviving entries and nothing else across the other 7,953 entries: yol "Middle English (1100-1500)" [enm] -> enm kpp Paku Karen [jkp] -> jkp jeg Oy [oyb] -> oyb dek Suma [sqm] -> sqm tpw Tupinamba [tpn] -> tpn The retired codes stay searchable via alternativeTags, so kpp/jeg/dek/tpw (and skk/thx) still find the right language. Yola is no longer offered at all: the current iso-639-3.tab has retired yol and no source we have still carries the name "Yola", so there is no correct entry we can build for it. Verified: find-language 191 tests, language-chooser-controller 97, language-chooser-react-hook 16, language-chooser-react-mui e2e 114 - all pass. tsc typecheck clean. Checked in the running react-mui demo that "Middle English" no longer offers the Ireland card and that "Paku Karen" returns a single card. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…test messages https://issues.bloomlibrary.org/youtrack/issue/BL-15916 Two test-only follow-ups found while preflighting the retired-ISO-code fix. scrollListToLanguageCard gave up as soon as the results list could not scroll any further, but search results arrive in batches - so "we reached the bottom" does not mean the card is absent, it may just not have streamed in yet. Under load (full suite running) that raced and "Search with typos gets fuzzy matches" failed intermittently; the same test passed 3/3 when run alone. Sweep from the top repeatedly until the card mounts or a 15s budget runs out, instead of one sweep with an early break. Full e2e suite now 114/114 twice in a row. Also reworded two assertion messages in the new retired-ISO-code tests. The first said searching "yol" "should still find enm", which is true of the raw search but not of what a user sees - defaultSearchResultModifier then filters enm out as a historic language. Say "raw search ... should still reach" and note the distinction, so the next reader is not misled into thinking Yola is reachable in the UI. Reordered the second test's loop to put yol first, since that is the assertion actually carrying the regression guard. No behavior change; no production code touched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
https://issues.bloomlibrary.org/youtrack/issue/BL-15916 Comments only - no logic change, and regenerating languageData.json produces a byte-identical file. Devin's review of the retired-ISO-code fix raised two informational notes worth recording where the next person will see them: - addOrCombineLangtagsEntry does not overwrite `exonym` on the combine path, so a folded retired/surviving pair keeps whichever entry langtags listed first. All five current pairs carry the same `name` in langtags so it makes no difference today, but a future pair with differing names listed retired-first would inherit the retired language's name - exactly the symptom this block prevents. Noted in langtagProcessing.ts as something to re-check after a langtags update. - scrollListToLanguageCard now deliberately sweeps until its timeout, so using it to assert a card is *absent* would burn the full 15s. Both current callers assert presence; noted in e2eHelpers.ts so it stays that way. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Author
|
[Claude Opus 5 from Andrew Polk's machine during preflight] Consulted Devin on 2026-07-29 up to commit Clean: no Bugs and no Investigate flags, so nothing was mirrored as a review thread. Four Informational items, and it is worth saying what they were rather than just counting them:
Both of those last two are recorded in commit CI ( |
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.
Follow-up to #149. Found while auditing the QA test steps on the card: the langtags.json API 1.4 update introduced a user-visible bad language entry that no test step covered.
The bug
langtags.json sometimes keeps a code that ISO has since retired in an entry's
iso639_3field even though the entry's owntagalready uses the surviving code — e.g.tag: "enm-Latn-IE"withiso639_3: "yol", ortag: "jkp-Zyyy-MM"withiso639_3: "kpp". Because the pipeline keys generated entries offiso639_3, we filed those under the retired code and minted a second, duplicate card for a language we already list under its current code.Where the surviving code belongs to a different language, the duplicate also inherited the wrong name and subtag. That was the user-visible symptom:
yol(Yola, a language of Ireland) was showing up in the language chooser as:carrying the language subtag
enm. Three consequences, all confirmed in the running react-mui demo:enmonto a collection;enmis on our deliberate historic-language exclusion list, but because the entry was keyedyolit slipped past the filter — so we were showing a historic language we intend to suppress.The new
iso-639-3.tabthat came with the API 1.4 update retiresyol, which is what triggered it.The fix
In
parseLangtagsJson: when an entry'siso639_3code is no longer iniso-639-3.tabbut the tag's language subtag is, trust the tag. Entries whose retired code matches their own tag (33 of them, e.g.aoh/Arma) are deliberately left alone — there is no surviving code to fold them into, and they behave exactly as before.Regenerating
languageData.jsonremoves exactly five duplicates, folding their regions, scripts and tags into the surviving entries and changing nothing else across the other 7,953:yol"Middle English (1100-1500)"[enm]enmkppPaku Karen[jkp]jkpjegOy[oyb]oybdekSuma[sqm]sqmtpwTupinambá[tpn]tpnRetired codes stay searchable via
alternativeTags, sokpp,jeg,dek,tpw(andskk,thx) still find the right language.Yola is no longer offered at all. The current
iso-639-3.tabhas retiredyol, and no source we have still carries the name "Yola", so there is no correct entry we can build for it. Suppressing it beats offering it under another language's name.Verification
find-language191 tests,language-chooser-controller97,language-chooser-react-hook16,language-chooser-react-muie2e 114 — all pass.tsctypecheck clean;eslint0 errors.languageSearch.spec.ts: one covering all five retired/surviving pairs (no duplicate entry, retired code still folded in and still searchable), one guarding the specificenm/yolsymptom.Also on the card, not in this PR
Auditing the test steps turned up three that were simply wrong (they asked testers to confirm country lists on the English and French cards, which are stripped on purpose; and to search for a "Kurdish Sign Language" that does not exist in the data). Those have been corrected on BL-15916, and the newly-added
dylBhutanese Sign Language entry reading "A language of Turkey" has been reported upstream — that one is upstream source data, not ours.Ref: https://issues.bloomlibrary.org/youtrack/issue/BL-15916
🤖 Generated with Claude Code
This change is
Devin review