Class Locale { static LOCALE_OBJECT := Map() static LOCALES_PATH := A_ScriptDir "\Locale\" static READ_RULES := Map( "en-US", Map("[On Combine] Add Space", True), ) static __New() { this.Fill() return } static GetReadRules(locale) { return this.READ_RULES.Get(locale, this.READ_RULES.Get(Language.FALLBACK_LOCALE.ISO)) } static InitReadRuleLocale(locale) { this.READ_RULES.Set(locale, this.READ_RULES.Get(Language.FALLBACK_LOCALE.ISO).Clone()) } static SetReadRule(locale, name, value) { this.READ_RULES[locale].Set(name, value) } static Fill() { this.LOCALE_OBJECT.Clear() local pathsArray := [] for lang, value in Language.supported { if value["locale"] { local iniPath := App.PATHS.LOC "\locale_" lang ".ini" local jsonPath := App.PATHS.LOC "\locale_" lang ".json" if FileExist(iniPath) pathsArray.Push(App.PATHS.LOC "\locale_" lang ".ini") if FileExist(jsonPath) pathsArray.Push(App.PATHS.LOC "\locale_" lang ".json") } } Loop Files App.PATHS.LOC "\Automated\*", "FR" if A_LoopFileFullPath ~= "i)\.(ini|json)$" pathsArray.Push(A_LoopFileFullPath) local activeMods := ModsInjector.GetActiveList() if activeMods.Length > 0 for folderName in activeMods if DirExist(ModsInjector.modsPath "\" folderName "\Locale") Loop Files ModsInjector.modsPath "\" folderName "\Locale\*", "FR" if A_LoopFileFullPath ~= "i)\.(ini|json)$" && !(A_LoopFileFullPath ~= "CharacterLegend") pathsArray.Push(A_LoopFileFullPath) this.LOCALE_OBJECT := this.ParseSourceFiles(pathsArray) return Event.Trigger("Locale", "Filled") } static ParseSourceFiles(pathsArray) { local bufferArray := [] for path in pathsArray { if path ~= "i)\.(ini)$" bufferArray.Push(Util.INIToMap(path)) else if path ~= "i)\.(json)$" { bufferArray.Push(JSON.LoadFile(path, "UTF-8")) } } return this.MergeLocales(bufferArray*) } static MergeLocales(maps*) { local output := Map() for i, mapToMerge in maps { if mapToMerge.Count = 0 continue for key, val in mapToMerge { if output.Has(key) && output[key] is Map { if val is String output[key].Set("__self", val) else if val is Map output[key] := this.MergeLocales(output[key], val) } else if output.Has(key) && output[key] is String && val is Map { local tempMap := Map() tempMap.Set("__self", output[key]) output[key] := this.MergeLocales(tempMap, val) } else { output.Set(key, val) } } } return output } static OpenDir(*) { Run(this.LOCALES_PATH) } static HandleString(str) { str := StrReplace(str, "\R2", "`r`n`r`n", True) str := StrReplace(str, "\R", "`r`n", True) str := StrReplace(str, "\r", "`r", True) str := StrReplace(str, "\n", "`n") str := StrReplace(str, "\t", "`t") return str } static Read(entryName, inSection := "", validate := False, &output?, strInjections := [], variantSelect := 1) { local intermediate := "" local section := Language.Validate(inSection) ? inSection : Language.Get() local combineRules := this.GetReadRules(section) local intermediate := this.ReadStr(§ion, &entryName, &combineRules) while (RegExMatch(intermediate, "\$\((.*?)\)", &match)) { intermediate := StrReplace(intermediate, match[0], this.VariantSelect(match[0], variantSelect)) } while (RegExMatch(intermediate, "\{@([a-zA-Z-]*)(?::([^\}]+))?\}", &match)) { local langCode := (match[1] != "" ? match[1] : section) local customEntry := (match[2] != "" ? match[2] : entryName) if customEntry = entryName && langCode = section { intermediate := StrReplace(intermediate, match[0], "DUPLICATED KEY REFERENCE (" entryName ") IN " section) } else { local replacement := this.ReadStr(&langCode, &customEntry, &combineRules) intermediate := StrReplace(intermediate, match[0], this.VariantSelect(replacement, variantSelect)) } } while (RegExMatch(intermediate, "\{U\+(.*?)\}", &match)) { local unicode := StrSplit(match[1], ",") local replacement := UnicodeUtils.GetBatchSymbols(unicode, "") intermediate := StrReplace(intermediate, match[0], replacement) } while (RegExMatch(intermediate, "(?", &match)) intermediate := StrReplace(intermediate, match[0], VariableParser.Parse(match[0])) while (RegExMatch(intermediate, "\{var:([^\}]+)\}", &match)) { local variableName := match[1] local parts := StrSplit(variableName, ".") local ref := %parts[1]% if (parts.Length > 1) { Loop parts.Length - 1 { ref := ref.%parts[A_Index + 1]% } } local replacement := ref intermediate := StrReplace(intermediate, match[0], replacement) } intermediate := this.HandleString(intermediate) if validate { output := intermediate return StrLen(intermediate) > 0 } else { intermediate := !IsSpace(intermediate) ? intermediate : "KEY (" entryName "): NOT FOUND in " section return strInjections ? Util.StrVarsInject(intermediate, strInjections*) : intermediate } } static ReadStr(§ion, &entry, &combineRules) { local options := [] if RegExMatch(entry, "^\[(.*?)\](.*)$", &optMatch) { options := StrSplit(optMatch[1], ",") entry := optMatch[2] } if RegExMatch(entry, "^(.*?\.)?([^.<]*)<([^>]*)>(.*)$", &multiMatch) { local i := 0 local starter := multiMatch[1] local firstKey := starter multiMatch[2] local secondKey := (multiMatch[4] ~= "^[.:]" ? firstKey : starter) multiMatch[4] local interKey := multiMatch[3] local outputString := "" for each in [firstKey, interKey, secondKey] if each != "" { local processedKey := this.ProcessLanguageReferences(&each, §ion, &combineRules) outputString .= (i > 0 && (combineRules["[On Combine] Add Space"] && !options.HasValue("-space")) ? " " : "") processedKey i++ } return outputString } else if RegExMatch(entry, "\+", &multiMatch) { local i := 0 local split := StrSplit(entry, "+") local outputString := "" for each in split if each != "" { local processedKey := this.ProcessLanguageReferences(&each, §ion, &combineRules) outputString .= (i > 0 && (combineRules["[On Combine] Add Space"] && !options.HasValue("-space")) ? " " : "") processedKey i++ } return outputString } local str := this.GetEntry(§ion, &entry) local output := "" if str is String return str if str is Map && str.Has("__self") output := str.Get("__self") if str is Array output := str.ToString("") return output } static ProcessLanguageReferences(&keyString, &defaultSection, &combineRules) { if RegExMatch(keyString, "^\{@([a-zA-Z-]*)(?::([^\}]+))?\}$", &langMatch) { local langCode := (langMatch[1] != "" ? langMatch[1] : defaultSection) local keyName := (langMatch[2] != "" ? langMatch[2] : keyString) if langMatch[2] == "" && langMatch[1] != "" return keyString return this.ReadStr(&langCode, &keyName, &combineRules) } return this.ReadStr(&defaultSection, &keyString, &combineRules) } static GetEntry(§ion, &entry) { if RegExMatch(entry, "[.:]") { local split := RegExSplit(entry, "[.:]") if !this.LOCALE_OBJECT.Has(section) return "" local current := this.LOCALE_OBJECT[section] for index, key in split { if current.HasProp("Has") && current.Has(key) { current := current[key] } else { if this.LOCALE_OBJECT.Has(section) && this.LOCALE_OBJECT[section].Has(entry) return this.LOCALE_OBJECT[section][entry] return "" } } return current } else if this.LOCALE_OBJECT.Has(section) && this.LOCALE_OBJECT[section].Has(entry) { return this.LOCALE_OBJECT[section][entry] } return "" } static ReadNoLinks(entryName, inSection := "") { baseString := this.Read(entryName, inSection) result := baseString while (RegExMatch(result, "]*>(.*?)", &match)) { linkText := match[1] result := StrReplace(result, match[0], linkText) } return result } static ReadInject(entryName, strInjections := [], inSection := "", validate := False) { return this.Read(entryName, inSection, validate, , strInjections) } static ReadMulti(entryNames*) { output := "" for entryName in entryNames { output .= " " this.Read(entryName) } return output } static VariantSelect(str, i) { output := str if (RegExMatch(str, "\$\((.*?)\)", &match)) { split := StrSplit(match[1], "|") if (i > 0 && i <= split.Length) { beforePart := SubStr(str, 1, match.Pos(0) - 1) afterPart := SubStr(str, match.Pos(0) + match.Len(0)) output := beforePart split[i] afterPart } } return output } }