feat: 火山玩法本地化
仍有硬编码残留,待处理
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AibisDream.Framework;
|
||||
using AibisDream.MiniGame.Language;
|
||||
using AibisDream.Utility;
|
||||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Localization;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Localization;
|
||||
using UnityEngine.Localization.Settings;
|
||||
using UnityEngine.Localization.Tables;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace AibisDream.EditorTests.Huoshan
|
||||
{
|
||||
public sealed class ExpressionLocalizationTests
|
||||
{
|
||||
private const string CatalogPath =
|
||||
"Assets/GameContent/Feature_Huoshan/Expression/ExpressionContentCatalog.asset";
|
||||
|
||||
[TestCase(null, false)]
|
||||
[TestCase("", false)]
|
||||
[TestCase("plain", false)]
|
||||
[TestCase("L10N.key", false)]
|
||||
[TestCase("l10n.key", true)]
|
||||
public void LocalizedParamPrefix_IsStrict(string value, bool expected)
|
||||
{
|
||||
Assert.That(LocalizationKit.IsLocalizedParam(value), Is.EqualTo(expected));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LocalizedParamKey_StripsOnlyKnownPrefix()
|
||||
{
|
||||
Assert.That(LocalizationKit.GetL10NParamKey("l10n.hs.exp.log1.target"),
|
||||
Is.EqualTo("hs.exp.log1.target"));
|
||||
Assert.That(LocalizationKit.GetL10NParamKey("raw"), Is.EqualTo("raw"));
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator LocalizeParamAsync_UsesExplicitLocaleAndDoesNotFallback()
|
||||
{
|
||||
Locale chinese = LocalizationEditorSettings.GetLocale(
|
||||
new LocaleIdentifier("zh-Hans"));
|
||||
Locale english = LocalizationEditorSettings.GetLocale(
|
||||
new LocaleIdentifier("en"));
|
||||
Assert.That(chinese, Is.Not.Null);
|
||||
Assert.That(english, Is.Not.Null);
|
||||
|
||||
Locale previous = LocalizationSettings.SelectedLocale;
|
||||
LocalizationSettings.SelectedLocale = english;
|
||||
Task<string> chineseTask = LocalizationKit.LocalizeParamAsync(
|
||||
"l10n.hs.exp.log1.target",
|
||||
chinese);
|
||||
Task<string> englishTask = LocalizationKit.LocalizeParamAsync(
|
||||
"l10n.hs.exp.log1.target",
|
||||
english);
|
||||
|
||||
while (!chineseTask.IsCompleted || !englishTask.IsCompleted)
|
||||
yield return null;
|
||||
|
||||
LocalizationSettings.SelectedLocale = previous;
|
||||
Assert.That(chineseTask.Result, Is.EqualTo("我就是个笑话"));
|
||||
Assert.That(englishTask.Result, Is.Empty);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator LocalizeParamAsync_ReturnsRawTextAndMissingKeyMarker()
|
||||
{
|
||||
Task<string> rawTask = LocalizationKit.LocalizeParamAsync("raw text");
|
||||
while (!rawTask.IsCompleted)
|
||||
yield return null;
|
||||
Assert.That(rawTask.Result, Is.EqualTo("raw text"));
|
||||
|
||||
Locale chinese = LocalizationEditorSettings.GetLocale(
|
||||
new LocaleIdentifier("zh-Hans"));
|
||||
LogAssert.Expect(
|
||||
LogType.Error,
|
||||
new Regex(@"Params 缺少条目.*Key=hs\.exp\.missing.*Locale=zh-Hans"));
|
||||
Task<string> missingTask = LocalizationKit.LocalizeParamAsync(
|
||||
"l10n.hs.exp.missing",
|
||||
chinese);
|
||||
while (!missingTask.IsCompleted)
|
||||
yield return null;
|
||||
Assert.That(missingTask.Result, Is.EqualTo("⟦hs.exp.missing⟧"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LegacyCommonParams_ArePresentInUnityParams()
|
||||
{
|
||||
StringTableCollection collection =
|
||||
LocalizationEditorSettings.GetStringTableCollection(ConstRef.ParamsTable);
|
||||
Assert.That(collection, Is.Not.Null);
|
||||
|
||||
string[] legacyKeys = { "天空", "海报", "植物", "朋友", "情绪", "记忆", "逻辑" };
|
||||
foreach (string key in legacyKeys)
|
||||
Assert.That(collection.SharedData.GetEntry(key), Is.Not.Null, key);
|
||||
|
||||
Assert.That(AssetDatabase.LoadAssetAtPath<TextAsset>(
|
||||
"Assets/StreamingAssets/Config/params.csv"), Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExpressionCatalog_IsValidAndCaseSensitive()
|
||||
{
|
||||
ExpressionContentCatalog catalog =
|
||||
AssetDatabase.LoadAssetAtPath<ExpressionContentCatalog>(CatalogPath);
|
||||
Assert.That(catalog, Is.Not.Null);
|
||||
Assert.That(catalog.GetValidationErrors(), Is.Empty);
|
||||
Assert.That(catalog.ActorLoopReference, Is.EqualTo("l10n.hs.exp.actor.loop"));
|
||||
Assert.That(catalog.Rounds.Select(round => round.Id),
|
||||
Is.EqualTo(new[] { "log1", "log2", "log3" }));
|
||||
|
||||
Assert.That(catalog.TryGetRound("log1", out ExpressionRoundDefinition log1), Is.True);
|
||||
Assert.That(log1.NonTargetParticleCount, Is.EqualTo(16));
|
||||
Assert.That(catalog.TryGetRound("LOG1", out _), Is.False);
|
||||
Assert.That(catalog.TryGetRound("missing", out _), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExpressionCatalog_RejectsDuplicateIdsAndNonLocalizedReferences()
|
||||
{
|
||||
ExpressionContentCatalog catalog =
|
||||
ScriptableObject.CreateInstance<ExpressionContentCatalog>();
|
||||
try
|
||||
{
|
||||
SetPrivateField(catalog, "actorLoopReference", "raw");
|
||||
var first = new ExpressionRoundDefinition();
|
||||
var second = new ExpressionRoundDefinition();
|
||||
ConfigureRound(first, "log1", "l10n.target", "l10n.tokens", 1);
|
||||
ConfigureRound(second, "log1", "raw", "l10n.tokens", 1);
|
||||
SetPrivateField(
|
||||
catalog,
|
||||
"rounds",
|
||||
new List<ExpressionRoundDefinition> { first, second });
|
||||
|
||||
string errors = string.Join("\n", catalog.GetValidationErrors());
|
||||
Assert.That(errors, Does.Contain("Actor Loop"));
|
||||
Assert.That(errors, Does.Contain("重复"));
|
||||
Assert.That(errors, Does.Contain("l10n.*"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Object.DestroyImmediate(catalog);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TokenParser_TrimsDropsEmptyAndRejectsFullwidthSeparator()
|
||||
{
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionTokens(
|
||||
" 哈哈 | | 嘿嘿|呵呵 ",
|
||||
out var tokens),
|
||||
Is.True);
|
||||
Assert.That(tokens, Is.EqualTo(new[] { "哈哈", "嘿嘿", "呵呵" }));
|
||||
|
||||
LogAssert.Expect(LogType.Error, new Regex("全角分隔符"));
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionTokens("哈哈|嘿嘿", out _),
|
||||
Is.False);
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionTokens(" | | ", out _),
|
||||
Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnicodeTokenizer_PreservesTextElementsAndDropsWhitespace()
|
||||
{
|
||||
string combining = "e\u0301";
|
||||
var elements =
|
||||
ExpressionTextTokenizer.GetVisibleElements($"中 A あ {combining} 😀。");
|
||||
|
||||
Assert.That(
|
||||
elements,
|
||||
Is.EqualTo(new[] { "中", "A", "あ", combining, "😀", "。" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnicodeLayout_PreservesWordGapWithoutWhitespaceParticle()
|
||||
{
|
||||
ExpressionTextTokenizer.Layout layout =
|
||||
ExpressionTextTokenizer.BuildLayout("A B", 1f, 0.6f);
|
||||
|
||||
Assert.That(layout.VisibleElements, Is.EqualTo(new[] { "A", "B" }));
|
||||
Assert.That(layout.Offsets[0], Is.EqualTo(-0.8f).Within(0.0001f));
|
||||
Assert.That(layout.Offsets[1], Is.EqualTo(0.8f).Within(0.0001f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnicodeSequenceMatch_UsesWholeTextElements()
|
||||
{
|
||||
var source = ExpressionTextTokenizer.GetVisibleElements("A😀e\u0301。");
|
||||
var fragment = ExpressionTextTokenizer.GetVisibleElements("😀e\u0301");
|
||||
Assert.That(ExpressionTextTokenizer.FindVisibleSequence(source, fragment),
|
||||
Is.EqualTo(1));
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.FindVisibleSequence(
|
||||
source,
|
||||
ExpressionTextTokenizer.GetVisibleElements("😀x")),
|
||||
Is.EqualTo(-1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExpressContentValidation_HasNoBlockingErrors()
|
||||
{
|
||||
var issues = ExpressionLocalizationValidator.Validate();
|
||||
string errors = string.Join(
|
||||
"\n",
|
||||
issues
|
||||
.Where(issue =>
|
||||
issue.Severity == ExpressionLocalizationIssueSeverity.Error)
|
||||
.Select(issue => issue.Message));
|
||||
|
||||
Assert.That(errors, Is.Empty);
|
||||
Assert.That(
|
||||
issues.Any(issue =>
|
||||
issue.Severity == ExpressionLocalizationIssueSeverity.Warning),
|
||||
Is.True,
|
||||
"英日空条目应作为待翻译警告保留。");
|
||||
}
|
||||
|
||||
private static void ConfigureRound(
|
||||
ExpressionRoundDefinition round,
|
||||
string id,
|
||||
string target,
|
||||
string tokens,
|
||||
int nonTargetCount)
|
||||
{
|
||||
SetPrivateField(round, "id", id);
|
||||
SetPrivateField(round, "targetReference", target);
|
||||
SetPrivateField(round, "tokenReference", tokens);
|
||||
SetPrivateField(round, "nonTargetParticleCount", nonTargetCount);
|
||||
}
|
||||
|
||||
private static void SetPrivateField(object target, string fieldName, object value)
|
||||
{
|
||||
FieldInfo field = target.GetType().GetField(
|
||||
fieldName,
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
Assert.That(field, Is.Not.Null, fieldName);
|
||||
field.SetValue(target, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6b8797332ac4876b66e381ea3c98723
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,292 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using AibisDream.Framework;
|
||||
using AibisDream.MiniGame.Language;
|
||||
using AibisDream.Utility;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Localization;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Localization;
|
||||
using UnityEngine.Localization.Tables;
|
||||
|
||||
namespace AibisDream.EditorTests.Huoshan
|
||||
{
|
||||
public enum ExpressionLocalizationIssueSeverity
|
||||
{
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
|
||||
public readonly struct ExpressionLocalizationIssue
|
||||
{
|
||||
public ExpressionLocalizationIssue(
|
||||
ExpressionLocalizationIssueSeverity severity,
|
||||
string message)
|
||||
{
|
||||
Severity = severity;
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public ExpressionLocalizationIssueSeverity Severity { get; }
|
||||
public string Message { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 校验 Stage6 Express 命令、轮次 Catalog 与 Params String Table 的引用关系。
|
||||
/// 英日空值是待翻译警告,不阻断内容校验。
|
||||
/// </summary>
|
||||
public static class ExpressionLocalizationValidator
|
||||
{
|
||||
private const string Stage6Path = "Assets/Yarn/FP/FP_Huoshan1/Stage6.yarn";
|
||||
private const string CatalogPath =
|
||||
"Assets/GameContent/Feature_Huoshan/Expression/ExpressionContentCatalog.asset";
|
||||
|
||||
private static readonly Regex YarnCommandRegex =
|
||||
new(@"^\s*<<(?<command>[A-Za-z0-9_]+)(?<arguments>.*?)>>", RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex QuotedArgumentRegex =
|
||||
new("\"(?<value>(?:\\\\.|[^\"])*)\"", RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex L10NRegex =
|
||||
new(@"l10n\.(?<key>[A-Za-z0-9._-]+)", RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex ChineseRegex =
|
||||
new(@"[\u3400-\u9FFF]", RegexOptions.Compiled);
|
||||
|
||||
private static readonly Dictionary<string, int[]> VisibleArgumentIndices =
|
||||
new(StringComparer.Ordinal)
|
||||
{
|
||||
["start_expression"] = new[] { 0, 1 },
|
||||
["expression_lie_begin"] = new[] { 0 },
|
||||
["expression_truth_lie_flicker"] = new[] { 0, 1 },
|
||||
["expression_lie_morph_truth"] = new[] { 0, 1 },
|
||||
["expression_truth_leak"] = new[] { 0 },
|
||||
["expression_lie_shuffle"] = new[] { 0 },
|
||||
["expression_actor_flash"] = new[] { 0 },
|
||||
["expression_actor_flash_in"] = new[] { 0 },
|
||||
["expression_flash_in"] = new[] { 0 },
|
||||
["expression_actor_truth_flash"] = new[] { 0 },
|
||||
["expression_truth_flash"] = new[] { 0 },
|
||||
["expression_actor_word_hit"] = new[] { 0 },
|
||||
["expression_word_hit"] = new[] { 0 },
|
||||
["expression_actor_slot"] = new[] { 0, 1 },
|
||||
["expression_actor_scroll"] = new[] { 0 },
|
||||
["expression_truth_attack"] = new[] { 0 },
|
||||
["expression_truth_destabilize"] = new[] { 0 },
|
||||
["expression_truth_settle"] = new[] { 0 }
|
||||
};
|
||||
|
||||
[MenuItem("Tools/AIBIS/Huoshan/Validate Express Localization")]
|
||||
public static void ValidateFromMenu()
|
||||
{
|
||||
IReadOnlyList<ExpressionLocalizationIssue> issues = Validate();
|
||||
int errorCount = issues.Count(issue =>
|
||||
issue.Severity == ExpressionLocalizationIssueSeverity.Error);
|
||||
int warningCount = issues.Count - errorCount;
|
||||
|
||||
foreach (ExpressionLocalizationIssue issue in issues)
|
||||
{
|
||||
if (issue.Severity == ExpressionLocalizationIssueSeverity.Error)
|
||||
Debug.LogError($"[ExpressL10N] {issue.Message}");
|
||||
else
|
||||
Debug.LogWarning($"[ExpressL10N] {issue.Message}");
|
||||
}
|
||||
|
||||
if (errorCount == 0)
|
||||
{
|
||||
Debug.Log(
|
||||
$"[ExpressL10N] 校验通过。errors=0, pending translations={warningCount}");
|
||||
}
|
||||
}
|
||||
|
||||
public static IReadOnlyList<ExpressionLocalizationIssue> Validate()
|
||||
{
|
||||
var issues = new List<ExpressionLocalizationIssue>();
|
||||
var referencedKeys = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
ValidateStage6(issues, referencedKeys);
|
||||
ValidateCatalog(issues, referencedKeys);
|
||||
ValidateParams(issues, referencedKeys);
|
||||
return issues;
|
||||
}
|
||||
|
||||
private static void ValidateStage6(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
HashSet<string> referencedKeys)
|
||||
{
|
||||
if (!File.Exists(Stage6Path))
|
||||
{
|
||||
AddError(issues, $"找不到 Stage6 Yarn:{Stage6Path}");
|
||||
return;
|
||||
}
|
||||
|
||||
string[] lines = File.ReadAllLines(Stage6Path);
|
||||
for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++)
|
||||
{
|
||||
string line = lines[lineIndex];
|
||||
if (line.TrimStart().StartsWith("//", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
foreach (Match keyMatch in L10NRegex.Matches(line))
|
||||
referencedKeys.Add(keyMatch.Groups["key"].Value);
|
||||
|
||||
Match commandMatch = YarnCommandRegex.Match(line);
|
||||
if (!commandMatch.Success)
|
||||
continue;
|
||||
|
||||
string command = commandMatch.Groups["command"].Value;
|
||||
if (!VisibleArgumentIndices.TryGetValue(command, out int[] visibleIndices))
|
||||
continue;
|
||||
|
||||
string[] arguments = QuotedArgumentRegex
|
||||
.Matches(commandMatch.Groups["arguments"].Value)
|
||||
.Cast<Match>()
|
||||
.Select(match => match.Groups["value"].Value)
|
||||
.ToArray();
|
||||
|
||||
foreach (int argumentIndex in visibleIndices)
|
||||
{
|
||||
if (argumentIndex >= arguments.Length)
|
||||
continue;
|
||||
|
||||
string value = arguments[argumentIndex];
|
||||
if (!LocalizationKit.IsLocalizedParam(value) && ChineseRegex.IsMatch(value))
|
||||
{
|
||||
AddError(
|
||||
issues,
|
||||
$"{Stage6Path}:{lineIndex + 1} 的 {command} 玩家文字参数仍为中文硬编码:{value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateCatalog(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
HashSet<string> referencedKeys)
|
||||
{
|
||||
ExpressionContentCatalog catalog =
|
||||
AssetDatabase.LoadAssetAtPath<ExpressionContentCatalog>(CatalogPath);
|
||||
if (catalog == null)
|
||||
{
|
||||
AddError(issues, $"找不到轮次 Catalog:{CatalogPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string validationError in catalog.GetValidationErrors())
|
||||
AddError(issues, validationError);
|
||||
|
||||
AddCatalogReference(catalog.ActorLoopReference, referencedKeys);
|
||||
foreach (ExpressionRoundDefinition round in catalog.Rounds)
|
||||
{
|
||||
if (round == null)
|
||||
continue;
|
||||
AddCatalogReference(round.TargetReference, referencedKeys);
|
||||
AddCatalogReference(round.TokenReference, referencedKeys);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateParams(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
HashSet<string> referencedKeys)
|
||||
{
|
||||
StringTableCollection collection =
|
||||
LocalizationEditorSettings.GetStringTableCollection(ConstRef.ParamsTable);
|
||||
if (collection == null)
|
||||
{
|
||||
AddError(issues, $"找不到 String Table Collection:{ConstRef.ParamsTable}");
|
||||
return;
|
||||
}
|
||||
|
||||
StringTable chinese =
|
||||
collection.GetTable(new LocaleIdentifier("zh-Hans")) as StringTable;
|
||||
StringTable english =
|
||||
collection.GetTable(new LocaleIdentifier("en")) as StringTable;
|
||||
StringTable japanese =
|
||||
collection.GetTable(new LocaleIdentifier("ja-JP")) as StringTable;
|
||||
|
||||
foreach (string key in referencedKeys.OrderBy(value => value, StringComparer.Ordinal))
|
||||
{
|
||||
if (collection.SharedData.GetEntry(key) == null)
|
||||
{
|
||||
AddError(issues, $"Params Shared Data 缺少 Key:{key}");
|
||||
continue;
|
||||
}
|
||||
|
||||
StringTableEntry chineseEntry = chinese?.GetEntry(key);
|
||||
if (string.IsNullOrWhiteSpace(chineseEntry?.LocalizedValue))
|
||||
{
|
||||
AddError(issues, $"Params 中文值为空:{key}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.EndsWith(".tokens", StringComparison.Ordinal) ||
|
||||
string.Equals(key, "hs.exp.actor.loop", StringComparison.Ordinal))
|
||||
{
|
||||
ValidatePipeList(issues, key, chineseEntry.LocalizedValue);
|
||||
}
|
||||
|
||||
AddPendingTranslation(issues, english, "en", key);
|
||||
AddPendingTranslation(issues, japanese, "ja-JP", key);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidatePipeList(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
string key,
|
||||
string value)
|
||||
{
|
||||
if (value.Contains('|'))
|
||||
{
|
||||
AddError(issues, $"Params/{key} 使用了全角分隔符“|”,必须使用半角“|”。");
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasToken = value
|
||||
.Split('|')
|
||||
.Any(token => !string.IsNullOrWhiteSpace(token));
|
||||
if (!hasToken)
|
||||
AddError(issues, $"Params/{key} 没有有效项。");
|
||||
}
|
||||
|
||||
private static void AddPendingTranslation(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
StringTable table,
|
||||
string localeCode,
|
||||
string key)
|
||||
{
|
||||
StringTableEntry entry = table?.GetEntry(key);
|
||||
if (entry == null)
|
||||
{
|
||||
AddError(issues, $"Params/{key} 缺少 {localeCode} 条目。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(entry.LocalizedValue))
|
||||
{
|
||||
issues.Add(new ExpressionLocalizationIssue(
|
||||
ExpressionLocalizationIssueSeverity.Warning,
|
||||
$"待翻译:Params/{key}, Locale={localeCode}"));
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddCatalogReference(
|
||||
string reference,
|
||||
HashSet<string> referencedKeys)
|
||||
{
|
||||
if (LocalizationKit.IsLocalizedParam(reference))
|
||||
referencedKeys.Add(LocalizationKit.GetL10NParamKey(reference));
|
||||
}
|
||||
|
||||
private static void AddError(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
string message)
|
||||
{
|
||||
issues.Add(new ExpressionLocalizationIssue(
|
||||
ExpressionLocalizationIssueSeverity.Error,
|
||||
message));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c602299e38f4bf4899abfbcbed84d77
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user