feat: 火山表达维修粒子字符池拆分
This commit is contained in:
@@ -22,6 +22,8 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
{
|
||||
private const string CatalogPath =
|
||||
"Assets/GameContent/Feature_Huoshan/Expression/ExpressionContentCatalog.asset";
|
||||
private const string ParticleProfilePath =
|
||||
"Assets/GameContent/Feature_Huoshan/Expression/ExpressionParticleLanguageProfile.asset";
|
||||
|
||||
[TestCase(null, false)]
|
||||
[TestCase("", false)]
|
||||
@@ -208,6 +210,210 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParticleLanguageProfile_ResolvesExactPrefixAndFallbackModes()
|
||||
{
|
||||
ExpressionParticleLanguageProfile profile =
|
||||
AssetDatabase.LoadAssetAtPath<ExpressionParticleLanguageProfile>(
|
||||
ParticleProfilePath);
|
||||
Assert.That(profile, Is.Not.Null);
|
||||
Assert.That(profile.GetValidationErrors(), Is.Empty);
|
||||
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier("zh-Hans")).UnitMode,
|
||||
Is.EqualTo(ExpressionParticleUnitMode.Grapheme));
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier("ja-JP")).UnitMode,
|
||||
Is.EqualTo(ExpressionParticleUnitMode.Grapheme));
|
||||
|
||||
foreach (string localeCode in new[] { "en", "es", "ru", "pt-BR" })
|
||||
{
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier(localeCode)).UnitMode,
|
||||
Is.EqualTo(ExpressionParticleUnitMode.Word),
|
||||
localeCode);
|
||||
}
|
||||
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier("en-US")).LocaleCode,
|
||||
Is.EqualTo("en"));
|
||||
|
||||
LogAssert.Expect(
|
||||
LogType.Warning,
|
||||
new Regex(@"Locale 'zz-ZZ'.*Grapheme"));
|
||||
Assert.That(
|
||||
profile.Resolve(new LocaleIdentifier("zz-ZZ")).UnitMode,
|
||||
Is.EqualTo(ExpressionParticleUnitMode.Grapheme));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WordTokenizer_UsesWhitespaceBoundariesAndKeepsPunctuation()
|
||||
{
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.TokenizeText(
|
||||
"I am not a joke",
|
||||
ExpressionParticleUnitMode.Word),
|
||||
Is.EqualTo(new[] { "I", "am", "not", "a", "joke" }));
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.TokenizeText(
|
||||
"don't self-doubt ¿Por qué? веришь?",
|
||||
ExpressionParticleUnitMode.Word),
|
||||
Is.EqualTo(new[]
|
||||
{
|
||||
"don't",
|
||||
"self-doubt",
|
||||
"¿Por",
|
||||
"qué?",
|
||||
"веришь?"
|
||||
}));
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.TokenizeText(
|
||||
"one \t\r\n two",
|
||||
ExpressionParticleUnitMode.Word),
|
||||
Is.EqualTo(new[] { "one", "two" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GraphemeTokenizer_PreservesCjkKanaAndCombiningCharacters()
|
||||
{
|
||||
const string combining = "e\u0301";
|
||||
Assert.That(
|
||||
ExpressionTextTokenizer.TokenizeText(
|
||||
$"汉 あ {combining}",
|
||||
ExpressionParticleUnitMode.Grapheme),
|
||||
Is.EqualTo(new[] { "汉", "あ", combining }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WordPool_PreservesDuplicateWeightsAndRejectsPipeSeparators()
|
||||
{
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool(
|
||||
"noise doubt noise",
|
||||
ExpressionParticleUnitMode.Word,
|
||||
out List<string> pool),
|
||||
Is.True);
|
||||
Assert.That(pool, Is.EqualTo(new[] { "noise", "doubt", "noise" }));
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool(
|
||||
"noise|doubt",
|
||||
ExpressionParticleUnitMode.Word,
|
||||
out _),
|
||||
Is.False);
|
||||
Assert.That(
|
||||
LanguageYarnCommand.TryParseExpressionPool(
|
||||
"noise|doubt",
|
||||
ExpressionParticleUnitMode.Word,
|
||||
out _),
|
||||
Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RoundSnapshot_IsParsedOnceAndKeepsItsLocaleAndWordUnits()
|
||||
{
|
||||
ExpressionParticleLanguageProfile languageProfile =
|
||||
AssetDatabase.LoadAssetAtPath<ExpressionParticleLanguageProfile>(
|
||||
ParticleProfilePath);
|
||||
ExpressionParticleLocaleSettings english =
|
||||
languageProfile.Resolve(new LocaleIdentifier("en"));
|
||||
Assert.That(
|
||||
ExpressionRoundTextSnapshot.TryCreate(
|
||||
new LocaleIdentifier("en"),
|
||||
english,
|
||||
"I am not a joke",
|
||||
new[] { "false alarm", "false alarm" },
|
||||
"noise doubt noise",
|
||||
out ExpressionRoundTextSnapshot snapshot,
|
||||
out string error),
|
||||
Is.True,
|
||||
error);
|
||||
|
||||
Locale previous = LocalizationSettings.SelectedLocale;
|
||||
try
|
||||
{
|
||||
LocalizationSettings.SelectedLocale =
|
||||
LocalizationEditorSettings.GetLocale(
|
||||
new LocaleIdentifier("zh-Hans"));
|
||||
|
||||
Assert.That(snapshot.Locale.Code, Is.EqualTo("en"));
|
||||
Assert.That(
|
||||
snapshot.TargetUnits,
|
||||
Is.EqualTo(new[] { "I", "am", "not", "a", "joke" }));
|
||||
Assert.That(
|
||||
snapshot.InterferencePoolUnits,
|
||||
Is.EqualTo(new[] { "false", "alarm", "false", "alarm" }));
|
||||
Assert.That(
|
||||
snapshot.DefaultPoolUnits,
|
||||
Is.EqualTo(new[] { "noise", "doubt", "noise" }));
|
||||
}
|
||||
finally
|
||||
{
|
||||
LocalizationSettings.SelectedLocale = previous;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WordProfile_ScalesConfiguredNonTargetCounts()
|
||||
{
|
||||
ExpressionParticleLanguageProfile profile =
|
||||
AssetDatabase.LoadAssetAtPath<ExpressionParticleLanguageProfile>(
|
||||
ParticleProfilePath);
|
||||
ExpressionParticleLocaleSettings english =
|
||||
profile.Resolve(new LocaleIdentifier("en"));
|
||||
|
||||
Assert.That(english.ScaleNonTargetCount(16), Is.EqualTo(10));
|
||||
Assert.That(english.ScaleNonTargetCount(28), Is.EqualTo(18));
|
||||
Assert.That(english.ScaleNonTargetCount(38), Is.EqualTo(25));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ParticleGeometry_UsesVisualEdgeDistanceAndShortestSeparationAxis()
|
||||
{
|
||||
var left = new Bounds(Vector3.zero, new Vector3(4f, 1f, 0.01f));
|
||||
var right = new Bounds(
|
||||
new Vector3(4.15f, 0f, 0f),
|
||||
new Vector3(4f, 1f, 0.01f));
|
||||
|
||||
Assert.That(
|
||||
ExpressionParticleGeometry.BoundsDistance(left, right),
|
||||
Is.EqualTo(0.15f).Within(0.0001f));
|
||||
|
||||
right.center = new Vector3(3.8f, 0f, 0f);
|
||||
Assert.That(
|
||||
ExpressionParticleGeometry.TryGetSeparation(
|
||||
left,
|
||||
right,
|
||||
0.1f,
|
||||
out Vector2 direction,
|
||||
out float overlap),
|
||||
Is.True);
|
||||
Assert.That(direction, Is.EqualTo(Vector2.left));
|
||||
Assert.That(overlap, Is.EqualTo(0.3f).Within(0.0001f));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TextParticle_VisualBoundsIncludesTheWholeWordForPointerDistance()
|
||||
{
|
||||
var gameObject = new GameObject("TextParticleBoundsTest");
|
||||
try
|
||||
{
|
||||
CandidateParticle particle =
|
||||
gameObject.AddComponent<CandidateParticle>();
|
||||
particle.ForceSetUnitText("self-doubt");
|
||||
Bounds bounds = particle.GetVisualWorldBounds();
|
||||
|
||||
Assert.That(bounds.size.x, Is.GreaterThan(0f));
|
||||
Assert.That(
|
||||
particle.DistanceToVisualBounds(
|
||||
new Vector2(bounds.max.x, bounds.center.y)),
|
||||
Is.EqualTo(0f).Within(0.0001f));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Object.DestroyImmediate(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Params_ContainsLocalizedExpressPoolForEveryLocale()
|
||||
{
|
||||
@@ -250,28 +456,28 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
try
|
||||
{
|
||||
CandidateParticle particle = gameObject.AddComponent<CandidateParticle>();
|
||||
Assert.That(particle.currentChar, Is.Empty);
|
||||
Assert.That(particle.CurrentUnitText, Is.Null.Or.Empty);
|
||||
|
||||
string combining = "e\u0301";
|
||||
particle.SetCharacterPools(
|
||||
particle.SetUnitPools(
|
||||
new[] { "😀" },
|
||||
new[] { combining });
|
||||
|
||||
particle.originalIsRed = true;
|
||||
particle.InitializeRandomCharacter();
|
||||
Assert.That(particle.currentChar, Is.EqualTo("😀"));
|
||||
particle.InitializeRandomUnit();
|
||||
Assert.That(particle.CurrentUnitText, Is.EqualTo("😀"));
|
||||
|
||||
particle.originalIsRed = false;
|
||||
particle.InitializeRandomCharacter();
|
||||
Assert.That(particle.currentChar, Is.EqualTo(combining));
|
||||
particle.InitializeRandomUnit();
|
||||
Assert.That(particle.CurrentUnitText, Is.EqualTo(combining));
|
||||
|
||||
particle.SetOverrideCharacterPool(new[] { "。" });
|
||||
particle.InitializeRandomCharacter();
|
||||
Assert.That(particle.currentChar, Is.EqualTo("。"));
|
||||
particle.SetOverrideUnitPool(new[] { "。" });
|
||||
particle.InitializeRandomUnit();
|
||||
Assert.That(particle.CurrentUnitText, Is.EqualTo("。"));
|
||||
|
||||
particle.SetOverrideCharacterPool(null);
|
||||
particle.InitializeRandomCharacter();
|
||||
Assert.That(particle.currentChar, Is.EqualTo(combining));
|
||||
particle.SetOverrideUnitPool((IReadOnlyList<string>)null);
|
||||
particle.InitializeRandomUnit();
|
||||
Assert.That(particle.CurrentUnitText, Is.EqualTo(combining));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -43,6 +43,18 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
private const string Stage6Path = "Assets/Yarn/FP/FP_Huoshan1/Stage6.yarn";
|
||||
private const string CatalogPath =
|
||||
"Assets/GameContent/Feature_Huoshan/Expression/ExpressionContentCatalog.asset";
|
||||
private const string ParticleProfilePath =
|
||||
"Assets/GameContent/Feature_Huoshan/Expression/ExpressionParticleLanguageProfile.asset";
|
||||
|
||||
private static readonly string[] SupportedLocaleCodes =
|
||||
{
|
||||
"zh-Hans",
|
||||
"en",
|
||||
"ja-JP",
|
||||
"es",
|
||||
"ru",
|
||||
"pt-BR"
|
||||
};
|
||||
|
||||
private static readonly Regex YarnCommandRegex =
|
||||
new(@"^\s*<<(?<command>[A-Za-z0-9_]+)(?<arguments>.*?)>>", RegexOptions.Compiled);
|
||||
@@ -110,10 +122,66 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
ValidateCatalog(issues, referencedKeys);
|
||||
referencedKeys.Add(
|
||||
LocalizationKit.GetL10NParamKey(ConstRef.ExpressParticlePoolParam));
|
||||
ValidateParams(issues, referencedKeys);
|
||||
ExpressionParticleLanguageProfile particleProfile =
|
||||
ValidateParticleProfile(issues);
|
||||
ValidateParams(issues, referencedKeys, particleProfile);
|
||||
return issues;
|
||||
}
|
||||
|
||||
private static ExpressionParticleLanguageProfile ValidateParticleProfile(
|
||||
List<ExpressionLocalizationIssue> issues)
|
||||
{
|
||||
ExpressionParticleLanguageProfile profile =
|
||||
AssetDatabase.LoadAssetAtPath<ExpressionParticleLanguageProfile>(
|
||||
ParticleProfilePath);
|
||||
if (profile == null)
|
||||
{
|
||||
AddError(issues, $"找不到粒子语言 Profile:{ParticleProfilePath}");
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (string validationError in profile.GetValidationErrors())
|
||||
AddError(issues, validationError);
|
||||
|
||||
foreach (string localeCode in SupportedLocaleCodes)
|
||||
{
|
||||
bool canResolve = profile.Locales.Any(settings =>
|
||||
{
|
||||
if (settings == null ||
|
||||
string.IsNullOrWhiteSpace(settings.LocaleCode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.Equals(
|
||||
settings.LocaleCode,
|
||||
localeCode,
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string requestedLanguage =
|
||||
localeCode.Split('-')[0];
|
||||
string configuredLanguage =
|
||||
settings.LocaleCode.Split('-')[0];
|
||||
return string.Equals(
|
||||
requestedLanguage,
|
||||
configuredLanguage,
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
});
|
||||
|
||||
if (!canResolve)
|
||||
{
|
||||
AddError(
|
||||
issues,
|
||||
$"粒子语言 Profile 无法解析支持的 Locale:{localeCode}");
|
||||
}
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
|
||||
private static void ValidateStage6(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
HashSet<string> referencedKeys)
|
||||
@@ -190,7 +258,8 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
|
||||
private static void ValidateParams(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
HashSet<string> referencedKeys)
|
||||
HashSet<string> referencedKeys,
|
||||
ExpressionParticleLanguageProfile particleProfile)
|
||||
{
|
||||
StringTableCollection collection =
|
||||
LocalizationEditorSettings.GetStringTableCollection(ConstRef.ParamsTable);
|
||||
@@ -200,18 +269,10 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
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;
|
||||
StringTable spanish =
|
||||
collection.GetTable(new LocaleIdentifier("es")) as StringTable;
|
||||
StringTable russian =
|
||||
collection.GetTable(new LocaleIdentifier("ru")) as StringTable;
|
||||
StringTable portuguese =
|
||||
collection.GetTable(new LocaleIdentifier("pt-BR")) as StringTable;
|
||||
var tables = SupportedLocaleCodes.ToDictionary(
|
||||
code => code,
|
||||
code => collection.GetTable(new LocaleIdentifier(code)) as StringTable,
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
string poolKey =
|
||||
LocalizationKit.GetL10NParamKey(ConstRef.ExpressParticlePoolParam);
|
||||
|
||||
@@ -223,128 +284,164 @@ namespace AibisDream.EditorTests.Huoshan
|
||||
continue;
|
||||
}
|
||||
|
||||
StringTableEntry chineseEntry = chinese?.GetEntry(key);
|
||||
StringTableEntry chineseEntry = tables["zh-Hans"]?.GetEntry(key);
|
||||
if (string.IsNullOrWhiteSpace(chineseEntry?.LocalizedValue))
|
||||
{
|
||||
AddError(issues, $"Params 中文值为空:{key}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key.EndsWith(".tokens", StringComparison.Ordinal))
|
||||
foreach (string localeCode in SupportedLocaleCodes)
|
||||
{
|
||||
ValidatePipeList(issues, key, chineseEntry.LocalizedValue);
|
||||
}
|
||||
StringTable table = tables[localeCode];
|
||||
StringTableEntry entry = table?.GetEntry(key);
|
||||
if (entry == null)
|
||||
{
|
||||
if (string.Equals(
|
||||
localeCode,
|
||||
"zh-Hans",
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
AddError(
|
||||
issues,
|
||||
$"Params/{key} 缺少 {localeCode} 条目。");
|
||||
}
|
||||
else
|
||||
{
|
||||
issues.Add(new ExpressionLocalizationIssue(
|
||||
ExpressionLocalizationIssueSeverity.Warning,
|
||||
$"待翻译:Params/{key}, Locale={localeCode}(缺少条目)"));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string.Equals(key, poolKey, StringComparison.Ordinal))
|
||||
{
|
||||
ValidateCharacterPool(
|
||||
issues,
|
||||
key,
|
||||
"zh-Hans",
|
||||
chineseEntry.LocalizedValue);
|
||||
AddPendingPoolTranslation(issues, english, "en", key);
|
||||
AddPendingPoolTranslation(issues, japanese, "ja-JP", key);
|
||||
AddPendingPoolTranslation(issues, spanish, "es", key);
|
||||
AddPendingPoolTranslation(issues, russian, "ru", key);
|
||||
AddPendingPoolTranslation(issues, portuguese, "pt-BR", key);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddPendingTranslation(issues, english, "en", key);
|
||||
AddPendingTranslation(issues, japanese, "ja-JP", key);
|
||||
if (string.IsNullOrWhiteSpace(entry.LocalizedValue))
|
||||
{
|
||||
if (string.Equals(localeCode, "zh-Hans", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
issues.Add(new ExpressionLocalizationIssue(
|
||||
ExpressionLocalizationIssueSeverity.Warning,
|
||||
$"待翻译:Params/{key}, Locale={localeCode}"));
|
||||
continue;
|
||||
}
|
||||
|
||||
ExpressionParticleLocaleSettings settings =
|
||||
particleProfile != null
|
||||
? particleProfile.Resolve(new LocaleIdentifier(localeCode))
|
||||
: ExpressionParticleLocaleSettings.CreateFallback();
|
||||
|
||||
if (string.Equals(key, poolKey, StringComparison.Ordinal))
|
||||
{
|
||||
ValidateParticlePool(
|
||||
issues,
|
||||
key,
|
||||
localeCode,
|
||||
entry.LocalizedValue,
|
||||
settings.UnitMode);
|
||||
}
|
||||
else if (key.EndsWith(".tokens", StringComparison.Ordinal))
|
||||
{
|
||||
ValidatePipeList(
|
||||
issues,
|
||||
key,
|
||||
localeCode,
|
||||
entry.LocalizedValue,
|
||||
settings.UnitMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
ValidateVisibleText(
|
||||
issues,
|
||||
key,
|
||||
localeCode,
|
||||
entry.LocalizedValue,
|
||||
settings.UnitMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateCharacterPool(
|
||||
private static void ValidateVisibleText(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
string key,
|
||||
string localeCode,
|
||||
string value)
|
||||
string value,
|
||||
ExpressionParticleUnitMode mode)
|
||||
{
|
||||
if (ExpressionTextTokenizer.TokenizeText(value, mode).Count == 0)
|
||||
{
|
||||
AddError(
|
||||
issues,
|
||||
$"Params/{key}, Locale={localeCode} 没有有效的粒子单位。");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateParticlePool(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
string key,
|
||||
string localeCode,
|
||||
string value,
|
||||
ExpressionParticleUnitMode mode)
|
||||
{
|
||||
if (value.Contains('|') || value.Contains('|'))
|
||||
{
|
||||
AddError(
|
||||
issues,
|
||||
$"Params/{key}, Locale={localeCode} 的字符池不能包含“|”或“|”。");
|
||||
$"Params/{key}, Locale={localeCode} 的粒子池不能包含“|”或“|”。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LanguageYarnCommand.TryParseExpressionPool(value, out _))
|
||||
if (!LanguageYarnCommand.TryParseExpressionPool(value, mode, out _))
|
||||
{
|
||||
string expected =
|
||||
mode == ExpressionParticleUnitMode.Word
|
||||
? "空白分隔单词"
|
||||
: "Unicode 文本元素";
|
||||
AddError(
|
||||
issues,
|
||||
$"Params/{key}, Locale={localeCode} 没有有效的 Unicode 文本元素。");
|
||||
$"Params/{key}, Locale={localeCode} 没有有效的{expected}。");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidatePipeList(
|
||||
List<ExpressionLocalizationIssue> issues,
|
||||
string key,
|
||||
string value)
|
||||
string localeCode,
|
||||
string value,
|
||||
ExpressionParticleUnitMode mode)
|
||||
{
|
||||
if (value.Contains('|'))
|
||||
{
|
||||
AddError(issues, $"Params/{key} 使用了全角分隔符“|”,必须使用半角“|”。");
|
||||
AddError(
|
||||
issues,
|
||||
$"Params/{key}, Locale={localeCode} 使用了全角分隔符“|”,必须使用半角“|”。");
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasToken = value
|
||||
string[] phrases = 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)
|
||||
.Where(token => !string.IsNullOrWhiteSpace(token))
|
||||
.ToArray();
|
||||
if (phrases.Length == 0)
|
||||
{
|
||||
AddError(issues, $"Params/{key} 缺少 {localeCode} 条目。");
|
||||
AddError(
|
||||
issues,
|
||||
$"Params/{key}, Locale={localeCode} 没有有效项。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(entry.LocalizedValue))
|
||||
foreach (string phrase in phrases)
|
||||
{
|
||||
issues.Add(new ExpressionLocalizationIssue(
|
||||
ExpressionLocalizationIssueSeverity.Warning,
|
||||
$"待翻译:Params/{key}, Locale={localeCode}"));
|
||||
if (ExpressionTextTokenizer.TokenizeText(phrase, mode).Count == 0)
|
||||
{
|
||||
AddError(
|
||||
issues,
|
||||
$"Params/{key}, Locale={localeCode} 包含没有有效粒子单位的短句。");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddPendingPoolTranslation(
|
||||
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}"));
|
||||
return;
|
||||
}
|
||||
|
||||
ValidateCharacterPool(
|
||||
issues,
|
||||
key,
|
||||
localeCode,
|
||||
entry.LocalizedValue);
|
||||
}
|
||||
|
||||
private static void AddCatalogReference(
|
||||
string reference,
|
||||
HashSet<string> referencedKeys)
|
||||
|
||||
Reference in New Issue
Block a user