Localização de qualquer texto

A maioria dos jogos usa localização baseada em chave, ou seja, é necessária uma chave para descrever um texto específico, mas eu ofereço uma opção melhor, embora essa opção não seja adequada para quem tem dublagem em jogos, é mais fácil através de uma chave.

O que é uma chave e por que é necessário


A chave, ou melhor, a palavra-chave, é a palavra pela qual será determinado qual texto é necessário e, em seguida, a pesquisa pelo idioma selecionado já está em andamento. Um exemplo de palavra-chave: scene_Escape_from_jail_Ethan_dialog_with_Mary_3 , sim, a chave ficará assim se o seu jogo tiver muitas cenas, um grande enredo. Sugiro que imediatamente escreva uma frase em um dos idiomas, geralmente o inglês ou aquele em que o programador é fluente. A propósito, como todas as frases do idioma atual e do idioma principal estarão na RAM, será mais produtivo do que obter cada vez que o arquivo for utilizado, para jogos grandes, você poderá modificar levemente o arquivo para cada cena.

Como tudo será organizado


Nas ações descritas abaixo, será usada a classe Lang estática, na qual todas as pesquisas por palavras / frases serão realizadas. Vamos declarar uma classe conectando as bibliotecas que precisamos:

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 } 

E assim, não podemos ficar sem uma biblioteca padrão, já que obteremos o arquivo dos recursos, podemos baixá-lo de qualquer maneira conveniente, mas é mais conveniente e prático. São necessárias bibliotecas do sistema para conectar a lista, dicionário e trabalhar com arquivos do editor. A biblioteca UnityEditor é necessária apenas para atualizar o arquivo durante o primeiro registro da frase, porque Após uma rápida reinicialização, todas as frases nem sempre serão carregadas, mas com a ajuda desta biblioteca, podemos resolver esse problema. A classe armazena dois campos estáticos, sejam os nomes do arquivo; no nosso caso, o caminho é uma pasta com recursos e pode haver qualquer nome do arquivo.

Agora você precisa adicionar listas para armazenar tudo o que é usado pelo idioma e pelo dicionário.

 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 

O campo LangIndex manterá o índice do idioma atual em relação ao registro no arquivo. Na lista de idiomas - todos os idiomas usados ​​no arquivo serão gravados. O dicionário armazenará todas as frases no idioma principal e no idioma atual.

É necessário adicionar a inicialização dos campos descritos acima da classe.

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

Ele usará imediatamente a diretiva interna para que não execute ações desnecessárias após a compilação do aplicativo. A chamada para Lang.Starting (...) deve ser algo como isto:

 #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 


A chamada durante a reprodução no editor deve conter dois parâmetros principais, é isso que frases de idioma devem ser traduzidas agora e qual idioma será usado como idioma principal; todos os outros parâmetros são os parâmetros de idioma que devem estar contidos no arquivo; esses parâmetros são necessários apenas durante o primeiro lançamento, quando o arquivo ainda não foi criado (e não é necessário excluí-lo posteriormente), caso contrário, se você precisar adicionar algum idioma, copie tudo do arquivo, exclua o arquivo e execute novamente o código no editor ou adicione você mesmo ao arquivo.

O código acima usa o método SystemLanguageParse (...) , que simplesmente converte os nomes de idiomas do tipo de seqüência de caracteres em SystemLanguage (esse método será menor).

Vamos nos debruçar sobre o 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 

Como esse método será usado apenas ao iniciar a partir do editor, podemos usar com segurança o utilitário do sistema para substituir o arquivo, bem como atualizar os arquivos alterados para o editor usando o método Refresh (). Entre essas ações, uma frase é simplesmente adicionada ao dicionário, para se proteger de regravar na mesma sessão.

A propósito, esqueci de dizer que as frases serão armazenadas em um arquivo .csv, o que nos permitirá traduzir confortavelmente frases para o Excele. Agora precisamos adicionar um método que seja bom para nós, o que nos permitirá alterar o 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]); } } 

Então, chegamos ao método mais importante, que pega a frase no idioma principal e a publica no usuário certo:

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

Esse método simplesmente pega uma frase no idioma principal e, em seguida, repete tudo o que está no dicionário, encontrando tal frase por chave, nos fornece os valores dessa chave, que é uma frase no idioma de que precisamos. Você pode usar este método com uma linha de código simples:

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

Agora a frase no idioma que precisamos entrará na string str, se de repente ela estiver ausente, a frase especificada nos parâmetros, ou seja, Hello world, cairá.

Este método pode ser melhorado um pouco, para que você possa usar argumentos para preencher:

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

Agora, esse método pode ser chamado como antes:

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

Mas agora nosso método formatou a saída, indicando parâmetros separados por vírgulas:

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

Traduções de frases


Como escrevi acima, o arquivo usa extensões .csv que permitem fazer tudo no Excel, mas não tão simples, o problema do C-sharpe e do Excel é que eles entendem o alfabeto cirílico em codificação diferente, o Excel entende apenas a codificação UTF-8-BOM ou aquele que nosso YaP não entende, devemos usar apenas UTF-8 nele, mesmo que o editor unitário compreenda UTF-8-BOM, no código existem duas palavras idênticas em codificações diferentes (UTF-8 e UTF-8- BOM) não será igual, o que levará à adição constante de palavras idênticas ao nosso arquivo.

Podemos codificar arquivos usando o NotePad ++ gratuito baixando-o de fora. site. A edição de um arquivo não trará nenhum problema; para adicionar uma única palavra, você pode até usar um editor de texto, o mesmo bloco de notas ou mesmo nosso ambiente de programação.

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

O principal a lembrar: UTF-8-BOM - para trabalhar no Excel, UTF-8 para trabalhar com código, não se esqueça.

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


All Articles