using System;
using System.Collections;
using Ichni.Diagnostics;
using UnityEngine;
using UnityEngine.Localization.Settings;
using UnityEngine.ResourceManagement.AsyncOperations;
namespace Ichni
{
///
/// Unity Localization 的统一启动入口。
///
/// 本类只包装 Unity 官方提供的 ,
/// 不自行初始化 Addressables、不查询 Locale Label,也不复制 Localization Package 的内部加载流程。
/// 因此 Settings、Yarn 与其它本地化消费者始终等待同一个由 Unity 管理的初始化操作。
///
///
public static class LocalizationBootstrap
{
public enum InitializationState
{
NotStarted,
Initializing,
Ready,
Failed
}
private static AsyncOperationHandle _initializationOperation;
public static InitializationState State { get; private set; } = InitializationState.NotStarted;
public static bool IsReady => State == InitializationState.Ready;
public static bool HasFailed => State == InitializationState.Failed;
public static string FailureReason { get; private set; }
///
/// Unity Localization 官方初始化 Handle。
/// 获取该属性会以幂等方式启动初始化,但不会同步阻塞主线程。
///
public static AsyncOperationHandle InitializationOperation
{
get
{
BeginInitialization();
return _initializationOperation;
}
}
///
/// 在关闭 Domain Reload 的 Editor 配置下,清理上一次 Play Mode 留下的项目侧状态。
/// Localization Package 会自行重建其内部 Handle,本类不持有也不释放 Package 的资源。
///
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetState()
{
_initializationOperation = default;
State = InitializationState.NotStarted;
FailureReason = null;
}
///
/// 按 Unity 官方流程获取唯一的 Localization 初始化操作。
/// 可以由多个系统重复调用;已经启动后不会创建第二条加载链。
///
public static void BeginInitialization()
{
if (State != InitializationState.NotStarted)
{
return;
}
try
{
// 不设置 InitializeSynchronously,也不直接访问 SelectedLocale。
// InitializationOperation 会异步完成 Locale 选择及所有配置为 Preload 的数据库初始化。
_initializationOperation = LocalizationSettings.InitializationOperation;
State = InitializationState.Initializing;
Log("开始等待 Unity Localization 官方 InitializationOperation。");
RefreshState();
}
catch (Exception exception)
{
Fail("无法创建 Unity Localization 初始化操作。", exception);
}
}
///
/// 刷新初始化结果。供 YarnTask 等不能直接 yield AsyncOperationHandle 的异步流程调用。
///
public static void RefreshState()
{
if (State != InitializationState.Initializing)
{
return;
}
if (!_initializationOperation.IsValid())
{
Fail("Unity Localization 返回了无效的 InitializationOperation Handle。");
return;
}
if (!_initializationOperation.IsDone)
{
return;
}
if (_initializationOperation.Status != AsyncOperationStatus.Succeeded)
{
Fail("Unity Localization 初始化失败。", _initializationOperation.OperationException);
return;
}
LocaleReady();
}
///
/// 供 MonoBehaviour 协程等待 Unity Localization 初始化。
/// 此处直接 yield 官方 Handle,不使用轮询超时或自建 Addressables 状态机。
///
public static IEnumerator WaitForInitialization()
{
BeginInitialization();
if (State == InitializationState.Initializing)
{
yield return _initializationOperation;
RefreshState();
}
}
private static void LocaleReady()
{
State = InitializationState.Ready;
FailureReason = null;
// SelectedLocale 只有在完整 InitializationOperation 成功后才允许同步读取。
string localeCode = LocalizationSettings.SelectedLocale != null
? LocalizationSettings.SelectedLocale.Identifier.Code
: "";
Log($"Unity Localization 初始化完成。SelectedLocale={localeCode}");
}
private static void Fail(string message, Exception exception = null)
{
FailureReason = exception == null ? message : $"{message} Exception={exception}";
State = InitializationState.Failed;
Debug.LogError($"[LocalizationBootstrap] {FailureReason}");
BuildHangTracer.Log("LOCALIZATION_BOOTSTRAP", $"[Failed] {FailureReason}");
}
private static void Log(string message)
{
Debug.Log($"[LocalizationBootstrap] {message}");
BuildHangTracer.Log("LOCALIZATION_BOOTSTRAP", message);
}
}
}