Table of Contents

Локализация

IcyUI показывает текст на языке игрока: разметка запрашивает строку по ключу через {Localize}, локализатор находит её на текущем языке, а смена языка сразу обновляет все локализованные тексты.

Настройка

Выберите, где хранятся переводы, и передайте конфигурации локализатор. С файлами gettext .po, по одному на язык, в папке Localization рядом с игрой:

Game.UseIcyUI(ui => ui
    .ConfigureAssets()
    .UsePoLocalization("Localization", CultureInfo.GetCultureInfo("en")));  // Localization/en.po, ru.po, ...
Локализатор Откуда строки Подходит для
PoLocalizer Файлы gettext .po, по одному на язык (en.po, ru.po, pt_BR.po). Большинства игр: переводчики работают в Poedit, Weblate или Crowdin, а игроки могут добавить язык без новой сборки.
ResourceManagerLocalizer Ресурсы .resx, скомпилированные в игру. Проектов, которые уже используют ресурсы .NET.
DictionaryLocalizer Строки, добавленные из кода. Прототипов, тестов, нескольких встроенных строк.
CompositeLocalizer Несколько локализаторов, опрашиваемых по порядку. Переводов модов поверх перевода игры или запасного варианта.

Подойдёт и любой другой источник: реализуйте ILocalizer — один метод, возвращающий строку для ключа и культуры или null.

Культура откатывается к родительской, а затем к языку по умолчанию: для ru-RU — ru-RU.po, затем ru.po, затем en.po. Ключ, не найденный нигде, показывается как есть, а IcyUI один раз пишет в журнал предупреждение (событие 4001). Файл .po с ошибкой пропускается, как будто его нет, с предупреждением (событие 4002): опечатка в переводе от игрока никогда не остановит игру.

В разметке

<!-- {Localize} shows the string for a key in the configuration's culture, and follows it when it changes. -->
<StackPanel Orientation="Vertical">
  <TextBlock Text="{Localize Menu.Title}" />
  <Button Content="{Localize Menu.Play}" />
</StackPanel>
  • {Localize Menu.Play} работает с любым свойством, принимающим текст. У Content строка становится TextBlock, как и обычный текст.
  • Context= различает два ключа с одинаковым текстом (msgctxt в gettext): {Localize Open, Context=Verb}.

Смена языка

Язык — это Culture конфигурации, та же настройка, в которой показываются числа:

configuration.Culture = CultureInfo.GetCultureInfo("ru");   // every {Localize} changes now

Каждый текст {Localize} меняется сразу. Привязка покажет числа в новом формате при следующем обновлении. По умолчанию Culture следует языку интерфейса системы; задайте её, если у игры своя настройка языка.

Интерфейс, построенный в коде

score.Localize(nameof(TextBlock.Text), "Hud.Score");

Такой текст меняется вместе с языком, как {Localize}, а элемент, которого ещё нет на Canvas, получает текст при добавлении. Configuration.Localize("Hud.Score") возвращает строку для кода, который сам форматирует текст.

Моды и слои

var mods = new PoLocalizer(configuration.Assets, "Mods/Localization");   // no default: only the player's language
var game = new PoLocalizer(configuration.Assets, "Localization", CultureInfo.GetCultureInfo("en"));
configuration.Assets.Localizer = new CompositeLocalizer(mods, game);

Язык по умолчанию задавайте только последнему локализатору. Слой мода с языком по умолчанию отвечал бы на любом языке строками этого языка, и перевод самой игры на язык игрока никогда бы не запрашивался.

Производительность

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

Типичные ошибки

  • Собственный текст навсегда заменяет локализованный. Если вы сами задаёте локализованное свойство (счёт, имя игрока), оно перестаёт меняться вместе с языком.
  • Стили не переключаются. {Localize} в установщике стиля читается один раз, при загрузке стиля. Ставьте его у элементов или в шаблоне.
  • Недостающие символы. Шрифт без кириллицы или CJK не покажет эти языки; выберите шрифт, который покрывает ваши языки.
  • Аргументы. "Score: {0}" пока не заполняется автоматически: форматируйте его в коде через Configuration.Localize.

См. также