diff --git a/Assets/Editor/Localization/LocalizedObjSpriteValidator.cs b/Assets/Editor/Localization/LocalizedObjSpriteValidator.cs deleted file mode 100644 index 1fa5cae49..000000000 --- a/Assets/Editor/Localization/LocalizedObjSpriteValidator.cs +++ /dev/null @@ -1,416 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using AibisDream.UI; -using UnityEditor; -using UnityEditor.AddressableAssets; -using UnityEditor.AddressableAssets.Settings; -using UnityEditor.AddressableAssets.Settings.GroupSchemas; -using UnityEditor.Build; -using UnityEditor.Build.Reporting; -using UnityEngine; - -namespace AibisDream.EditorTools.Localization -{ - internal readonly struct LocalizedObjSpriteDirectory - { - internal LocalizedObjSpriteDirectory( - string localeCode, - string folderName, - string addressRoot, - string groupName) - { - LocaleCode = localeCode; - FolderName = folderName; - AddressRoot = addressRoot; - GroupName = groupName; - } - - internal string LocaleCode { get; } - internal string FolderName { get; } - internal string AddressRoot { get; } - internal string GroupName { get; } - } - - public readonly struct LocalizedObjSpriteValidationIssue - { - public LocalizedObjSpriteValidationIssue(string assetPath, string message) - { - AssetPath = assetPath; - Message = message; - } - - public string AssetPath { get; } - public string Message { get; } - - public override string ToString() - { - return $"{AssetPath}: {Message}"; - } - } - - /// - /// Validates locale-specific Obj folders without depending on Yarn references or - /// Unity Localization asset tables. A logical image becomes localized as soon as - /// it appears in any non-Chinese locale folder. - /// - public static class LocalizedObjSpriteValidator - { - internal const string DefaultArtRoot = "Assets/RawResources/Art"; - internal const string DefaultFolderName = "Obj"; - internal const string DefaultGroupName = "Shared_UI"; - - internal static readonly LocalizedObjSpriteDirectory[] LocaleDirectories = - { - new("en", "Obj_en", "Sprite/Obj_en", "Shared_ObjEn"), - new("ja-JP", "Obj_ja-JP", "Sprite/Obj_ja-JP", "Shared_ObjJaJp"), - new("ru", "Obj_ru", "Sprite/Obj_ru", "Shared_ObjRu"), - new("es", "Obj_es", "Sprite/Obj_es", "Shared_ObjEs"), - new("pt-BR", "Obj_pt-BR", "Sprite/Obj_pt-BR", "Shared_ObjPtBr") - }; - - private const string ValidateMenuPath = - "Tools/AIBIS/Localization/Validate Localized Obj Sprites"; - private const string ConfigureMenuPath = - "Tools/AIBIS/Localization/Configure Localized Obj Addressables"; - - [MenuItem(ValidateMenuPath)] - public static void ValidateFromMenu() - { - IReadOnlyList issues = Validate(); - LogIssues(issues); - if (issues.Count == 0) - { - Debug.Log( - "[LocalizedObjSpriteValidator] Validation passed. " + - "All detected localized Obj sprite groups and Addressables are complete."); - } - } - - public static IReadOnlyList Validate( - string artRoot = DefaultArtRoot, - bool validateAddressables = true) - { - var issues = new List(); - if (!Directory.Exists(artRoot)) - { - issues.Add(new LocalizedObjSpriteValidationIssue( - ToAssetPath(artRoot), - "Art root does not exist.")); - return issues; - } - - string defaultDirectory = Path.Combine(artRoot, DefaultFolderName); - ValidateDirectoryExists( - defaultDirectory, - LocalizedObjSpriteNaming.DefaultLocaleCode, - issues); - - var localizedRelativePaths = new HashSet( - StringComparer.OrdinalIgnoreCase); - foreach (LocalizedObjSpriteDirectory directory in LocaleDirectories) - { - string localeDirectory = Path.Combine(artRoot, directory.FolderName); - if (!ValidateDirectoryExists(localeDirectory, directory.LocaleCode, issues)) - continue; - - foreach (string filePath in Directory.EnumerateFiles( - localeDirectory, - "*.png", - SearchOption.AllDirectories)) - { - localizedRelativePaths.Add(GetRelativePath(localeDirectory, filePath)); - } - } - - foreach (string relativePath in localizedRelativePaths.OrderBy( - path => path, - StringComparer.OrdinalIgnoreCase)) - { - ValidateRequiredFile( - Path.Combine(defaultDirectory, relativePath), - LocalizedObjSpriteNaming.DefaultLocaleCode, - issues); - - foreach (LocalizedObjSpriteDirectory directory in LocaleDirectories) - { - ValidateRequiredFile( - Path.Combine(artRoot, directory.FolderName, relativePath), - directory.LocaleCode, - issues); - } - } - - if (validateAddressables) - ValidateAddressableConfiguration(issues); - - return issues; - } - - [MenuItem(ConfigureMenuPath)] - public static void ConfigureAddressables() - { - AddressableAssetSettings settings = - AddressableAssetSettingsDefaultObject.Settings; - if (settings == null) - throw new InvalidOperationException("AddressableAssetSettings does not exist."); - - foreach (LocalizedObjSpriteDirectory directory in LocaleDirectories) - { - string assetPath = $"{DefaultArtRoot}/{directory.FolderName}"; - EnsureAssetFolder(assetPath); - - AddressableAssetGroup group = settings.FindGroup(directory.GroupName); - if (group == null) - { - group = settings.CreateGroup( - directory.GroupName, - false, - false, - true, - null, - typeof(BundledAssetGroupSchema), - typeof(ContentUpdateGroupSchema)); - } - - ConfigureGroup(settings, group); - - string guid = AssetDatabase.AssetPathToGUID(assetPath); - AddressableAssetEntry entry = - settings.CreateOrMoveEntry(guid, group, false, false); - entry.address = directory.AddressRoot; - } - - settings.SetDirty( - AddressableAssetSettings.ModificationEvent.BatchModification, - null, - true, - true); - EditorUtility.SetDirty(settings); - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - Debug.Log( - "[LocalizedObjSpriteValidator] Localized Obj folders and " + - "Addressable groups configured."); - } - - public static void ConfigureFromCommandLine() - { - ConfigureAddressables(); - } - - internal static void LogIssues( - IReadOnlyList issues) - { - foreach (LocalizedObjSpriteValidationIssue issue in issues) - Debug.LogError($"[LocalizedObjSpriteValidator] {issue}"); - } - - private static void ConfigureGroup( - AddressableAssetSettings settings, - AddressableAssetGroup group) - { - BundledAssetGroupSchema bundled = - group.GetSchema() ?? - group.AddSchema(); - bundled.BuildPath.SetVariableByName( - settings, - AddressableAssetSettings.kLocalBuildPath); - bundled.LoadPath.SetVariableByName( - settings, - AddressableAssetSettings.kLocalLoadPath); - bundled.IncludeInBuild = true; - bundled.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZ4; - bundled.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; - EditorUtility.SetDirty(bundled); - - ContentUpdateGroupSchema contentUpdate = - group.GetSchema() ?? - group.AddSchema(); - contentUpdate.StaticContent = false; - EditorUtility.SetDirty(contentUpdate); - EditorUtility.SetDirty(group); - } - - private static void ValidateAddressableConfiguration( - ICollection issues) - { - AddressableAssetSettings settings = - AddressableAssetSettingsDefaultObject.Settings; - if (settings == null) - { - issues.Add(new LocalizedObjSpriteValidationIssue( - "Assets/AddressableAssetsData", - "AddressableAssetSettings does not exist.")); - return; - } - - ValidateAddressableFolder( - settings, - $"{DefaultArtRoot}/{DefaultFolderName}", - LocalizedObjSpriteNaming.DefaultAddressRoot, - DefaultGroupName, - false, - issues); - - foreach (LocalizedObjSpriteDirectory directory in LocaleDirectories) - { - ValidateAddressableFolder( - settings, - $"{DefaultArtRoot}/{directory.FolderName}", - directory.AddressRoot, - directory.GroupName, - true, - issues); - } - } - - private static void ValidateAddressableFolder( - AddressableAssetSettings settings, - string assetPath, - string expectedAddress, - string expectedGroup, - bool validateGroupSchema, - ICollection issues) - { - string guid = AssetDatabase.AssetPathToGUID(assetPath); - AddressableAssetEntry entry = string.IsNullOrEmpty(guid) - ? null - : settings.FindAssetEntry(guid); - if (entry == null) - { - issues.Add(new LocalizedObjSpriteValidationIssue( - assetPath, - "Folder is not registered as an Addressable entry.")); - return; - } - - if (!string.Equals(entry.address, expectedAddress, StringComparison.Ordinal)) - { - issues.Add(new LocalizedObjSpriteValidationIssue( - assetPath, - $"Address is '{entry.address}', expected '{expectedAddress}'.")); - } - - AddressableAssetGroup group = entry.parentGroup; - if (group == null || !string.Equals( - group.Name, - expectedGroup, - StringComparison.Ordinal)) - { - issues.Add(new LocalizedObjSpriteValidationIssue( - assetPath, - $"Group is '{group?.Name ?? ""}', expected '{expectedGroup}'.")); - return; - } - - if (!validateGroupSchema) - return; - - BundledAssetGroupSchema bundled = group.GetSchema(); - if (bundled == null || - !bundled.IncludeInBuild || - bundled.Compression != BundledAssetGroupSchema.BundleCompressionMode.LZ4 || - bundled.BundleMode != BundledAssetGroupSchema.BundlePackingMode.PackTogether || - bundled.BuildPath.GetName(settings) != AddressableAssetSettings.kLocalBuildPath || - bundled.LoadPath.GetName(settings) != AddressableAssetSettings.kLocalLoadPath) - { - issues.Add(new LocalizedObjSpriteValidationIssue( - assetPath, - "Addressable group must use local paths, LZ4, Include In Build, and Pack Together.")); - } - - ContentUpdateGroupSchema contentUpdate = - group.GetSchema(); - if (contentUpdate == null || contentUpdate.StaticContent) - { - issues.Add(new LocalizedObjSpriteValidationIssue( - assetPath, - "Addressable group must use non-static content update settings.")); - } - } - - private static bool ValidateDirectoryExists( - string directoryPath, - string localeCode, - ICollection issues) - { - if (Directory.Exists(directoryPath)) - return true; - - issues.Add(new LocalizedObjSpriteValidationIssue( - ToAssetPath(directoryPath), - $"Missing required '{localeCode}' Obj directory.")); - return false; - } - - private static void ValidateRequiredFile( - string filePath, - string localeCode, - ICollection issues) - { - if (File.Exists(filePath)) - return; - - issues.Add(new LocalizedObjSpriteValidationIssue( - ToAssetPath(filePath), - $"Missing required '{localeCode}' sprite variant.")); - } - - private static string GetRelativePath(string rootPath, string filePath) - { - string normalizedRoot = Path.GetFullPath(rootPath) - .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + - Path.DirectorySeparatorChar; - string normalizedFile = Path.GetFullPath(filePath); - return normalizedFile.Substring(normalizedRoot.Length); - } - - private static void EnsureAssetFolder(string path) - { - string[] segments = path.Split('/'); - string current = segments[0]; - for (int index = 1; index < segments.Length; index++) - { - string next = $"{current}/{segments[index]}"; - if (!AssetDatabase.IsValidFolder(next)) - AssetDatabase.CreateFolder(current, segments[index]); - current = next; - } - } - - private static string ToAssetPath(string path) - { - return path.Replace('\\', '/'); - } - } - - public sealed class LocalizedObjSpriteBuildValidator : IPreprocessBuildWithReport - { - // Run before build preprocessors that mutate version information. - public int callbackOrder => -1000; - - public void OnPreprocessBuild(BuildReport report) - { - IReadOnlyList issues = - LocalizedObjSpriteValidator.Validate(); - if (issues.Count == 0) - return; - - LocalizedObjSpriteValidator.LogIssues(issues); - string summary = string.Join( - Environment.NewLine, - issues.Take(20).Select(issue => issue.ToString())); - if (issues.Count > 20) - { - summary += Environment.NewLine + - $"... and {issues.Count - 20} more issue(s)."; - } - - throw new BuildFailedException( - "Localized Obj sprite validation failed:" + - Environment.NewLine + summary); - } - } -} diff --git a/Assets/Editor/Localization/LocalizedObjSpriteValidator.cs.meta b/Assets/Editor/Localization/LocalizedObjSpriteValidator.cs.meta deleted file mode 100644 index 17fcfcaba..000000000 --- a/Assets/Editor/Localization/LocalizedObjSpriteValidator.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 686ca5acbb6647d4c802ecd2a8ba043d -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Editor/Tests/LocalizedObjSpriteTests.cs b/Assets/Editor/Tests/LocalizedObjSpriteTests.cs deleted file mode 100644 index 7576adb02..000000000 --- a/Assets/Editor/Tests/LocalizedObjSpriteTests.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using AibisDream.EditorTools.Localization; -using AibisDream.UI; -using NUnit.Framework; - -namespace AibisDream.SystemEditor.Tests -{ - public sealed class LocalizedObjSpriteTests - { - private string temporaryRoot; - - [SetUp] - public void SetUp() - { - temporaryRoot = Path.Combine( - Path.GetTempPath(), - "AibisDreamLocalizedObjSpriteTests", - Guid.NewGuid().ToString("N")); - Directory.CreateDirectory(temporaryRoot); - Directory.CreateDirectory(Path.Combine( - temporaryRoot, - LocalizedObjSpriteValidator.DefaultFolderName)); - foreach (LocalizedObjSpriteDirectory directory in - LocalizedObjSpriteValidator.LocaleDirectories) - { - Directory.CreateDirectory(Path.Combine( - temporaryRoot, - directory.FolderName)); - } - } - - [TearDown] - public void TearDown() - { - if (!string.IsNullOrEmpty(temporaryRoot) && Directory.Exists(temporaryRoot)) - Directory.Delete(temporaryRoot, true); - } - - [TestCase("zh-Hans", "Sprite/Obj/病历.png")] - [TestCase("zh-CN", "Sprite/Obj/病历.png")] - [TestCase("en", "Sprite/Obj_en/病历.png")] - [TestCase("en-US", "Sprite/Obj_en/病历.png")] - [TestCase("ja-JP", "Sprite/Obj_ja-JP/病历.png")] - [TestCase("ru-RU", "Sprite/Obj_ru/病历.png")] - [TestCase("es-ES", "Sprite/Obj_es/病历.png")] - [TestCase("pt-BR", "Sprite/Obj_pt-BR/病历.png")] - public void GetKey_UsesCanonicalLocaleDirectory( - string localeCode, - string expected) - { - Assert.That( - LocalizedObjSpriteNaming.GetKey("病历.png", localeCode), - Is.EqualTo(expected)); - } - - [Test] - public void CandidateNames_FallBackToUnsuffixedChineseWithoutDuplicates() - { - Assert.That( - LocalizedObjSpriteNaming.GetCandidateKeys("病历.png", "en"), - Is.EqualTo(new[] - { - "Sprite/Obj_en/病历.png", - "Sprite/Obj/病历.png" - })); - Assert.That( - LocalizedObjSpriteNaming.GetCandidateKeys("病历.png", "zh-Hans"), - Is.EqualTo(new[] { "Sprite/Obj/病历.png" })); - } - - [Test] - public void Validator_IgnoresUnsuffixedOnlySprites() - { - WriteSprite( - LocalizedObjSpriteValidator.DefaultFolderName, - "普通图片.png"); - - Assert.That( - LocalizedObjSpriteValidator.Validate(temporaryRoot, false), - Is.Empty); - } - - [Test] - public void Validator_RequiresEveryVariantAfterAGroupIsDetected() - { - WriteCompleteGroup("病历"); - Assert.That( - LocalizedObjSpriteValidator.Validate(temporaryRoot, false), - Is.Empty); - - File.Delete(Path.Combine(temporaryRoot, "Obj_ru", "病历.png")); - var issues = LocalizedObjSpriteValidator.Validate(temporaryRoot, false); - - Assert.That(issues, Has.Count.EqualTo(1)); - Assert.That(issues.Single().AssetPath, Does.EndWith("Obj_ru/病历.png")); - Assert.That(issues.Single().Message, Does.Contain("'ru'")); - } - - [Test] - public void ProjectLocaleFolders_AreAddressableByConvention() - { - Assert.That( - LocalizedObjSpriteValidator.Validate(), - Is.Empty); - } - - private void WriteCompleteGroup(string logicalName) - { - WriteSprite( - LocalizedObjSpriteValidator.DefaultFolderName, - $"{logicalName}.png"); - foreach (LocalizedObjSpriteDirectory directory in - LocalizedObjSpriteValidator.LocaleDirectories) - { - WriteSprite(directory.FolderName, $"{logicalName}.png"); - } - } - - private void WriteSprite(string folderName, string relativePath) - { - string filePath = Path.Combine(temporaryRoot, folderName, relativePath); - Directory.CreateDirectory(Path.GetDirectoryName(filePath)); - File.WriteAllBytes(filePath, Array.Empty()); - } - } -} diff --git a/Assets/Editor/Tests/LocalizedObjSpriteTests.cs.meta b/Assets/Editor/Tests/LocalizedObjSpriteTests.cs.meta deleted file mode 100644 index 2f13e56a9..000000000 --- a/Assets/Editor/Tests/LocalizedObjSpriteTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 212d27ffec81bcb48a886db924f43e1a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset index 6d0087dde..31485eaca 100644 --- a/ProjectSettings/ProjectSettings.asset +++ b/ProjectSettings/ProjectSettings.asset @@ -133,7 +133,7 @@ PlayerSettings: vulkanEnableLateAcquireNextImage: 0 vulkanEnableCommandBufferRecycling: 1 loadStoreDebugModeEnabled: 0 - bundleVersion: 0.6.1.16-20260731-152037 + bundleVersion: 0.6.2.1 preloadedAssets: - {fileID: 11400000, guid: 0fb2bc2476953fc43a74f0ab8879077c, type: 2} metroInputSource: 0