Слово не переводится, а реплика рядом переводится: разбираю баг во встроенном переводчике Chrome
- понедельник, 12 октября 2026 г. в 00:00:02
Я сделал это расширение для себя. Смотрю фильмы и сериалы с английскими субтитрами, и раньше каждое незнакомое слово значило паузу и вкладку с переводчиком. Встроенный переводчик Chrome переводит страницы, а с субтитрами в видео не помогает. Поэтому у меня просто тултип: навёл на слово и сразу видишь перевод. Переводит Translator API, он встроен в Chrome и работает прямо на устройстве. О самом расширении я рассказывал в прошлой статье, тут только про один баг.
Я проверял расширение сам, как обычный зритель. Кликаю по слову «phenom», и в тултипе пишет «Языковая пара не поддерживается». При этом перевод всей реплики в том же тултипе есть. На другом сайте клик по слову «half» дал «Перевод не удался: Other generic failures occurred.», а реплика опять переведена. То есть модель скачана, пара поддерживается, а слово почему-то не переводится.
Разбор кода показал, что слово и реплика переводятся разными путями. Слово переводит Translator того фрейма, где лежит плеер. Реплика уходит другим путём, через service worker расширения. Поэтому когда слово отказывает, по реплике этого не видно. Запасного пути у слова не было: любая ошибка сразу шла в тултип.
Первая. Любое исключение выдавало себя за «пара не поддерживается». Метод availability() превращал любую ошибку, кроме таймаута, в ответ 'unavailable', а интерфейс на 'unavailable' писал «пара не поддерживается». У настоящего 'unavailable' причина может быть неочевидной. В документации Chrome сказано, что Translator API по умолчанию доступен только верхнему окну и iframe того же origin. Iframe с чужого домена получает его только с атрибутом allow="translator", а встроенные плееры обычно как раз такие. Расширение работает во всех фреймах, так что слово в таком плеере упирается в запрет, а реплика идёт другим путём и переводится. Оговорка: на сайтах из отчёта я это руками в живом Chrome не проверял, так что это вероятная причина, а не доказанная.
Вторая. Умерший экземпляр переводчика жил в кэше до перезагрузки страницы. «Other generic failures occurred.» - это текст исключения UnknownError. Так встроенные AI-API Chrome отвечают на внутренний сбой, например когда сервис моделей перезапустился. Экземпляр кэшировался навсегда, а translate() вызывался на нём без повтора и без пересоздания. Из кэша он уходил только при ошибке создания, не при ошибке перевода. Одна случайная осечка превращалась в «слова не переводятся, пока не обновишь страницу».
Третья. Общий кулдаун. После отказа включалась пауза на минуту, одна на все языковые пары и все пути. Сбой на паре en-ru глушил и немецкий, и всё остальное. Ещё флаг 'unavailable' выключал подписи у уже сохранённых слов.
Порядок теперь такой. Сначала путь фрейма. Если translate() отказал, экземпляр выбрасывается и один раз повторяется на свежем. Не помогло - слово уходит тем же путём, что и реплика, через service worker, тоже с одним повтором. Только после этого человек видит ошибку. Сработавший запасной путь запоминается для пары, чтобы следующие слова не ждали зря. Зависший экземпляр второй раз не ждём.
Текст ошибки стал честнее. «Языковая пара не поддерживается» показывается, только если так ответили все пути и пара ни разу не переводилась на этой странице. Любой успешный перевод слова или реплики помечает пару рабочей. Остальные исключения превращаются в «Перевод не удался: <текст>», и это тоже повод идти запасным путём. Кулдаун теперь привязан к пути и паре, а не общий.
В тестовом Chromium 141, на котором идут тесты, объекта Translator просто нет. Поэтому проверки идут на замоканном переводчике: 12 сценариев на адаптере, вырезанном из настоящего content.js. Среди них iframe с 'unavailable', временный сбой availability(), мёртвый экземпляр один раз и навсегда, отказ обоих путей, зависший translate(). На старом коде 1.13 шесть сценариев падают по существу и выдают дословно те же две строки, что были на экране. Остальные проходят и на старом коде: они стерегут, чтобы исправление не сломало то, что работало, например честный отказ пары. Путь через service worker проверен и в живом Chrome 155: Translator там доступен и переводит.
Из отзывов: у тестера в Brave слова не переводились совсем, а в Chromium всё работало. В трекере Brave есть запрос отключать встроенные AI-API по умолчанию (brave-browser#49502). Как это устроено в его версии, я не проверял. Но вывод такой: проверка 'Translator' in self ничего не гарантирует. Браузер может объявить интерфейс и не дать им пользоваться, и расширение должно показать человеку понятную причину, а не молчать.
Ошибки встроенных API надо различать по классам: нет модели, нет прав, внутренний сбой, не скачан языковой пакет. Если свалить всё в одну фразу, человек видит ложный диагноз и ничего не может сделать. Экземпляр в кэше должен уметь умереть и пересоздаться, за ним отдельный сервис моделей со своей жизнью. И у каждого пути перевода должен быть запасной, чтобы отказ одного не убивал функцию целиком.
Расширение бесплатное, лежит в Chrome Web Store: https://chromewebstore.google.com/detail/subtitle-translator-learn/floomboamhlmkalghefpabefhciknllf. Если у вас где-то слова не переводятся, а реплика переводится, или расширение не видит субтитры на каком-то сайте, напишите в комментариях сайт и браузер.