Localización de cualquier texto.

La mayoría de los juegos usan localización basada en teclas, es decir, se necesita una clave para describir un texto específico, pero ofrezco una mejor opción, aunque esta opción no es adecuada para aquellos que tienen voz actuando en los juegos, es más fácil a través de una clave.

¿Qué es una clave y por qué es necesaria?


La clave, o más bien la palabra clave, es la palabra por la cual se determinará qué texto se necesita, y luego la búsqueda del idioma seleccionado ya está en marcha. Un ejemplo de una palabra clave: scene_Escape_from_jail_Ethan_dialog_with_Mary_3 , sí, la clave se verá así si su juego tiene muchas escenas, una gran trama. Sugiero escribir inmediatamente de inmediato una frase en uno de los idiomas, con mayor frecuencia en inglés o uno que el programador tenga fluidez. Por cierto, dado que todas las frases del idioma actual y el idioma principal se encuentran en la RAM, será más productivo que obtenerlo del archivo cada vez, para juegos grandes, puede modificar ligeramente el archivo para cada escena.

Cómo se arreglará todo


En las acciones que se describen a continuación, se utilizará la clase estática Lang, en la que se realizarán todas las búsquedas de palabras / frases. Declaremos una clase conectando las bibliotecas que necesitamos:

using UnityEngine; using System.Collections.Generic; // list and dictionary #if UNITY_EDITOR using UnityEditor; using System.IO; // created file in editor #endif public class Lang { private const string Path = "/Resources/"; // path to resources folder private const string FileName = "Language"; // file name with phrases } 

Por lo tanto, no podemos prescindir de una biblioteca estándar, ya que obtendremos el archivo de los recursos, podemos descargarlo de cualquier manera conveniente, pero es más conveniente y práctico. Se necesitan bibliotecas del sistema para conectar la lista, el diccionario y trabajar con archivos del editor. La biblioteca UnityEditor solo es necesaria para actualizar el archivo durante el primer registro de la frase, porque Después de un reinicio rápido, no siempre se cargarán todas las frases, pero con la ayuda de esta biblioteca podemos resolver este problema. La clase almacena dos campos estáticos, que sean los nombres del archivo, en nuestro caso la ruta es una carpeta con recursos, y puede haber cualquier nombre del archivo.

Ahora necesita agregar listas para almacenar todo lo utilizado por el idioma y el diccionario.

 private static int LangIndex; // variable to store the index of the current language private static List<SystemLanguage> languages = new List<SystemLanguage>(); // having languages in game private static Dictionary<string, string> Phrases = new Dictionary<string, string>(); // keys and values 

El campo LangIndex contendrá el índice del idioma actual relativo al registro en el archivo. En la lista de idiomas: se grabarán todos los idiomas utilizados en el archivo. El diccionario almacenará todas las frases en el idioma principal y en el idioma actual.

Debe agregar la inicialización de los campos de clase descritos anteriormente.

Código
 public static bool isStarting // bool for check starting { get; private set; } public static SystemLanguage language // return current language { get; private set; } #if UNITY_EDITOR public static void Starting(SystemLanguage _language, SystemLanguage default_language = SystemLanguage.English, params SystemLanguage[] _languages) // write languages without main language, it self added #else public static void Starting(SystemLanguage _language = SystemLanguage.English) // main language - only for compilation #endif { #if UNITY_EDITOR if (!File.Exists(Application.dataPath + Path + FileName + ".csv")) // if file wasn't created { File.Create(Application.dataPath + "/Resources/" + FileName + ".csv").Dispose(); // create and lose link File.WriteAllText(Application.dataPath + "/Resources/" + FileName + ".csv", SetLanguage(default_language, _languages)); // write default text with index } #endif string[] PhrasesArr = Resources.Load<TextAsset>(FileName).text.Split('\n'); // temp var for write in dicrionary string[] string_languages = PhrasesArr[0].Split(';'); // string with using languages int _length = string_languages.Length - 1; for (int i = 0; i < _length; i++) { languages.Add(SystemLanguageParse(string_languages[i])); // string language to SystemLanguage } LangIndex = FindIndexLanguage(_language); // index with current language for (int i = 0; i < PhrasesArr.Length; i++) // add keys and value { string[] temp_string = PhrasesArr[i].Split(';'); if (temp_string.Length > LangIndex) Phrases.Add(temp_string[0], temp_string[LangIndex]); else Phrases.Add(temp_string[0], temp_string[0]); } isStarting = true; } 

Inmediatamente usará la directiva incorporada para que no realice acciones innecesarias después de compilar la aplicación. La llamada a Lang.Starting (...) debería ser algo como esto:

 #if !UNITY_EDITOR Lang.Starting(LANGUAGE); #else Lang.Starting(LANGUAGE, SystemLanguage.English, SystemLanguage.Russian, SystemLanguage.Ukrainian); #endif private static int FindIndexLanguage(SystemLanguage _language) // finding index or current language { int _index = languages.IndexOf(_language); if (_index == -1) // if language not found return 0; // return main language return _index; } #if UNITY_EDITOR private static void Add(string AddString) // add phrases only form editor { File.AppendAllText(Application.dataPath + "/Resources/" + FileName +".csv", AddString + "\n"); // rewrite text to file Phrases.Add(AddString, AddString); // add phrase to dicrionary AssetDatabase.Refresh(); // refresh file } #endif #if UNITY_EDITOR private static string SetLanguage(SystemLanguage default_language, params SystemLanguage[] _languages) // set first string to file { string ret_string = ""; ret_string += default_language + ";"; foreach (SystemLanguage _language in _languages) { ret_string += _language + ";"; } return ret_string + "!@#$%\n"; // for last index } #endif 


La llamada cuando se reproduce en el editor debe contener dos parámetros principales, esto es qué frases de idioma se deben traducir ahora y qué idioma se usará como idioma principal, todos los demás parámetros son los parámetros de idioma que deben estar contenidos en el archivo, estos parámetros son necesarios solo durante el primer lanzamiento, cuando el archivo aún no se ha creado (y no es necesario eliminarlo más tarde), de lo contrario, si necesita agregar algún idioma, debe copiar todo del archivo, eliminar el archivo y volver a ejecutar el código en el editor o agregarlo al archivo usted mismo.

El código anterior utiliza el método SystemLanguageParse (...) , que simplemente traduce los nombres de idioma del tipo de cadena a SystemLanguage (este método será más bajo).

Detengámonos en el método add:

 #if UNITY_EDITOR private static void Add(string AddString) // add phrases only form editor { File.AppendAllText(Application.dataPath + "/Resources/" + FileName +".csv", AddString + "\n"); // rewrite text to file Phrases.Add(AddString, AddString); // add phrase to dicrionary AssetDatabase.Refresh(); // refresh file } #endif 

Dado que este método se usará solo cuando se inicie desde el editor, podemos usar de forma segura la utilidad del sistema para sobrescribir el archivo, así como actualizar los archivos modificados al editor usando el método Refresh (). Entre estas acciones, simplemente se agrega una frase al diccionario, para evitar que se vuelva a grabar en la misma sesión.

Por cierto, olvidé decir que las frases se almacenarán en un archivo .csv, lo que nos permitirá traducir cómodamente frases a Excele. Ahora necesitamos agregar un método que sea bueno para nosotros, que cambiará el idioma:

  public static void ChangeLanguage(SystemLanguage _language) // change language { string[] PhrasesArr = Resources.Load<TextAsset>(FileName).text.Split('\n'); // load all text from file LangIndex = FindIndexLanguage(_language); Phrases.Clear(); // clear dictionary with phrases for (int i = 1; i < PhrasesArr.Length; i++) { string[] temp_string = PhrasesArr[i].Split(';'); if (temp_string.Length > LangIndex) Phrases.Add(temp_string[0], temp_string[LangIndex]); else Phrases.Add(temp_string[0], temp_string[0]); } } 

Y así, llegamos al método más importante, que tomará la frase en el idioma principal y la emitirá al usuario correcto:

  public static string Phrase(string DefaultPhrase) // translate phrase, args use to formating string { #if UNITY_EDITOR if (!isStarting) // if not starting { throw new System.Exception("Forgot initialization.Use Lang.Starting(...)"); // throw exception } #endif string temp_EnglishPhrase = DefaultPhrase; // temp variable for try get value if (Phrases.TryGetValue(DefaultPhrase, out DefaultPhrase)) // if value has been found { return temp_EnglishPhrase; } #if UNITY_EDITOR Add(temp_EnglishPhrase); // add phrase if value hasn't been found #endif return temp_EnglishPhrase; } 

Este método simplemente toma una frase en el idioma principal, luego itera sobre todo lo que está en el diccionario, encontrando dicha frase por clave, nos da los valores de esta clave, que es una frase en el idioma que necesitamos. Puede usar este método con una línea de código simple:

 string str = Lang.Phrase("Hello world"); 

Ahora la frase en el idioma que necesitamos entrará en la cadena str, si de repente falta, entonces la frase especificada en los parámetros, es decir, Hola mundo, caerá.

Este método se puede mejorar un poco, para que pueda tomar argumentos para completar:

  public static string Phrase(string DefaultPhrase, params string[] args) // translate phrase, args use to formating string { #if UNITY_EDITOR if (!isStarting) // if not starting { throw new System.Exception("Forgot initialization.Use Lang.Starting(...)"); // throw exception } #endif string temp_EnglishPhrase = DefaultPhrase; // temp variable for try get value if (Phrases.TryGetValue(DefaultPhrase, out DefaultPhrase)) // if value has been found { if (args.Length == 0) return DefaultPhrase; return string.Format(DefaultPhrase, args); } #if UNITY_EDITOR Add(temp_EnglishPhrase); // add phrase if value hasn't been found #endif if (args.Length == 0) return temp_EnglishPhrase; return string.Format(temp_EnglishPhrase, args); } 

Ahora este método se puede llamar como antes:

 string str = Lang.Phrase("Hello world"); 

Pero ahora nuestro método tiene salida formateada, que indica parámetros separados por comas:

 string str = Lang.Phrase("Hello {0} from {1}", "world", "habr"); 

Traducciones de frases


Como escribí anteriormente, el archivo usa extensiones .csv que le permitirán hacer todo en Excel, pero no es tan simple, el problema de C-sharpe y Excel es que entienden el alfabeto cirílico en diferentes codificaciones, Excel solo entiende la codificación UTF-8-BOM o el que nuestro YaP no entiende, deberíamos usar solo UTF-8, aunque el editor unitario entienda UTF-8-BOM, en el código hay dos palabras idénticas en diferentes codificaciones (UTF-8 y UTF-8- BOM) no será igual, lo que conducirá a la adición constante de palabras idénticas a nuestro archivo.

Podemos codificar archivos usando NotePad ++ gratis descargándolo desde fuera. sitio. Editar un archivo no le traerá ningún problema, para agregar una sola palabra, incluso puede usar un editor de texto, el mismo bloc de notas o incluso nuestro entorno de programación.

Código final
 using UnityEngine; using System.Collections.Generic; // list and dictionary #if UNITY_EDITOR using UnityEditor; using System.IO; // created file in editor #endif public class Lang { private const string Path = "/Resources/"; // path to resources folder private const string FileName = "Language"; // file name with phrases private static int NumberOfLanguage; // variable to store the index of the current language private static List<SystemLanguage> languages = new List<SystemLanguage>(); // having languages in game private static Dictionary<string, string> Phrases = new Dictionary<string, string>(); // keys and values private static SystemLanguage language; // current language #if UNITY_EDITOR public static void Starting(SystemLanguage _language, SystemLanguage default_language, params SystemLanguage[] _languages) // write languages without main language, it self added #else public static void Starting(SystemLanguage _language = SystemLanguage.English) // main language - only for compilation #endif { #if UNITY_EDITOR if (!File.Exists(Application.dataPath + Path + FileName + ".csv")) // if file wasn't created { File.Create(Application.dataPath + "/Resources/" + FileName + ".csv").Dispose(); // create and lose link File.WriteAllText(Application.dataPath + "/Resources/" + FileName + ".csv", SetLanguage(default_language, _languages)); // write default text with index } #endif string[] PhrasesArr = Resources.Load<TextAsset>(FileName).text.Split('\n'); // temp var for write in dicrionary string[] string_languages = PhrasesArr[0].Split(';'); // string with using languages int _length = string_languages.Length - 1; for (int i = 0; i < _length; i++) { languages.Add(SystemLanguageParse(string_languages[i])); // string language to SystemLanguage } NumberOfLanguage = FindIndexLanguage(_language); // index with current language for (int i = 0; i < PhrasesArr.Length; i++) // add keys and value { string[] temp_string = PhrasesArr[i].Split(';'); if (temp_string.Length > NumberOfLanguage) Phrases.Add(temp_string[0], temp_string[NumberOfLanguage]); else Phrases.Add(temp_string[0], temp_string[0]); } isStarting = true; } public static bool isStarting // bool for check starting { get; private set; } public static SystemLanguage Language // return current language { get { return language; } } public static string Phrase(string DefaultPhrase, params string[] args) // translate phrase, args use to formating string { #if UNITY_EDITOR if (!isStarting) // if not starting { throw new System.Exception("Forgot initialization.Use Lang.Starting(...)"); // throw exception } #endif string temp_EnglishPhrase = DefaultPhrase; // temp variable for try get value if (Phrases.TryGetValue(DefaultPhrase, out DefaultPhrase)) // if value has been found { if (args.Length == 0) return DefaultPhrase; return string.Format(DefaultPhrase, args); } #if UNITY_EDITOR Add(temp_EnglishPhrase); // add phrase if value hasn't been found #endif if (args.Length == 0) return temp_EnglishPhrase; return string.Format(temp_EnglishPhrase, args); } public static void ChangeLanguage(SystemLanguage _language) // change language { string[] PhrasesArr = Resources.Load<TextAsset>(FileName).text.Split('\n'); // load all text from file NumberOfLanguage = FindIndexLanguage(_language); Phrases.Clear(); // clear dictionary with phrases for (int i = 1; i < PhrasesArr.Length; i++) { string[] temp_string = PhrasesArr[i].Split(';'); if (temp_string.Length > NumberOfLanguage) Phrases.Add(temp_string[0], temp_string[NumberOfLanguage]); else Phrases.Add(temp_string[0], temp_string[0]); } } private static int FindIndexLanguage(SystemLanguage _language) // finding index or current language { int _index = languages.IndexOf(_language); if (_index == -1) // if language not found return 0; // return main language return _index; } #if UNITY_EDITOR private static void Add(string AddString) // add phrases only form editor { File.AppendAllText(Application.dataPath + "/Resources/" + FileName + ".csv", AddString + "\n"); // rewrite text to file Phrases.Add(AddString, AddString); // add phrase to dicrionary AssetDatabase.Refresh(); // refresh file } #endif #if UNITY_EDITOR private static string SetLanguage(SystemLanguage default_language, params SystemLanguage[] _languages) // set first string to file { string ret_string = ""; ret_string += default_language + ";"; foreach (SystemLanguage _language in _languages) { ret_string += _language + ";"; } return ret_string + "!@#$%\n"; // for last index } #endif private static SystemLanguage SystemLanguageParse(string _language) // just parse from string to SystemLanguage { switch (_language) { case "English": return SystemLanguage.English; case "Russian": return SystemLanguage.Russian; case "Ukrainian": return SystemLanguage.Ukrainian; case "Polish": return SystemLanguage.Polish; case "French": return SystemLanguage.French; case "Japanese": return SystemLanguage.Japanese; case "Chinese": return SystemLanguage.Chinese; case "Afrikaans": return SystemLanguage.Afrikaans; case "Arabic": return SystemLanguage.Arabic; case "Basque": return SystemLanguage.Basque; case "Belarusian": return SystemLanguage.Belarusian; case "Bulgarian": return SystemLanguage.Bulgarian; case "ChineseSimplified": return SystemLanguage.ChineseSimplified; case "ChineseTraditional": return SystemLanguage.ChineseTraditional; case "Czech": return SystemLanguage.Czech; case "Danish": return SystemLanguage.Danish; case "Dutch": return SystemLanguage.Dutch; case "Estonian": return SystemLanguage.Estonian; case "Faroese": return SystemLanguage.Faroese; case "Finnish": return SystemLanguage.Finnish; case "German": return SystemLanguage.German; case "Greek": return SystemLanguage.Greek; case "Hebrew": return SystemLanguage.Hebrew; case "Hungarian": return SystemLanguage.Hungarian; case "Icelandic": return SystemLanguage.Icelandic; case "Indonesian": return SystemLanguage.Indonesian; case "Italian": return SystemLanguage.Italian; case "Korean": return SystemLanguage.Korean; case "Latvian": return SystemLanguage.Latvian; case "Lithuanian": return SystemLanguage.Lithuanian; case "Norwegian": return SystemLanguage.Norwegian; case "Portuguese": return SystemLanguage.Portuguese; case "Romanian": return SystemLanguage.Romanian; case "SerboCroatian": return SystemLanguage.SerboCroatian; case "Slovak": return SystemLanguage.Slovak; case "Slovenian": return SystemLanguage.Slovenian; case "Spanish": return SystemLanguage.Spanish; case "Swedish": return SystemLanguage.Swedish; case "Thai": return SystemLanguage.Thai; case "Turkish": return SystemLanguage.Turkish; case "Vietnamese": return SystemLanguage.Vietnamese; } return SystemLanguage.Unknown; } } 

Lo más importante para recordar: UTF-8-BOM: para trabajar en Excel, UTF-8 para trabajar con código, no lo olvide.

Source: https://habr.com/ru/post/446844/


All Articles