Для просмотра видеоинструкции нажмите здесь

Если вы инди-разработчик или небольшая студия, локализация - это то, что вы, скорее всего, не собираетесь реализовывать в первоначальном выпуске своей игры. Тем не менее, для увеличения вашей аудитории и привлечения новых игроков наличие многоязычной поддержки является важным элементом, который вы рассмотрите в следующих версиях. В этом уроке я покажу очень простой способ выполнить локализацию вашей игры без каких-либо сторонних ресурсов.

Предварительные требования

Я собираюсь использовать Unity 2019.3, но подойдут и более ранние версии. Я использовал эту механику в одной из своих игр - Zen Jigsaw - вы можете увидеть ее там в действии.

Как всегда, исходный код доступен на GitHub, пожалуйста, найдите ссылку в конце этой статьи.

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

В этом руководстве будут использоваться два языка - английский и русский, но этот подход легко расширяется на все другие поддерживаемые языки.

До Unity у меня был опыт работы с другим популярным фреймворком для разработки игр под названием Libgdx. Если вы когда-либо работали с ним, вы можете найти определенные сходства в описанной здесь технике. Локализация в Libgdx поддерживается "из коробки" и реализуется чрезвычайно просто. Я был немного удивлен, что в Unity до сих пор нет этой обязательной функции. Да, доступен пакет 0.4, но он все еще находится на ранней стадии разработки.

Часть 1. Создание файлов свойств

Чтобы получить доступ к файлам свойств из кода, мы сохраним их в папке «Ресурсы».

Я называю эти файлы «свойствами» из-за структуры, например, файл English.txt имеет следующее содержимое:

welcome=Welcome!
long_text=This is a\nvery long\ntext

Каждый файл свойств будет соответствовать условиям контракта:

  • Отдельная строка состоит из двух частей - клавиши слева от разделителя и значения справа.
  • Строки разделяются новой строкой
  • Имя файла соответствует Unity SystemLanguageenum
  • Все файлы свойств имеют одинаковые ключи, но разные локализованные значения.
  • Разделитель между ключом и значением - «=», вы также можете использовать любой другой символ.

Часть 2. Чтение файлов свойств

Чтобы использовать подготовленные ранее файлы, давайте создадим скрипт, который будет отвечать за чтение файлов построчно, собирая все пары ключ-значение и заполняя их в текстовые компоненты:

public class LangResolver : MonoBehaviour
{
    private const char Separator = '=';
    private readonly Dictionary<string, string> _lang = new Dictionary<string, string>();
    private SystemLanguage _language;
    private void Awake()
    {
        DontDestroyOnLoad(gameObject);
        ReadProperties();
        Debug.Log(_lang.Count);
        Debug.Log(_lang.Keys.First());
        Debug.Log(_lang.Values.First());
    }
    private void ReadProperties()
    {
        _language = Application.systemLanguage;
        var file = Resources.Load<TextAsset>(_language.ToString());
        if (file == null)
        {
            file = Resources.Load<TextAsset>(SystemLanguage.English.ToString());
            _language = SystemLanguage.English;
        }
        foreach (var line in file.text.Split('\n'))
        {
            var prop = line.Split(Separator);
            _lang[prop[0]] = prop[1];
        }
    }
}

Давайте создадим новый пустой игровой объект и присоединим к нему LangResolver.

Если вы сейчас нажмете кнопку Play, вы увидите следующий результат:

Это размер _lang dictionary, а также первый ключ и значение из файла свойств на английском языке!

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

Примечание! Этот сценарий предназначен для инициализации только один раз, в идеале в самой первой сцене (обычно в сцене Splash). Если у вас нет такой сцены, следует добавить проверку на наличие дубликатов.

На этом этапе вам может быть интересно: где на сцене находится селектор языка? Я намеренно исключил эту часть UX за рамки учебника. В современных играх возможность смены языка больше не является необходимостью, полагаться на системный язык совершенно нормально. Но у меня это есть в Расширенном руководстве по локализации, обязательно ознакомьтесь с ним!

Часть 3. Локализация

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

public class LangText : MonoBehaviour
{
    public string Identifier;
}

Этот скрипт будет прикреплен к каждому текстовому компоненту, который мы хотим заполнить текстом из файлов. Поле Идентификатор - это ключ от свойств.

Последний шаг - заполнить все тексты. Давайте добавим в LangResolver следующий общедоступный метод:

public void ResolveTexts()
{
    var allTexts = Resources.FindObjectsOfTypeAll<LangText>();
    foreach (var langText in allTexts)
    {
        var text = langText.GetComponent<Text>();
        text.text = Regex.Unescape(_lang[langText.Identifier]);
    }
}

Примечание! Regex.Unescape метод используется для правильного анализа новых строк и других возможных символов форматирования из файлов свойств.

Хотя вы можете вызвать этот метод из самого скрипта (например, из Awake), я предпочитаю иметь специальный UIResolver class:

public class UIResolver : MonoBehaviour
{
    private void Start()
    {
        FindObjectOfType<LangResolver>().ResolveTexts();
    }
}

Часть 4. Тестирование

Теперь сложнее всего протестировать его на разных языках, так как вам нужно будет настроить язык вашей ОС. Я считаю, что вы не хотите этого делать, поэтому давайте просто «смоделируем» это поведение, изменив первую строку в ReadProperties method на _language = SystemLanguage.Russian

Если вы нажмете кнопку Play, вы увидите:

Не волнуйтесь, если вы не умеете читать кириллицу, текст - это прямой перевод.

С английским языком по умолчанию результат будет следующим:

В общем, вам не нужно хранить какой-либо текст в самом текстовом компоненте, поскольку его текст в любом случае будет заменен. Однако может быть полезно понять, как текст будет выглядеть с точки зрения UX, поэтому я бы предложил сохранить некоторый текст в редакторе Unity, используя файлы свойств как окончательный источник истины.

Потом

Молодцы, закончили урок! Если у вас есть какие-либо вопросы, оставьте их в разделе комментариев ниже.

Исходные файлы проекта можно найти в этом репозитории GitHub.

Игру, о которой я говорил, можно скачать здесь Zen Jigsaw.