任何文本的本地化

大多数游戏都使用基于键的本地化,也就是说,需要一个键来描述特定的文本,但是我提供了一个更好的选项,尽管此选项不适合那些在游戏中有声音的人,但通过键更容易。

什么是钥匙,为什么需要它


关键字(或关键字)是一个单词,通过它可以确定需要哪些文本,然后已经在搜索所选语言。 关键字的示例: scene_Escape_from_jail_Ethan_dialog_with_Mary_3 ,是的,如果您的游戏中有很多场景,很大的情节,则键将看起来像这样。 我建议立即用一种语言(通常是英语或程序员能熟练使用的一种语言)写一个短语。 顺便说一下,由于当前语言和主要语言的所有短语都位于RAM中,因此与每次从文件中获取文件相比,它的工作效率更高。对于大型游戏,您可以针对每个场景略微修改文件。

一切将如何安排


在下面描述的动作中,将使用静态Lang类,其中所有单词/词组的搜索都将发生。 让我们通过连接所需的库来声明一个类:

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 } 

因此,我们离不开标准库,因为我们将从资源中获取文件,因此可以通过任何方便的方式下载该文件,但它更加方便实用。 需要系统库来连接列表,字典并使用编辑器中的文件。 仅在短语的第一条记录期间才需要UnityEditor库来更新文件,因为 快速重启后,并非总是会加载所有短语,但是借助此库,我们可以解决此问题。 该类存储两个静态字段,将其作为文件名,在我们的示例中,路径是带有资源的文件夹,并且可以有任何文件名。

现在,您需要添加列表以存储语言和字典使用的所有内容。

 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 

LangIndex字段将保存当前语言相对于文件中记录的索引。 在语言列表中-将记录文件中使用的所有语言。 词典将以主要语言和当前语言存储所有短语。

有必要添加上述类的字段的初始化。

代号
 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; } 

它将立即使用内置指令,以便在编译应用程序后不会执行不必​​要的操作。 对Lang.Starting(...)的调用应如下所示:

 #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 


在编辑器中播放时,调用应包含两个主要参数,这是现在应翻译的语言短语,以及将使用哪种语言作为主要语言,所有其他参数是文件中应包含的语言参数,这些参数仅在首次启动时才需要,当尚未创建文件时(并且以后不必删除它),否则,如果需要添加某种语言,则需要从文件中复制所有内容,删除文件,然后在编辑器中重新运行代码或自己将其添加到文件中。

上面的代码使用SystemLanguageParse(...)方法,该方法只是将语言名称从字符串类型转换为SystemLanguage(此方法将更低)。

让我们来谈谈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 

由于仅在从编辑器启动时才使用此方法,因此我们可以安全地使用系统实用程序覆盖文件,并使用Refresh()方法将更改后的文件更新到编辑器。 在这些操作之间,仅将短语添加到字典中,以保护自己免于在同一会话中重新录制。

顺便说一句,我忘了说短语将存储在.csv文件中,这将使我们能够轻松地将短语转换为Excele。 现在,我们需要添加一种对我们有益的方法,这将使我们能够更改语言:

  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]); } } 

因此,我们来到了最重要的方法,该方法将采用主要语言中的短语并将其发布给合适的用户:

  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; } 

该方法只使用主要语言中的短语,然后遍历字典中的所有内容,通过键找到这样的短语,它为我们提供了该键的值,这就是我们需要的语言中的短语。 您可以通过简单的代码行使用此方法:

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

现在,我们需要的语言中的短语将进入字符串str,如果突然丢失,则参数中指定的短语(即Hello world)将掉落。

此方法可以进行一些改进,以便您可以使用参数来填充:

  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); } 

现在可以像以前一样调用此方法:

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

但是现在我们的方法已格式化输出,指示参数之间用逗号分隔:

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

词组翻译


就像我在上面写的,该文件使用.csv扩展名,可以让您在Excel中完成所有操作,但并非如此简单,C-sharpe和Excel的问题在于它们能够理解不同编码的西里尔字母,而Excel仅能理解UTF-8-BOM编码或YaP无法理解的代码,即使统一编辑器可以理解UTF-8-BOM,我们也应仅在其中使用UTF-8,但在代码中,两个相同的单词使用不同的编码(UTF-8和UTF-8- BOM)将不相等,这将导致向我们的文件中不断添加相同的单词。

我们可以通过免费下载NotePad ++文件进行编码 。 网站。 编辑文件不会给您带来任何问题,添加一个单词甚至可以使用文本编辑器,相同的记事本甚至是我们的编程环境。

最终代码
 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; } } 

要记住的主要内容:UTF-8-BOM-用于在Excel中工作,UTF-8用于与代码工作,请不要忘记。

Source: https://habr.com/ru/post/zh-CN446844/


All Articles