IEnumerator loadLanguage(Language language) { System.DateTime started = System.DateTime.Now; string filePath = Path.Combine(Application.streamingAssetsPath, "Languages/" + language.getFileName()); languageString = ""; if (filePath.Contains("://")) { WWW www = new WWW(filePath); yield return(www); languageString = www.text; } else { languageString = File.ReadAllText(filePath); } localizedText = SerializedNestedStrings.deserialize(languageString); System.TimeSpan timeElapsed = System.DateTime.Now - started; Debug.Log("Language " + language.getFileName() + " loaded in " + timeElapsed.TotalMilliseconds + "ms"); PrefsHelper.setPreferredLanguage(language.getLanguageID()); loadedLanguage = language; languageString = ""; }
IEnumerator loadLanguage(Language language) { System.DateTime started = System.DateTime.Now; string filePath = Path.Combine(Application.streamingAssetsPath, "Languages/" + language.getFileName()); languageString = ""; if (filePath.Contains("://")) { WWW www = new WWW(filePath); yield return(www); languageString = www.text; } else { languageString = File.ReadAllText(filePath); } localizedText = SerializedNestedStrings.deserialize(languageString); System.TimeSpan timeElapsed = System.DateTime.Now - started; Debug.Log("Language " + language.getFileName() + " loaded in " + timeElapsed.TotalMilliseconds + "ms"); PrefsHelper.setPreferredLanguage(language.getLanguageID()); languageFontMetadata = localizedText.getSubData("meta.font"); loadedLanguageIsComplete = false; loadedLanguage = language; languageString = ""; loadedLanguageIsComplete = getLocalizedValue("generic.complete", "N").Equals("Y", System.StringComparison.OrdinalIgnoreCase); if (onLanguageChanged != null) { onLanguageChanged(language); } }
bool checkEntryIntegrity(SerializedNestedStrings languageData, string key, string value, SerializedNestedStrings englishData) { if (englishData != null && englishData != languageData) { // Check parameter counts var paramCount = 0; while (true) { if (!value.Contains("{" + paramCount.ToString() + "}")) { break; } paramCount++; } var englishText = englishData[key]; if (englishText != null) { var englishParamCount = 0; while (true) { if (!englishText.Contains("{" + englishParamCount.ToString() + "}")) { break; } englishParamCount++; } if (paramCount != englishParamCount) { Debug.LogWarning($"Language {getLanguageIdName(languageData)} has an inconsistent parameter count in key {key}"); } } } return(true); }
public static SerializedNestedStrings deserialize(string serializedData, SerializedNestedStrings existingStrings = null) { serializedData = serializedData.Replace(((char)13).ToString(), ""); //Remove any instances of carriage return if (existingStrings == null) { existingStrings = new SerializedNestedStrings(); } deserializeData(existingStrings.rootData, serializedData.Split('\n'), 0, 0); return(existingStrings); }
public static SerializedNestedStrings deserialize(string serializedData) { serializedData = serializedData.Replace(((char)13).ToString(), ""); //Remove any instances of carriage return List <string> lines = new List <string>(serializedData.Split('\n')); SerializedNestedStrings newData = new SerializedNestedStrings(); string currentKeyBase = ""; for (int i = 0; i < lines.Count; i++) { string line = lines[i]; int tabCount = Regex.Match(line, @"^\t*").Value.Length, dotCount = currentKeyBase.Split('.').Length - 1; while (tabCount < dotCount) { //Key is up from previous line in hierarchy //Remove "{key}." from current hierarchy string to go up one level if (dotCount == 1) { currentKeyBase = ""; } else { currentKeyBase = Regex.Replace(currentKeyBase, @"(.*)\.[^\.]*.$", @"$1."); } dotCount--; } if (line.Contains("=")) { //line is string value, add to deserialized data string key = Regex.Replace(line.Split('=')[0], @"^\t*", ""), value = Regex.Replace(line, @"^[^=]*=(.*)", @"$1"); newData[currentKeyBase + key] = value; } else { //line is beginning of a group, increase nest level by adding line to keystring base currentKeyBase += Regex.Replace(line, @"^\t*", "") + "."; } } return(newData); }
public void loadLocalizedText(string filename) { string filePath = Path.Combine(Application.streamingAssetsPath, filename); if (!File.Exists(filePath)) { filePath = filePath.Replace(language, "English"); Debug.Log("Language " + language + " not found. Using English"); language = "English"; } if (File.Exists(filePath)) { System.DateTime started = System.DateTime.Now; localizedText = SerializedNestedStrings.deserialize(File.ReadAllText(filePath)); System.TimeSpan timeElapsed = System.DateTime.Now - started; Debug.Log("Language " + language + " loaded successfully. Deserialization time: " + timeElapsed.TotalMilliseconds + "ms"); } else { Debug.LogError("No English json found!"); } }
//Use the second row sheet buffer to get proper codenames for langauges string getLanguageIdName(SerializedNestedStrings languageData) { return(languageData[idNameKey]); }
public void updateLanguages() { var languages = new Dictionary <string, SerializedNestedStrings>(); SerializedNestedStrings englishData = null; var sheetTitles = new List <string>(); var missingValues = new Dictionary <string, Dictionary <string, int> >(); for (int i = 1; i <= subsheetCount; i++) { var sheet = GDocService.GetSpreadsheet(spreadsheetId, i); var sheetTitle = sheet.Title.Text; sheetTitles.Add(sheetTitle); // Ran only at start of loop, but necessary here so we don't have to read the first sheet twice if (i == 1) { languages = generateLanguageDict(sheet); englishData = languages.FirstOrDefault().Value; foreach (var language in languages) { missingValues[language.Key] = new Dictionary <string, int>(); } } // Missing values structure is initially populated with every language and sheet title set to 0 missing values foreach (var language in languages) { missingValues[language.Key][sheetTitle] = 0; } foreach (ListEntry row in sheet.Entries) { string rowKey = ""; foreach (ListEntry.Custom element in row.Elements) { if (element.LocalName.Equals(KeyIdentifier)) { rowKey = element.Value; } else if (languages.ContainsKey(element.LocalName)) { var languageData = languages[element.LocalName]; if (!string.IsNullOrEmpty(element.Value)) { var cleansedEntry = cleanseEntry(element.Value); if (checkEntryIntegrity(languageData, rowKey, cleansedEntry, englishData)) { languageData[rowKey] = cleansedEntry; } } else { if (!string.IsNullOrEmpty(englishData[rowKey])) { missingValues[element.LocalName][sheetTitle]++; } } } } } } string fullLanguagesPath = Path.Combine(Application.dataPath, languagesPath); foreach (var languageData in languages) { string name = getLanguageIdName(languageData.Value); File.WriteAllText(Path.Combine(fullLanguagesPath, name), languageData.Value.ToString()); var languageId = languageData.Value["generic.idname"]; if (string.IsNullOrEmpty(languageId) || !languagesData.languages.Any(a => a.getLanguageID().Equals(languageId))) { Debug.LogWarning($"Language {languageData.Key} not found in languages data."); } else { var metaRecordedStatus = languageData.Value["meta.recorded"]; if (string.IsNullOrEmpty(metaRecordedStatus) || !metaRecordedStatus.Equals("Y", System.StringComparison.OrdinalIgnoreCase)) { Debug.LogWarning($"Language {languageData.Key} does not have metadata recorded in google sheets"); } } } // Format missing text report var missingValuesLanguageReports = missingValues //.Where(language => language.Value.Any(sheet => sheet.Value > 0)) // Select from languages who have missing values whatsoever .Select(language => language.Key + ": " + language.Value.Sum(sheet => sheet.Value) + " - " // Sum up all missing values in a language + string.Join(", ", language.Value // Then list out each subsheet in that language and its amount of missing values .Where(sheet => sheet.Value > 0) // Exclude any sheets with no missing values .Select(sheet => sheet.Key + ": " + sheet.Value.ToString()))); // Write to log var reportText = "Push this file with any localization updates.\n\n"; reportText += $"Last pulled:\n{System.DateTime.Now.ToString()}\n"; reportText += "\nThis is the order the sheets were found in. If github tries to change them, rearrange the cells so they match this and update language content again.\n" + string.Join("\n", sheetTitles) + "\n"; reportText += "\nHow many values are missing translations from each language (doesn't count non-game pages such as Steam Store):\n" + string.Join("\n", missingValuesLanguageReports) + "\n"; File.WriteAllText(Path.Combine(Application.dataPath, reportFile), reportText); Debug.Log("Language content updated"); }
public List <LanguageFontCharData> GetMissingFontCharData(TMPFont forceFont = null, Language forceLanguage = null) { string fullLanguagesPath = Path.Combine(Application.dataPath, languagesPath); string fullCharsPath = Path.Combine(Application.dataPath, charsPath); var missingCharData = new List <LanguageFontCharData>(); foreach (var language in LanguagesData.instance.languages) { if (forceLanguage != null && forceLanguage != language) { continue; } var filePath = Path.Combine(fullLanguagesPath, language.getFileName()); var fontDict = SerializedNestedStrings.deserialize(File.ReadAllText(filePath)).getSubData("meta.font").subData; foreach (var fontKVPair in fontDict) { if (LocalizationManager.parseFontCompabilityString(language, fontKVPair.Value.value)) { // Font is marked as compatible var font = TMPFontsData.instance.fonts.FirstOrDefault(a => a.idName.Equals(fontKVPair.Key)); if (forceFont != null && forceFont != font) { continue; } if (font == null) { Debug.LogWarning(fontKVPair.Key + " is missing from TMP Fonts Data asset"); continue; } if (font.fontAsset == null) { Debug.LogWarning(fontKVPair.Key + " is missing associated TMPro font in TMP Fonts asset"); continue; } var charString = File.ReadAllText(Path.Combine(fullCharsPath, language.getFileName() + "Chars.txt")); charString = string.Join("", charString.Distinct()); List <char> currentChars = charString.ToCharArray().ToList(); currentChars.Add('a'); // Check fonts AND fallbacks var fallbackList = new List <TMP_FontAsset>(); fallbackList.Add(font.fontAsset); // Current language fallbackList.AddRange(font.fontAsset.fallbackFontAssets); // language's fallbacks fallbackList.AddRange(TMP_Settings.fallbackFontAssets); // Global fallbacks fallbackList = fallbackList.Distinct().ToList(); foreach (var fontAsset in fallbackList) { var missingChars = new List <char>(); // NO idea why but the hasCharacters() function seems to just be true all the time, so also check for null/any missingChars = currentChars.Where(a => !fontAsset.characterDictionary.ContainsKey((int)a)).ToList(); if (missingChars != null && missingChars.Any()) { currentChars = missingChars; } else { currentChars = null; break; } } if (currentChars != null) { currentChars = currentChars.Except(ignoreChars.ToCharArray()).ToList(); if (currentChars.Any()) { missingCharData.Add(new LanguageFontCharData(language, font, string.Join("", currentChars))); } } } } } return(missingCharData .OrderBy(a => a.font.fontAsset.name) .ThenBy(a => a.language.getLanguageID()) .ToList()); }