Поддержка нескольких языков в игре

Есть различные способы решения данной проблемы, тем не менее, предлагаем свой вариант. Возможно кому-нибудь данный способ пригодиться. Ориентирован он, кстати говоря, для смены языка интерфейса игры. Работает скрипт только с объектами Unity UI. Суть заключается в следующем. Например, при создании меню каких-то настроек, для описания ее элементов мы используем UI Text, каждый из которых в свою очередь, помещаем в специальный массив. То есть, мы можем выбрать, где язык будет меняться. А сами языковые пакеты, находятся во внешней папке, в этом собственно фишка. Иначе говоря, локализацию можно сделать, когда игра уже готова и скомпилирована. После запуска, все имеющиеся языки, станут доступны в отдельном меню.


Итак, у нас есть готовое меню или какой-нибудь интерфейс. Теперь в игру нужно встроить список Dropdown UI, добавляем его в нужное место и настраиваем внешний вид по вкусу. Далее, создаем в проекте папку с именем, например, Localization. Эта папка должна находится рядом со стандартной папкой проекта Assets, а после компиляции игры, ее нужно создать рядом с исполняемым файлом.

Вешаем на сцену скрипт Localization:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.IO;
using System.Xml;
using System.Linq;

public class Localization : MonoBehaviour {
	
	public Text[] elements; // все текстовые элементы интерфейса, для которых предусмотрен перевод
	public string path = "Localization"; // путь где будут все локали
	public Dropdown dropdown; // меню языков, делаем из стандартного UI

	[HideInInspector] public int[] idList; // создается/обновляется вместе с шаблоном языка

	private string[] fileList;
	private string locale;

	void Start()
	{
		LoadLocale(); // создание списка всех доступных локалей
		DefaultLocale(-1); // установка локали по умолчанию, в данном случае, будет выбрана первая из списка
	}

	// создание массива id значений, относительно текстовых элементов
	// одинаковым текстовым элементам, будет присвоен одинаковый id
	void GetID()
	{
		int i = 1;
		idList = new int[elements.Length];
		for(int j = 0; j < elements.Length; j++)
		{
			if(idList[j] == 0)
			{
				string key = elements[j].text;
				idList[j] = i;
				for(int t = j + 1; t < elements.Length; t++)
				{
					if(elements[t].text.CompareTo(key) == 0)
					{
						idList[t] = i;
					}
				}
				i++;
			}
		}
	}

	public void DefaultLocale(int value)
	{
		dropdown.value = value;
	}

	void SetData(string value)
	{
		Dropdown.OptionData option = new Dropdown.OptionData();
		option.text = Path.GetFileNameWithoutExtension(value);
		dropdown.options.Add(option);
	}

	public void LoadLocale() 
	{
		dropdown.options = new System.Collections.Generic.List<Dropdown.OptionData>();

		if(!Directory.Exists(path))
		{
			SetData("none");
			return;
		}

		fileList = Directory.GetFiles(path, "*.xml");

		if(fileList.Length == 0)
		{
			SetData("none");
			return;
		}

		for(int i = 0; i < fileList.Length; i++)
		{
			SetData(fileList[i]);
		}
			
		dropdown.onValueChanged.AddListener(delegate{SetLocale();});
	}

	public void BuildDefaultLocale() // для редактора, создание/обновление локали по умолчанию
	{
		if(!Directory.Exists(path))
		{
			Debug.LogWarning(this + " Путь указан не верно!");
			return;
		}

		GetID();
		string file = path + "/Default.xml"; // имя стандартной локали

		string[] arr = new string[elements.Length];
		for(int i = 0; i < elements.Length; i++)
		{
			arr[i] = elements[i].text; // копируем все текстовые элементы
		}

		string[] res_txt = arr.Distinct().ToArray(); // убираем элементы, которые повторяются
		int[] res_id = idList.Distinct().ToArray();

		XmlElement elm;
		XmlDocument xmlDoc = new XmlDocument();
		XmlNode rootNode = xmlDoc.CreateElement("Locale");
		xmlDoc.AppendChild(rootNode);

		for(int i = 0; i < res_txt.Length; i++) // запись в файл, без повторяющихся элементов
		{
			elm = xmlDoc.CreateElement("text");
			elm.SetAttribute("id", res_id[i].ToString());
			rootNode.AppendChild(elm);
                        elm.SetAttribute("value", res_txt[i]);
                        rootNode.AppendChild(elm);
		}

		xmlDoc.Save(file);
		Debug.Log(this + " Создан фаил --> " + file);
	}
	
	int GetInt(string text)
	{
		int value;
		if(int.TryParse(text, out value)) return value;
		return 0;
	}

	void SetLocale() // чтение файла и замена текста
	{
		locale = fileList[dropdown.value];

		try
		{
			XmlTextReader reader = new XmlTextReader(locale);
			while(reader.Read())
			{
				if(reader.IsStartElement("text"))
				{
					ReplaceText(GetInt(reader.GetAttribute("id")), reader.GetAttribute("value"));
				}
			}
			reader.Close();
		}
		catch(System.Exception)
		{
			Debug.LogError(this + " Ошибка чтения файла! --> " + locale);
		}
	}

	void ReplaceText(int id, string text) // поиск и замена всех элементов, по ключу
	{
		for(int j = 0; j < idList.Length; j++)
		{
			if(idList[j] == id) elements[j].text = text;
		}
	}
}

Указываем все необходимые элементы.

Дополнительно, создаем вспомогательный скрипт LocalizationEditor:

#if UNITY_EDITOR
using UnityEngine;
using System.Collections;
using UnityEditor;

[CustomEditor(typeof(Localization))]

public class LocalizationEditor : Editor {

	public override void OnInspectorGUI()
	{
		DrawDefaultInspector();
		Localization lcz = (Localization)target;
		if(GUILayout.Button("Build Default Locale"))
		{
			lcz.BuildDefaultLocale();
		}
	}
}
#endif

Он нужен для генерации файла-шаблона.

Как это работает?

Весь текст интерфейса создаем на "языке по умолчанию", допустим, английский. Когда всё готово и в массив скрипта добавленный все необходимые элементы, в инспекторе жмем кнопочку Build Default Locale:

Поддержка нескольких языков в игре

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

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

<Locale>
  <text id="1" value="Game settings" />
  <text id="2" value="New game" />
  <text id="3" value="Option" />
</Locale>

Всё что нужно сделать это дублировать файл Default.xml переименовать допустим в Russian.xml и сделать перевод значений:

<Locale>
  <text id="1" value="Настройки игры" />
  <text id="2" value="Новая игра" />
  <text id="3" value="Опция" />
</Locale>

Сохраняем и готово! Теперь будут доступны два языка.

Скачать скрипты:

Внимание! Посетители, находящиеся в группе Гости, не могут скачивать файлы.

Комментариев 18

Офлайн
EagleOwle 3 июля 2016
Приветствую. По совершенно не понятной причине, у меня переводит только часть слов. Массив fileList и idList заполнены корректно. В файле тоже всё нормально, но текст не прописывается. При чем ни из Default.xml, ни из других переводов. Может быть есть мысли, почему так?
Офлайн
Light 4 июля 2016
EagleOwle, были изменения в скрипте? В любом случаи, планируется несколько иная версия данного проекта, возможно именно он подойдет лучше.
Офлайн
EagleOwle 4 июля 2016
Light,
Изменения в скрипте только касательно расположения папки Localization.
Вот кусок разработки, посмотрите, если не сложно... https://drive.google.com/file/d/0B0ZlcJhLrb1Fc1ZNZFJDSUZHa1E/view?usp=sharing
Офлайн
Light 4 июля 2016
EagleOwle, я немного изменил логику сохранения и загрузки, поэтому скачай оригинал и внеси изменения путей, затем надо заново сгенерировать файл XML и сделать перевод на другой язык.

Кроме того, я заметил что у тебя текст некоторых кнопок, назначаются из других классов, т.е. это фиксированный текст получается, без учета локали.
Офлайн
EagleOwle 5 июля 2016
Light,
Спасибо, всё работает. Осталось подумать, как реализовать динамический текст.
Офлайн
Light 6 июля 2016
EagleOwle, скоро обновление проекта, где эта проблема будет решена.
Офлайн
temich777e 31 мая 2017
Light,
у меня проблема. В настройках ставлю нужный язык и нажимаю на кнопку перехода на уровень, а потом, когда опять перехожу в меню язык сбрасывается и снова ставится дефолтный.
Что делать?
Внимание! У Вас нет прав для просмотра скрытого текста.
--------------------
Артем Ефимов
Офлайн
Light 1 июня 2017
temich777e, рекомендую использовать обновленную версию https://null-code.ru/project/138-lokalizaciya-igrovogo-menyu-hud-i-tp.html
Офлайн
Conagher 31 мая 2018
Есть такой же скрипт но на андроид?
Офлайн
Light 1 июня 2018
Conagher, привязки к конкретной платформе нет, должно всё работать.
Офлайн
Conagher 1 июня 2018
Light,
пробывал на андроид ничего не работает.Работало только в Unity
Офлайн
Light 1 июня 2018
Conagher, я делал сборку для андройда этой версии https://null-code.ru/project/138-lokalizaciya-igrovogo-menyu-hud-i-tp.html они почти одинаковые. Всё работает, язык переключается.
Офлайн
monstYT 28 ноября 2018
Можно ли этим способом локализировать несколько сцен, создавая разные файлы??? Например: (Rus-menu.xml и Rus-ui.xml)
Офлайн
Light 29 ноября 2018
monstYT, рекомендую нашу улучшенную версию, которую можно скачать на Патреоне https://www.patreon.com/posts/lokalizatsiia-22814615 создавать разные файлы не нужно, есть просто локали с разными переводами, так как каждая сцена всё-равно ссылается на один и тоже файл.
Офлайн
monstYT 29 ноября 2018
Странно, сделал всё как по инструкции, дублировал default, перевёл, назвал russian, пишет что ошибка
Офлайн
monstYT 29 ноября 2018
monstYT,
P.S. Мне именно этот способ нужен, не обновлённый
Офлайн
monstYT 29 ноября 2018
Хотя... Новый способ тоже ничего. Но про этот жду объяснения
Офлайн
Light 30 ноября 2018
monstYT, используй это https://null-code.ru/project/138-lokalizaciya-igrovogo-menyu-hud-i-tp.html там есть демо сцена рабочая.
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика