using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using TinaX.I18N.Const; using TinaX.Services; using UniRx; using UnityEngine; namespace TinaX.I18N.Internal { public class I18NManager : II18N, II18NInternal, ILocalizationService { [Inject] public IAssetService mAssets { get; set; } private I18NConfig mConfig; private bool mInited = false; private string m_RegionName = string.Empty; /// /// 当前使用区域对应的语言列表 /// private IEnumerable m_CurrentLanguageNames; private IEnumerable m_CurrentLanguages; /// /// Global dict /// [Group Name] -> [Key] -> Value; /// private Dictionary> mDicts = new Dictionary>(); public async Task Start() { if (mInited) return null; mConfig = XConfig.GetConfig(I18NConst.ConfigPath_Resources, AssetLoadType.Resources); if (mConfig == null) return new XException("[TinaX.I18N]Load I18N config file failed. please cheak in ProjectSettings window."); if (!mConfig.EnableI18N) return null; if(mAssets == null) return new XException("[TinaX.I18N]No service implements the built-in asset loading interface in Framework"); if (mConfig.Regions != null && mConfig.Regions.Count > 0) { bool check_default = !mConfig.DefaultRegion.IsNullOrEmpty(); string default_region_name = check_default ? mConfig.DefaultRegion.ToLower() : string.Empty; XRegion _default_region = null; foreach(var item in mConfig.Regions) { if (item.Name.IsNullOrEmpty()) continue; bool _flag_1 = false; if(check_default && _default_region == null) { if (item.Name.ToLower().Equals(default_region_name)) { _default_region = item; _flag_1 = true; } } if (mConfig.AutomaticMatchingBySystemLanaguage) { bool flag_2 = false; if(item.BindLanguage != null && item.BindLanguage.Count > 0) { foreach(var lang in item.BindLanguage) { if(lang == Application.systemLanguage) { flag_2 = true; _default_region = item; break; } } } if (flag_2) break; } else { if (_flag_1) break; } } if (_default_region != null) { try { await this.useRegion(_default_region); } catch(XException e) { return e; } } } //Debug code //foreach(var item in mDicts) //{ // item.Key.LogConsole(); // foreach(var kv in item.Value) // { // (" " + kv.Key + " | " + kv.Value).LogConsole(); // } //} mInited = true; return null; } public IEnumerable GetCurrentLanguages() => m_CurrentLanguages; public Task UseRegionAsync(string regionName) { if (!mInited) throw new XException("[TinaX.I18N] Cannot \"UseRegion\" before framework started."); if(mConfig == null) throw new Exception("[TinaX.I18N] I18n config not found. cannot set region"); if (regionName.IsNullOrEmpty()) throw new ArgumentNullException(nameof(regionName)); if (mConfig.Regions == null || mConfig.Regions.Count == 0) { Debug.LogWarning("[TinaX.I18N] None valid region in i18n config."); return Task.CompletedTask; } foreach(var item in mConfig.Regions) { if (item.Name.ToLower().Equals(regionName.ToLower())) { return useRegion(item); } } Debug.LogWarning("[TinaX.I18N] Not found region \"" + regionName + "\" in config."); return Task.CompletedTask; } public void UseRegionAsync(string regionName, Action callback) { this.UseRegionAsync(regionName) .ToObservable() .ObserveOnMainThread() .Subscribe(_ => { callback?.Invoke(null); }, e => { if (e is XException) callback(e as XException); else Debug.LogException(e); }); } public string GetText(string key, string groupName = I18NConst.DefaultGroupName, string defaultText = null) { if (mDicts.TryGetValue(groupName, out var kv)) { if (kv.TryGetValue(key, out var value)) return value; } return defaultText == null ? key : defaultText; } private async Task useRegion(XRegion region) { if (region == null) throw new ArgumentNullException(nameof(region)); mDicts.Clear(); if(region.JsonDicts != null && region.JsonDicts.Count > 0) { List> load_task = new List>(); List load_task_index = new List(); for(var i = 0; i < region.JsonDicts.Count; i++) { if (!region.JsonDicts[i].LoadPath.IsNullOrEmpty()) { load_task.Add(this.getJsonDict(region.JsonDicts[i].LoadPath)); load_task_index.Add(i); } } if(load_task.Count > 0) { await Task.WhenAll(load_task); for(var i = 0; i < load_task.Count; i++) { foreach(var item in load_task) { var json_obj = item.Result; bool base64_value = region.JsonDicts[load_task_index[i]].Base64Value; string groupName = region.JsonDicts[load_task_index[i]].GroupName; if (groupName.IsNullOrEmpty()) groupName = I18NConst.DefaultGroupName; foreach(var kv in json_obj.data) { if (!mDicts.ContainsKey(groupName)) mDicts.Add(groupName, new Dictionary()); if (!mDicts[groupName].ContainsKey(kv.k)) { if (base64_value) { try { string _value = kv.v.DecodeBase64(); mDicts[groupName].Add(kv.k, _value); } catch { Debug.LogError($"[TinaX.I18N] Decode base64 error: Json: {region.JsonDicts[load_task_index[i]].LoadPath} | Key: {kv.k}"); } } else mDicts[groupName].Add(kv.k, kv.v); } } } } } } if(region.AssetDicts != null && region.AssetDicts.Count > 0) { foreach(var item in region.AssetDicts) { if (item.Asset == null) continue; string groupName = item.GroupName.IsNullOrEmpty() ? I18NConst.DefaultGroupName : item.GroupName; foreach(var kv in item.Asset.data) { if (!mDicts.ContainsKey(groupName)) mDicts.Add(groupName, new Dictionary()); if (!mDicts[groupName].ContainsKey(kv.k)) mDicts[groupName].Add(kv.k, kv.v); } } } m_RegionName = region.Name; this.m_CurrentLanguageNames = region.BindLanguage.Select(l => l.ToString()).ToArray(); this.m_CurrentLanguages = region.BindLanguage.ToArray(); Debug.Log($"[{I18NConst.ServiceName}] {(IsCmnHans() ? "使用区域:" : "Use region: ")} {region.Name}"); XEvent.Call(I18NEventConst.OnUseRegion, region.Name, TinaX.Const.FrameworkConst.FrameworkEventGroupName); } private async Task getJsonDict(string loadPath) { var text_asset = await mAssets.LoadAsync(loadPath); var json_obj = JsonUtility.FromJson(text_asset.text); mAssets.Release(text_asset); return json_obj; } private bool IsCmnHans() { if (this.m_CurrentLanguages == null) return false; if (this.m_CurrentLanguages.Any(l => l == SystemLanguage.Chinese || l == SystemLanguage.ChineseSimplified)) return true; else return false; } } }