UI换了很多,spine主视觉停用
This commit is contained in:
@@ -0,0 +1,502 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Localization;
|
||||
using UnityEngine.Localization.Settings;
|
||||
|
||||
public static class LocalizationService
|
||||
{
|
||||
public const string LanguagePrefKey = "bansonic_language_code";
|
||||
public const string DefaultLanguageCode = "zh-CN";
|
||||
public const string GameTextTableCollectionName = "GameText";
|
||||
public const string LiteralTextTableCollectionName = "GameLiteralText";
|
||||
|
||||
private static readonly object SyncRoot = new object();
|
||||
private static bool _initialized;
|
||||
private static string _currentLanguageCode = DefaultLanguageCode;
|
||||
private static readonly Dictionary<string, Dictionary<string, string>> EntryFallbacks = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
|
||||
private static readonly Dictionary<string, Dictionary<string, string>> LiteralFallbacks = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public static event Action<string> LanguageChanged;
|
||||
|
||||
public static string CurrentLanguageCode
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _currentLanguageCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsEnglish => string.Equals(CurrentLanguageCode, "en-US", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
public static IReadOnlyList<string> GetAvailableLanguageCodes()
|
||||
{
|
||||
EnsureInitialized();
|
||||
|
||||
if (!LocalizationSettings.HasSettings || LocalizationSettings.AvailableLocales == null)
|
||||
{
|
||||
return new[] { DefaultLanguageCode, "en-US" };
|
||||
}
|
||||
|
||||
IList<Locale> locales = LocalizationSettings.AvailableLocales.Locales;
|
||||
if (locales == null || locales.Count == 0)
|
||||
{
|
||||
return new[] { DefaultLanguageCode, "en-US" };
|
||||
}
|
||||
|
||||
List<string> results = new List<string>(locales.Count);
|
||||
for (int i = 0; i < locales.Count; i++)
|
||||
{
|
||||
Locale locale = locales[i];
|
||||
if (locale == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string normalized = NormalizeLanguageCode(locale.Identifier.Code);
|
||||
if (!results.Contains(normalized))
|
||||
{
|
||||
results.Add(normalized);
|
||||
}
|
||||
}
|
||||
|
||||
if (results.Count == 0)
|
||||
{
|
||||
results.Add(DefaultLanguageCode);
|
||||
results.Add("en-US");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!results.Contains(DefaultLanguageCode))
|
||||
{
|
||||
results.Insert(0, DefaultLanguageCode);
|
||||
}
|
||||
|
||||
if (!results.Contains("en-US"))
|
||||
{
|
||||
results.Add("en-US");
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public static void EnsureInitialized()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (SyncRoot)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_currentLanguageCode = NormalizeLanguageCode(PlayerPrefs.GetString(LanguagePrefKey, ResolveStartupLanguageCode()));
|
||||
PlayerPrefs.SetString(LanguagePrefKey, _currentLanguageCode);
|
||||
PlayerPrefs.Save();
|
||||
LoadRuntimeFallbackPack(DefaultLanguageCode);
|
||||
LoadRuntimeFallbackPack("en-US");
|
||||
|
||||
if (LocalizationSettings.HasSettings)
|
||||
{
|
||||
LocalizationSettings.InitializeSynchronously = true;
|
||||
var init = LocalizationSettings.InitializationOperation;
|
||||
if (!init.IsDone)
|
||||
{
|
||||
init.WaitForCompletion();
|
||||
}
|
||||
|
||||
if (LocalizationSettings.StringDatabase != null)
|
||||
{
|
||||
LocalizationSettings.StringDatabase.MissingTranslationState = (MissingTranslationBehavior)0;
|
||||
}
|
||||
|
||||
LocalizationSettings.SelectedLocaleChanged -= HandleSelectedLocaleChanged;
|
||||
LocalizationSettings.SelectedLocaleChanged += HandleSelectedLocaleChanged;
|
||||
ApplySelectedLocale(_currentLanguageCode, savePreference: true, notifyListeners: false);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetLanguage(string languageCode)
|
||||
{
|
||||
EnsureInitialized();
|
||||
string normalized = NormalizeLanguageCode(languageCode);
|
||||
if (string.Equals(_currentLanguageCode, normalized, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (LocalizationSettings.HasSettings)
|
||||
{
|
||||
ApplySelectedLocale(normalized, savePreference: true, notifyListeners: false);
|
||||
return;
|
||||
}
|
||||
|
||||
_currentLanguageCode = normalized;
|
||||
PlayerPrefs.SetString(LanguagePrefKey, _currentLanguageCode);
|
||||
PlayerPrefs.Save();
|
||||
LanguageChanged?.Invoke(_currentLanguageCode);
|
||||
}
|
||||
|
||||
public static string Get(string key, string fallback = null)
|
||||
{
|
||||
if (TryGet(key, out string localized))
|
||||
{
|
||||
return localized;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return fallback ?? string.Empty;
|
||||
}
|
||||
|
||||
return fallback ?? key;
|
||||
}
|
||||
|
||||
public static string GetFormat(string key, params object[] args)
|
||||
{
|
||||
string format = Get(key, key);
|
||||
try
|
||||
{
|
||||
return string.Format(CultureInfo.InvariantCulture, format, args);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
public static string LocalizeLiteral(string source)
|
||||
{
|
||||
if (TryGetLiteral(source, out string localized))
|
||||
{
|
||||
return localized;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(source))
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
public static bool TryGet(string key, out string localized)
|
||||
{
|
||||
localized = null;
|
||||
EnsureInitialized();
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
localized = ResolveFromTable(GameTextTableCollectionName, key);
|
||||
if (!string.IsNullOrEmpty(localized))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
localized = ResolveFromRuntimeFallback(EntryFallbacks, key);
|
||||
return !string.IsNullOrEmpty(localized);
|
||||
}
|
||||
|
||||
public static bool TryGetLiteral(string source, out string localized)
|
||||
{
|
||||
localized = null;
|
||||
EnsureInitialized();
|
||||
if (string.IsNullOrWhiteSpace(source))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
localized = ResolveFromTable(LiteralTextTableCollectionName, source);
|
||||
if (!string.IsNullOrEmpty(localized))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
localized = ResolveFromRuntimeFallback(LiteralFallbacks, source);
|
||||
return !string.IsNullOrEmpty(localized);
|
||||
}
|
||||
|
||||
private static void HandleSelectedLocaleChanged(Locale locale)
|
||||
{
|
||||
_currentLanguageCode = NormalizeLanguageCode(locale?.Identifier.Code ?? DefaultLanguageCode);
|
||||
PlayerPrefs.SetString(LanguagePrefKey, _currentLanguageCode);
|
||||
PlayerPrefs.Save();
|
||||
LanguageChanged?.Invoke(_currentLanguageCode);
|
||||
}
|
||||
|
||||
private static void ApplySelectedLocale(string languageCode, bool savePreference, bool notifyListeners)
|
||||
{
|
||||
string normalized = NormalizeLanguageCode(languageCode);
|
||||
Locale targetLocale = FindLocale(normalized) ?? FindLocale(DefaultLanguageCode);
|
||||
if (targetLocale == null)
|
||||
{
|
||||
_currentLanguageCode = normalized;
|
||||
if (savePreference)
|
||||
{
|
||||
PlayerPrefs.SetString(LanguagePrefKey, _currentLanguageCode);
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
|
||||
if (notifyListeners)
|
||||
{
|
||||
LanguageChanged?.Invoke(_currentLanguageCode);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool localeChanged = LocalizationSettings.SelectedLocale == null ||
|
||||
!string.Equals(LocalizationSettings.SelectedLocale.Identifier.Code, targetLocale.Identifier.Code, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
_currentLanguageCode = NormalizeLanguageCode(targetLocale.Identifier.Code);
|
||||
if (savePreference)
|
||||
{
|
||||
PlayerPrefs.SetString(LanguagePrefKey, _currentLanguageCode);
|
||||
PlayerPrefs.Save();
|
||||
}
|
||||
|
||||
if (localeChanged)
|
||||
{
|
||||
LocalizationSettings.SelectedLocale = targetLocale;
|
||||
}
|
||||
else if (notifyListeners)
|
||||
{
|
||||
LanguageChanged?.Invoke(_currentLanguageCode);
|
||||
}
|
||||
}
|
||||
|
||||
private static Locale FindLocale(string languageCode)
|
||||
{
|
||||
if (!LocalizationSettings.HasSettings || LocalizationSettings.AvailableLocales == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string normalized = NormalizeLanguageCode(languageCode);
|
||||
IList<Locale> locales = LocalizationSettings.AvailableLocales.Locales;
|
||||
if (locales == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < locales.Count; i++)
|
||||
{
|
||||
Locale locale = locales[i];
|
||||
if (locale == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.Equals(NormalizeLanguageCode(locale.Identifier.Code), normalized, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
|
||||
if (normalized.StartsWith("en", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
for (int i = 0; i < locales.Count; i++)
|
||||
{
|
||||
Locale locale = locales[i];
|
||||
if (locale != null && locale.Identifier.Code.StartsWith("en", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (normalized.StartsWith("zh", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
for (int i = 0; i < locales.Count; i++)
|
||||
{
|
||||
Locale locale = locales[i];
|
||||
if (locale != null && locale.Identifier.Code.StartsWith("zh", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string ResolveFromTable(string tableName, string entryKey)
|
||||
{
|
||||
if (!LocalizationSettings.HasSettings || LocalizationSettings.StringDatabase == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string value = LocalizationSettings.StringDatabase.GetLocalizedString(tableName, entryKey, LocalizationSettings.SelectedLocale);
|
||||
return string.IsNullOrEmpty(value) ? null : value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[LocalizationService] Failed to resolve '{entryKey}' from '{tableName}': {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void LoadRuntimeFallbackPack(string languageCode)
|
||||
{
|
||||
string normalized = NormalizeLanguageCode(languageCode);
|
||||
if (EntryFallbacks.ContainsKey(normalized) && LiteralFallbacks.ContainsKey(normalized))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string path = Path.Combine(Application.streamingAssetsPath, "localization", normalized + ".json");
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
EntryFallbacks[normalized] = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
LiteralFallbacks[normalized] = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string json = File.ReadAllText(path);
|
||||
LocalizationLanguagePack pack = Newtonsoft.Json.JsonConvert.DeserializeObject<LocalizationLanguagePack>(json)
|
||||
?? new LocalizationLanguagePack();
|
||||
|
||||
EntryFallbacks[normalized] = pack.entries ?? new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
LiteralFallbacks[normalized] = pack.literals ?? new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[LocalizationService] Failed to load runtime pack '{normalized}': {ex.Message}");
|
||||
EntryFallbacks[normalized] = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
LiteralFallbacks[normalized] = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ResolveFromRuntimeFallback(Dictionary<string, Dictionary<string, string>> source, string key)
|
||||
{
|
||||
string normalized = NormalizeLanguageCode(_currentLanguageCode);
|
||||
LoadRuntimeFallbackPack(normalized);
|
||||
|
||||
if (source.TryGetValue(normalized, out Dictionary<string, string> primary) &&
|
||||
primary != null &&
|
||||
primary.TryGetValue(key, out string localized) &&
|
||||
!string.IsNullOrEmpty(localized))
|
||||
{
|
||||
return localized;
|
||||
}
|
||||
|
||||
if (!string.Equals(normalized, DefaultLanguageCode, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
LoadRuntimeFallbackPack(DefaultLanguageCode);
|
||||
if (source.TryGetValue(DefaultLanguageCode, out Dictionary<string, string> fallback) &&
|
||||
fallback != null &&
|
||||
fallback.TryGetValue(key, out localized) &&
|
||||
!string.IsNullOrEmpty(localized))
|
||||
{
|
||||
return localized;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string ResolveStartupLanguageCode()
|
||||
{
|
||||
return Application.systemLanguage switch
|
||||
{
|
||||
SystemLanguage.English => "en-US",
|
||||
_ => DefaultLanguageCode
|
||||
};
|
||||
}
|
||||
|
||||
private static string NormalizeLanguageCode(string languageCode)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(languageCode))
|
||||
{
|
||||
return DefaultLanguageCode;
|
||||
}
|
||||
|
||||
string trimmed = languageCode.Trim();
|
||||
if (string.Equals(trimmed, "en", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "en-US";
|
||||
}
|
||||
|
||||
if (string.Equals(trimmed, "zh", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(trimmed, "zh-Hans", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "zh-CN";
|
||||
}
|
||||
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class LocalizationLanguagePack
|
||||
{
|
||||
public string languageCode;
|
||||
public Dictionary<string, string> entries = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
public Dictionary<string, string> literals = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal sealed class LocalizationLanguagePackJsonSurrogate
|
||||
{
|
||||
public string languageCode;
|
||||
public SerializableDictionaryEntry[] entries;
|
||||
public SerializableDictionaryEntry[] literals;
|
||||
|
||||
public LocalizationLanguagePack ToPack()
|
||||
{
|
||||
var pack = new LocalizationLanguagePack
|
||||
{
|
||||
languageCode = languageCode,
|
||||
entries = new Dictionary<string, string>(StringComparer.Ordinal),
|
||||
literals = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
};
|
||||
|
||||
if (entries != null)
|
||||
{
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(entries[i].key))
|
||||
{
|
||||
pack.entries[entries[i].key] = entries[i].value ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (literals != null)
|
||||
{
|
||||
for (int i = 0; i < literals.Length; i++)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(literals[i].key))
|
||||
{
|
||||
pack.literals[literals[i].key] = literals[i].value ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pack;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
internal struct SerializableDictionaryEntry
|
||||
{
|
||||
public string key;
|
||||
public string value;
|
||||
}
|
||||
Reference in New Issue
Block a user