fix(playback): ignore single-letter words as Cyrillic encoding evidence

Codex review round 7 on #1471: a minimal CP1252 caption like "À la"
yields one lone Cyrillic-looking letter that passed both guards. Single
letters carry no trustworthy script information — an isolated accent
decodes to a lone Cyrillic letter, while genuine Cyrillic dialogue always
contains multi-letter words — so only words of length >= 2 now count as
Cyrillic evidence. Files whose only high bytes are isolated letters are
fundamentally ambiguous and default to Windows-1252.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5 committed 2026-08-23 12:39:52 +02:00
1 parent 333d62fc1f
commit 9a7ecd7f4f
2 files changed
+25 -5

No files matched your search

@@ -76,6 +76,21 @@ describe('external-subtitle-cues.util', () => {
expect(decoded).toContain('À table');
});
it('keeps a minimal isolated-accent CP1252 caption Latin ("À la")', () => {
// One lone Cyrillic-looking letter and two ASCII letters passed
// the earlier share guard; single-letter words must carry no
// script evidence at all.
const cp1252 = [
0xc0, // À
...Array.from(
' la\n1\n00:00:01,000 --> 00:00:02,000\n'
).map((c) => c.charCodeAt(0)),
];
expect(decodeExternalSubtitleBytes(toBuffer(cp1252))).toContain(
'À la'
);
});
it('keeps an isolated accented CP1252 word Latin ("À table")', () => {
// À=0xC0 decodes under CP1251 to the pure-Cyrillic one-letter
// word "А"; without the letter-share guard that single vote
@@ -93,15 +93,20 @@ function chooseLegacySingleByteDecode(buffer: ArrayBuffer): string {
asciiLetters += word.length;
} else if (/[A-Za-z]/.test(word)) {
mixedLetters += word.length;
} else {
} else if (word.length >= 2) {
// A single letter carries no trustworthy script information: an
// isolated CP1252 accent ("\u00c0"/"\u00c9") decodes to a lone Cyrillic
// letter and must not count as Cyrillic evidence, while genuine
// Cyrillic dialogue always contains multi-letter words.
cyrillicLetters += word.length;
}
}
// Two guards: mixed-script words are strong evidence of misread Latin
// text, and isolated accented CP1252 words ("\u00c0 table" \u2192 "\u0410 table")
// masquerade as tiny pure-Cyrillic words \u2014 so Cyrillic must also carry a
// meaningful share of all letters before 1251 wins. Genuinely Cyrillic
// Two guards on top of that: mixed-script words are strong evidence of
// misread Latin text ("\u00e9tait" \u2192 "\u0439tait"), and Cyrillic must carry a
// meaningful share of all letters before 1251 wins \u2014 genuinely Cyrillic
// dialogue dominates its own letter count even with embedded Latin names.
// A file whose only high bytes are one or two isolated letters is
// fundamentally ambiguous; defaulting it to 1252 is the best effort.
const plausiblyCyrillic =
cyrillicLetters > mixedLetters * 2 &&
cyrillicLetters * 4 > asciiLetters;