Решили: язык первого запуска берётся у СИСТЕМЫ, а не из дефолта проекта, и тут же
записывается в prefs (ISettingsService.LanguageCode / SetLanguage). Применяет его новый
entry point LocaleStartup; LocalizationService остался тонким и про настройки игрока не знает.
Почему: до этой правки LocalizationSettings в рантайме не трогал НИКТО — игра всегда
стартовала в локали, стоящей в настройках проекта, то есть половина аудитории видела чужой язык,
пока не найдёт настройки (а переключателя языка там до сих пор нет). Развилок было три, и все
решены в пользу «пустота — это факт»:
- дефолт против пустоты. Поле
Languageвprefsне имеет дефолта: «поля нет» означает «игрок не выбирал». Подставь мы"en"— русский получил бы английский, а файл после первогоSaveзаявил бы, что он его сам выбрал. - запоминать подобранное или каждый раз спрашивать систему. Запоминаем: иначе смена языка ОС переключала бы игру под человеком, который уже привык к её языку.
- чем отвечать на язык, которого у нас нет. Английским, а не «ближайшим»: японцу русский не ближе английского.
Сказано: «Язык по системному» — выбор Макса из списка находок по старту игры (2026-07-31, сессия 4d35c7ed).
Грабли: порядок IStartable в VContainer — это порядок регистрации, поэтому применять локаль
только в Start() было бы миной: перестановка строк в скоупе, и LocaleStartup прочитал бы
настройки до их загрузки. Поэтому он ещё и слушает ISettingsService.Changed — заодно бесплатно
подхватит будущий переключатель языка. Второе: Start() сделан публичным, а не явной реализацией
IStartable, чтобы тест не тащил VContainer в тестовую сборку — правка references в asmdef всё
равно не видна compile-check.ps1, пока редактор не пересоберёт .rsp.
Владелец правды: тест LocaleStartupTests (пять инвариантов: первый запуск, приоритет выбора
игрока, фолбэк на английский, en-GB → en, применение без перезапуска),
Assets/_Project/Scripts/Game/Services/LocaleStartup.cs.