ESPNNHL season preview: Rankings from 1-32 and what to know about every teamRTP DesportoPortugal-Noruega. Elogio de Jesus e Ronaldo titular na DinamarcaThe Jerusalem PostTwo states is not enough: Palestinians need sovereignty, Israelis need security - opinionDaily MaverickMADLANGA COMMISSION: Feroz Khan facing criminal charges for defying subpoena to appear at inquiryPunchMeet Danielle Adewusi, the doctor crowned Miss Universe Nigeria 2026Bollywood HungamaAnkur Rathee says Best Of The Best took him back to his college dancer days: “A version of myself I thought I had left behind”Sky TG24I titoli di Sky TG24 del 28 settembre, edizione delle 13SoompiSM Entertainment Announces Strict Legal Action Against Malicious Posts And Illegal AI-Generated Images Targeting Lim Yoona And aespa’s WinterIl Fatto QuotidianoRanucci-Rai, la destra contro la giudice: “È la stessa del caso Almasri”. I togati del Csm: “Tentativo di condizionamento”Egypt IndependentSisi moves to delegate certain Presidential powers to Defense Minister – here’s what that meansVilaWebEls treballadors de biblioteques de Barcelona critiquen la “tàctica de desgast” de l’Ajuntament després que anul·li la mesa de negociacióThe IndependentFive men arrested over RAF Fairford ‘bomb plot’ are British nationals, police say
The Daily Newsstand · Free, Always
Monday, September 28, 2026

Siri с Apple Intelligence на Mac с русским языком: как система принимает это решение и как её переубедить

Translate

Новая Siri с Apple Intelligence не работает на системе с русским языком. Не «работает хуже» — её просто нет в настройках. То же самое у поляков, чехов, греков, венгров, израильтян и ещё полутора десятков языков, которые Apple пока не поддерживает.

Я полез разбираться, где именно проходит эта граница, и выяснилось кое-что интересное: macOS проверяет язык системы ровно один раз — при загрузке. Дальше ей всё равно.

Загрузился с английским — Siri AI включилась. Вернул свой язык — она продолжает работать до следующей перезагрузки. Никаких патчей, никаких системных файлов: одна загрузка на английском, а дальше живи как жил.

Ниже — как я это проверял, где система прячет своё решение, и во что упирается попытка автоматизировать такой круг.

Где лежит решение

Первый вопрос: как вообще узнать, поднялась Siri AI или нет. Читаемого публичного признака нет — ни ключа в настройках, ни API. Пришлось идти в системный журнал.

Нужные события пишут три процесса: Siri, Siri AI и generativeexperiencesd. Предикат получился такой:

(process == "Siri" OR process == "Siri AI" OR process == "generativeexperiencesd")
AND (eventMessage CONTAINS[c] "Enhanced Siri"
  OR eventMessage CONTAINS[c] "LanguageIsSupported"
  OR eventMessage CONTAINS[c] "selectedSiriLanguageIneligible")

Сильные признаки выглядят так:

Enhanced Siri computed state: available=true
Enhanced Siri returning: unavailable
Missing Linwood desired capabilities: LanguageIsSupported

Linwood — внутреннее кодовое имя возможности. Когда язык системы не английский, в журнале ровно эта строка: не хватает LanguageIsSupported. После загрузки с английским — available=true.

И главное наблюдение: если после этого поменять язык обратно, available=true никуда не девается. Решение принято, и до следующей загрузки система к нему не возвращается.

Есть ещё слабый признак — ChatInputViewModel.isServiceAvailable changed to true. Он приходит от интерфейса и говорит только о том, что окно готово рисоваться. Я его учитываю, но как подтверждение, а не как доказательство: полагаться на него нельзя.

Как дождаться готовности

Знать предикат мало — надо понять, когда можно возвращать язык. Слишком рано вернёшь, и есть риск, что система не успела зафиксировать решение; слишком поздно — человек сидит и смотрит на английский интерфейс.

Машина состояний получилась такая:

  • журнал читается потоком, параллельно каждые 15 секунд дочитывается с начала загрузки — на случай, если событие пришло раньше, чем мы подписались;

  • события фильтруются по bootUUID: чужая загрузка нам не интересна;

  • состояние ready должно продержаться 3 секунды — система успевает передумать, и я это видел;

  • общий таймаут — 90 секунд, после чего один повтор с «подъёмом» Siri, чтобы она записала своё состояние;

  • если не вышло — язык возвращается всё равно.

Последний пункт важнее, чем кажется. В первой версии при неудачной проверке бросалось исключение, и система оставалась английской до следующей перезагрузки — то есть за неудачу платил человек. Сейчас язык возвращается в любом случае, транзакция закрывается фазой completed-without-siri, а уведомление честно говорит, что в этой загрузке не получилось.

На практике всё занимает 20–60 секунд.

Очередь: признака «встал в очередь» нет

Отдельная история для тех, у кого Apple Intelligence не включалась никогда. Apple ставит в очередь только на английской системе, и решается это тоже при загрузке. То есть первый шаг человек обязан сделать руками, а программа может только провести по шагам.

Я искал способ определить, встал человек в очередь или нет. В eligibilityd лежат внутренние коды доменов — числа без имён, завязываться на них нельзя: поменяются в следующем обновлении, и приложение начнёт врать.

Читаемое нашлось в кэшах подписочных функций:

  • com.apple.CloudSubscriptionFeatures.cache — запись ai.enhanced-siri с canUse = true;

  • com.apple.CloudSubscriptionFeatures.waitlist — waitlistResults, JSON со статусом active.

Оба — кэши, то есть могут быть пустыми в момент проверки. Поэтому первый успех запоминается своей меткой: иначе пункт «Первая настройка» в меню мигал бы туда-сюда.

Ещё одно, проверенное отдельно: очередь и скачивание моделей смену языка переживают. Сидеть в английской системе сутки не нужно — достаточно встать в очередь и вернуть свой язык.

ChatGPT, который «недоступен»

Побочный сюжет, на котором я потерял вечер. ChatGPT в Siri подключается только на английской системе: на других языках macOS показывает его недоступным и кнопку входа не даёт.

Ладно, значит подключаем во время английского окна. Но дальше начинается интересное: после возврата языка настройки снова показывают «Войти…» и «ChatGPT недоступен» — хотя он уже подключён и работает. Врёт именно интерфейс настроек.

Это пришлось выносить в документацию отдельным предупреждением, потому что первый порыв — нажать «Войти…» ещё раз, и вот тогда действительно можно сломать себе подключение.

В приложении для этого сделан отдельный режим: перезагрузка останавливается на английском, открывает настройки Siri и ждёт, пока человек войдёт и нажмёт «Готово».

Что должно произойти за одну перезагрузку

Сама последовательность:

  1. Запомнить текущий язык и список открытых программ.

  2. Мягко закрыть программы — не убить, а дать сохраниться.

  3. Поставить английский.

  4. Перезагрузить Mac.

  5. После входа дождаться готовности Siri AI.

  6. Вернуть язык.

  7. Открыть программы обратно.

Между пунктами 4 и 5 процесс умирает вместе с системой. Поэтому всё состояние пишется на диск: транзакция с фазами, метка ожидания, идентификатор загрузки. После входа стартует агент, видит метку, сверяет bootUUID — и продолжает с того места, где остановились. Метка от чужой загрузки игнорируется: это защита от ситуации «перезагрузился кнопкой на корпусе посреди процесса».

Какие программы закрывать и как их вернуть

Казалось бы, мелочь. Оказалось — самая грязная часть.

Как понять, что программа «пользовательская». Через NSWorkspace.runningApplications и activationPolicy: 0 — обычная программа с окнами, 1 — фоновый агент в строке меню. Тут ждали грабли JXA, о них ниже.

Как вернуть порядок окон. Через CGWindowListCopyWindowInfo. И вот ловушка: флаг kCGWindowListOptionOnScreenOnly показывает окна только текущего рабочего стола. У меня программы с других столов считались безоконными и открывались скрытыми. Пришлось разделить: порядок окон беру с текущего стола, а сам факт наличия окон — по полному списку.

Как назвать себя в «Элементах входа». macOS подписывает фоновый элемент именем исполняемого файла. Пока агент указывал на /usr/bin/osascript, в настройках у человека висело «osascript, неизвестный разработчик» — выглядит ровно так, как выглядит зловред. Лечится тем, что агент запускает исполняемый файл внутри бандла.

Побочный эффект: агент идёт мимо LaunchServices, поэтому штатная защита от второго экземпляра не срабатывает, и в строке меню появлялось два значка. Пришлось писать свою.

Мелочь, которая съела вечер: Safari без вкладок

После перезагрузки через приложение Safari открывался пустым. При этом в меню «История» пункт «Открыть снова все окна из последнего сеанса» был живой — то есть сеанс никуда не делся, Safari просто его не поднимал.

Причина оказалась в моей же логике. Перед перезагрузкой приложение выставляло TALLogoutSavesState = false — системный флаг «не сохранять состояние при выходе». Логика была разумная: программы я открываю сам, уже после возврата языка, и не хочу, чтобы система открыла их вторым комплектом.

Но Safari восстанавливает свою сессию после перезагрузки только если система вообще собиралась что-то восстанавливать. Собственной настройки «Все окна из последнего сеанса» ему мало — она работает, когда Safari перезапускают внутри одного сеанса. Поэтому в тестах без перезагрузки вкладки возвращались, а после настоящей — нет.

Лечится одной строкой: флаг ставится по галочке пользователя, а не принудительно в false. Двойного запуска не будет — к моменту выхода все программы уже закрыты, и восстанавливать системе нечего.

Почему JXA и чем за это платишь

Всё написано на JXA — JavaScript for Automation, штатном языке автоматизации macOS с доступом к Cocoa через мост. Плюс очевиден: внутри .app лежит обычный читаемый исходник, без Xcode и без зависимостей. Для программы, которая просит доверия и не подписана, это аргумент.

Минусы я собрал все.

Числа из моста приходят строками. Это выражение всегда ложно:

alert.suppressionButton.state === 1

Слева строка. Везде нужен Number(...).

Константы-перечисления через мост не проходят вообще:

$.NSApplicationActivationPolicyRegular  // undefined

Сравнивать надо с литералом 0. Именно из-за этого у меня молча пустовал список программ: условие сравнивалось с undefined и не выполнялось никогда.

Выходные параметры требуют null, обычные объектные — $(). Перепутаешь в одну сторону — JavaScriptCore падает с сегфолтом, в другую — «unrecognized selector», потому что null превращается в NSNull.

ObjC.unwrap для массива строк отдаёт объекты ObjC, нужен ObjC.deepUnwrap.

Скомпилированный аплет не рисует image у пункта меню. Совсем. Выяснил бисекцией: тот же код в обычном скрипте рисует, в собранном .app — нет. Пришлось делать свой NSView с NSImageView и NSTextField и подсовывать его в item.view.

И самое обидное. Функция IOPSGetPercentRemaining в моём собранном приложении возвращает 0xE00002C1 — kIOReturnNotPrivileged. Отказ в правах на чтение уровня заряда. В обычном скрипте и даже в отдельном тестовом аплете с теми же модулями она работает; разницы в подписи, правах и бандле я не нашёл.

Неприятен даже не сам отказ, а то, что он был молчаливым: код честно откатывался на pmset раз в двадцать секунд, и «исправление отставания процента», которое я неделей раньше с гордостью закоммитил, не работало ни дня. Обнаружилось только когда значок пропал целиком. Сейчас заряд читается цепочкой IOPSCopyPowerSourcesInfo → IOPSCopyPowerSourcesList → IOPSGetPowerSourceDescription — в приложении это работает.

Фон: с трёх секунд до двух сотых

Пока я занимался Siri, в строке меню жил индикатор заряда, написанный как пишут все: таймер раз в двадцать секунд, а в нём pmset -g batt, pmset -g, ps и top -l 2.

Замер за две минуты:

  • сам процесс — 0.38 с процессора, около 1.1 пробуждения в секунду;

  • дочерние процессы — около 3 с, из них top -l 2 даёт 0.48 с за каждый запуск.

То есть программа, которая рисует три цифры, тратила в восемь раз больше не на себя, а на то, что запускала.

Переделал на события:

Заряд и источник питания. IOKit объявляет их через notify: com.apple.system.powersources.percent и .source. Функция notify_register_file_descriptor пишет уведомления в файловый дескриптор, а его слушает NSFileHandle.waitForDataInBackgroundAndNotify. Между событиями программа не просыпается вообще.

Энергосбережение. NSProcessInfo.isLowPowerModeEnabled вместо pmset, а смена — через NSProcessInfoPowerStateDidChangeNotification. Тут грабли: это уведомление приходит из фонового потока, а JXA из фонового потока трогать нельзя. Подписка только блоком в NSOperationQueue.mainQueue.

top и ps запускаются только когда человек открыл меню. Замер идёт полторы секунды, поэтому раздел «больше всего тратят заряд» дописывается в уже открытое меню таймером в kCFRunLoopCommonModes — обычные таймеры, пока меню открыто, стоят.

Итог за те же две минуты: 0.02 секунды процессора, ноль пробуждений, ноль дочерних процессов.

Заодно: где система прячет настоящую батарейку

Раз уж индикатор всё равно рисовался, я захотел вернуть процент заряда наружу — в macOS 27 система рисует его внутри значка, и мне это не нравилось.

Штатно вынести его нельзя: в Пункте управления остался один ключ BatteryShowPercentage, который только включает число, а рисует его система сама и только внутри. Значит, рисовать самому.

Тут выяснилось, что системная батарейка — не символ из SF Symbols. Я взял battery.0percent и получил заметно более узкий и низкий значок. Настоящая фигура лежит в ресурсах Пункта управления и называется там BatteryFillShape.

В Assets.car внутри ControlCenter.app нашлось: battery-cap 2 × 12 — тот самый пупырышек справа, и две молнии — battery-bolt 11 × 14 и battery-bolt-large 13 × 16.

Две молнии — не случайность: система выбирает их по ширине корпуса. Маленькая идёт к узкому корпусу 19 × 10, большая — к обычному 23 × 12. Я сначала взял маленькую, и молния оказалась на пятую часть мельче системной; на глаз это видно, если положить значки рядом.

Ресурсы там чёрные, поэтому для цветного значка их приходится перекрашивать: нарисовать и залить цветом с NSCompositingOperationSourceAtop. А молния при зарядке выбивается из заливки через NSCompositingOperationDestinationOut.

Что получилось

Приложение называется AI Restart. Один пункт в строке меню: закрывает программы, ставит английский, перезагружает, после входа возвращает язык и открывает всё обратно. Секунд тридцать английского — и дальше всё как обычно, только Siri AI работает.

Бесплатно, открытый код под MIT, в сеть не ходит, прав администратора не просит, в фоне не работает.

Оговорки, без которых было бы нечестно:

  • Приложение не подписано — сертификат разработчика стоит 99 $ в год, а программа бесплатная. macOS скажет, что не может проверить разработчика. Исходник внутри бандла читаемый, сборка — одна команда без Xcode и зависимостей, так что проверить можно самому.

  • Проверял я только на macOS 27 на Apple Silicon. Про другие версии ничего не обещаю.

  • Apple может закрыть этот способ любым обновлением. Тогда Siri AI просто перестанет включаться, ничего не сломается.

Код и сборка: https://github.com/markkkkkkkkkkkkkkkkkkkkkkkkk/ai-restart

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.