Add localization for ShowdownSet parse fail popup

This commit is contained in:
Kurt
2025-08-22 16:28:03 -05:00
parent d8853b6de6
commit f0423610af
15 changed files with 446 additions and 53 deletions

View File

@@ -0,0 +1,8 @@
using System;
namespace PKHeX.Core;
public readonly record struct BattleTemplateParseError(BattleTemplateParseErrorType Type, string Value)
{
public string Humanize(BattleTemplateParseErrorLocalization localization) => Type.Humanize(localization, Value);
}

View File

@@ -0,0 +1,55 @@
using System.Text.Json.Serialization;
namespace PKHeX.Core;
/// <summary>
/// Localized strings for <see cref="BattleTemplateParseErrorType"/> values.
/// Each enum member maps 1:1 to a property for JSON (de)serialization.
/// </summary>
public sealed class BattleTemplateParseErrorLocalization
{
private static readonly BattleTemplateParseErrorLocalizationContext Context = new(LocalizationStorage<BattleTemplateParseErrorLocalization>.Options);
public static readonly LocalizationStorage<BattleTemplateParseErrorLocalization> Cache = new("setparse", Context.BattleTemplateParseErrorLocalization);
public static BattleTemplateParseErrorLocalization Get(string language = GameLanguage.DefaultLanguage) => Cache.Get(language);
public static BattleTemplateParseErrorLocalization Get(LanguageID language) => Cache.Get(language.GetLanguageCode());
// General / structural
public required string LineLength { get; init; } = "Line exceeded the maximum supported length: {0}";
// Token issues
public required string TokenUnknown { get; init; } = "Unrecognized: {0}";
public required string TokenFailParse { get; init; } = "Token could not be parsed: {0}";
// Move issues
public required string MoveCountTooMany { get; init; } = "Too many moves specified: {0}";
public required string MoveSlotAlreadyUsed { get; init; } = "Move slot already used: {0}";
public required string MoveDuplicate { get; init; } = "Duplicate move specified: {0}";
public required string MoveUnrecognized { get; init; } = "Move not recognized: {0}";
// Item
public required string ItemUnrecognized { get; init; } = "Held item not recognized: {0}";
// Ability
public required string AbilityDeclaration { get; init; } = "Ability already declared: {0}";
public required string AbilityUnrecognized { get; init; } = "Ability not recognized: {0}";
public required string AbilityAlreadySpecified { get; init; } = "Ability already specified: {0}";
// Nature
public required string NatureUnrecognized { get; init; } = "Nature not recognized: {0}";
public required string NatureAlreadySpecified { get; init; } = "Nature already specified: {0}";
// Hidden Power
public required string HiddenPowerUnknownType { get; init; } = "Hidden Power type not recognized: {0}";
public required string HiddenPowerIncompatibleIVs { get; init; } = "Hidden Power type incompatible with IVs: {0}";
// EffortValue Nature Amp (Stat modifiers with + / - )
public required string NatureEffortAmpDeclaration { get; init; } = "Nature / effort amp already declared: {0}";
public required string NatureEffortAmpUnknown { get; init; } = "Unknown nature effort amp token: {0}";
public required string NatureEffortAmpAlreadySpecified { get; init; } = "Nature effort amp already specified: {0}";
public required string NatureEffortAmpConflictNature { get; init; } = "Declared effort amp conflicts with previously specified nature.";
public required string NatureAmpNoPlus { get; init; } = "Missing '+' nature amp token.";
public required string NatureAmpNoMinus { get; init; } = "Missing '-' nature amp token.";
}
[JsonSerializable(typeof(BattleTemplateParseErrorLocalization))]
public sealed partial class BattleTemplateParseErrorLocalizationContext : JsonSerializerContext;

View File

@@ -0,0 +1,75 @@
using System;
using static PKHeX.Core.BattleTemplateParseErrorType;
namespace PKHeX.Core;
public enum BattleTemplateParseErrorType : byte
{
None = 0,
LineLength,
TokenUnknown,
TokenFailParse,
MoveCountTooMany,
MoveSlotAlreadyUsed,
MoveDuplicate,
MoveUnrecognized,
ItemUnrecognized,
AbilityDeclaration,
AbilityUnrecognized,
AbilityAlreadySpecified,
NatureUnrecognized,
NatureAlreadySpecified,
HiddenPowerUnknownType,
HiddenPowerIncompatibleIVs,
NatureEffortAmpDeclaration,
NatureEffortAmpUnknown,
NatureEffortAmpAlreadySpecified,
NatureEffortAmpConflictNature,
NatureAmpNoPlus,
NatureAmpNoMinus,
}
public static class BattleTemplateParseErrorExtensions
{
/// <summary>
/// Returns the localized string for the provided <paramref name="type"/>.
/// Falls back to the enum name if no mapping exists.
/// </summary>
public static string Humanize(this BattleTemplateParseErrorType type, BattleTemplateParseErrorLocalization localization, string value)
{
var template = GetTemplate(type, localization);
if (value.Length == 0)
return template;
return string.Format(template, value);
}
private static string GetTemplate(BattleTemplateParseErrorType type, BattleTemplateParseErrorLocalization localization) => type switch
{
None => "",
LineLength => localization.LineLength,
TokenUnknown => localization.TokenUnknown,
TokenFailParse => localization.TokenFailParse,
MoveCountTooMany => localization.MoveCountTooMany,
MoveSlotAlreadyUsed => localization.MoveSlotAlreadyUsed,
MoveDuplicate => localization.MoveDuplicate,
MoveUnrecognized => localization.MoveUnrecognized,
ItemUnrecognized => localization.ItemUnrecognized,
AbilityDeclaration => localization.AbilityDeclaration,
AbilityUnrecognized => localization.AbilityUnrecognized,
AbilityAlreadySpecified => localization.AbilityAlreadySpecified,
NatureUnrecognized => localization.NatureUnrecognized,
NatureAlreadySpecified => localization.NatureAlreadySpecified,
HiddenPowerUnknownType => localization.HiddenPowerUnknownType,
HiddenPowerIncompatibleIVs => localization.HiddenPowerIncompatibleIVs,
NatureEffortAmpDeclaration => localization.NatureEffortAmpDeclaration,
NatureEffortAmpUnknown => localization.NatureEffortAmpUnknown,
NatureEffortAmpAlreadySpecified => localization.NatureEffortAmpAlreadySpecified,
NatureEffortAmpConflictNature => localization.NatureEffortAmpConflictNature,
NatureAmpNoPlus => localization.NatureAmpNoPlus,
NatureAmpNoMinus => localization.NatureAmpNoMinus,
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null),
};
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text;
using static PKHeX.Core.Species;
using static PKHeX.Core.BattleTemplateParseErrorType;
namespace PKHeX.Core;
@@ -47,7 +48,10 @@ public sealed class ShowdownSet : IBattleTemplate
/// <summary>
/// Any lines that failed to be parsed.
/// </summary>
public readonly List<string> InvalidLines = new(0);
public readonly List<BattleTemplateParseError> InvalidLines = new(0);
private void LogError(BattleTemplateParseErrorType type, ReadOnlySpan<char> text = default)
=> InvalidLines.Add(new(type, text.ToString()));
/// <summary>
/// Loads a new <see cref="ShowdownSet"/> from the input string.
@@ -137,7 +141,7 @@ private void ParseLines(SpanLineEnumerator lines, BattleTemplateLocalization loc
first = false;
continue;
}
InvalidLines.Add(line.ToString());
LogError(LineLength, line);
continue;
}
@@ -168,7 +172,7 @@ private void ParseLines(IEnumerable<string> lines, BattleTemplateLocalization lo
first = false;
continue;
}
InvalidLines.Add(line);
LogError(LineLength, line);
continue;
}
@@ -183,64 +187,84 @@ private void ParseLines(IEnumerable<string> lines, BattleTemplateLocalization lo
}
}
private void ParseLine(ReadOnlySpan<char> line, ref int movectr, BattleTemplateLocalization localization)
private void ParseLine(ReadOnlySpan<char> line, ref int countMoves, BattleTemplateLocalization localization)
{
var moves = Moves.AsSpan();
// Check the first char, to see if it is a move input.
var firstChar = line[0];
if (firstChar is '-' or '')
{
if (movectr >= MaxMoveCount)
{
InvalidLines.Add($"Too many moves: {line}");
return;
}
var moveString = ParseLineMove(line, localization.Strings);
int move = StringUtil.FindIndexIgnoreCase(localization.Strings.movelist, moveString);
if (move < 0)
InvalidLines.Add($"Unknown Move: {moveString}");
else if (moves.Contains((ushort)move))
InvalidLines.Add($"Duplicate Move: {moveString}");
else
moves[movectr++] = (ushort)move;
ParseMoveLine(line, ref countMoves, localization);
return;
}
// Check if it is a directional move input.
var dirMove = BattleTemplateConfig.GetMoveDisplay(MoveDisplayStyle.Directional);
var dirMoveIndex = dirMove.IndexOf(firstChar);
if (dirMoveIndex != -1)
{
if (moves[dirMoveIndex] != 0)
{
InvalidLines.Add($"Move slot already specified: {line}");
return;
}
var moveString = ParseLineMove(line, localization.Strings);
int move = StringUtil.FindIndexIgnoreCase(localization.Strings.movelist, moveString);
if (move < 0)
InvalidLines.Add($"Unknown Move: {moveString}");
else if (moves.Contains((ushort)move))
InvalidLines.Add($"Duplicate Move: {moveString}");
else
moves[dirMoveIndex] = (ushort)move;
movectr++;
ParseMoveLineIndex(line, ref countMoves, localization, dirMoveIndex);
return;
}
if (firstChar is '[' or '@') // Ability
// Check if it is an Ability selection/line
if (firstChar is '[' or '@')
{
ParseLineAbilityBracket(line, localization.Strings);
return;
}
// Otherwise, tokenized input line.
var token = localization.Config.TryParse(line, out var value);
if (token == BattleTemplateToken.None)
{
InvalidLines.Add($"Unknown Token: {line}");
LogError(TokenUnknown, line);
return;
}
var valid = ParseEntry(token, value, localization);
if (!valid)
InvalidLines.Add(line.ToString());
LogError(TokenFailParse, line);
}
private void ParseMoveLine(ReadOnlySpan<char> line, ref int countMoves, BattleTemplateLocalization localization)
{
if (countMoves >= MaxMoveCount)
{
LogError(MoveCountTooMany, line);
return;
}
TryAddMoveAtIndex(line, ref countMoves, localization, countMoves);
}
private void ParseMoveLineIndex(ReadOnlySpan<char> line, ref int countMoves, BattleTemplateLocalization localization, int index)
{
if (Moves[index] != 0)
{
LogError(MoveSlotAlreadyUsed, line);
return;
}
TryAddMoveAtIndex(line, ref countMoves, localization, index);
}
private void TryAddMoveAtIndex(ReadOnlySpan<char> line, ref int countMoves, BattleTemplateLocalization localization, int index)
{
var strings = localization.Strings;
var movelist = strings.movelist;
var moveString = ParseLineMove(line, strings);
int move = StringUtil.FindIndexIgnoreCase(movelist, moveString);
var moves = Moves.AsSpan();
if (move < 0)
{
LogError(MoveUnrecognized, moveString);
}
else if (moves.Contains((ushort)move))
{
LogError(MoveDuplicate, moveString);
}
else
{
moves[index] = (ushort)move;
countMoves++;
}
}
private void ParseLineAbilityBracket(ReadOnlySpan<char> line, GameStrings localizationStrings)
@@ -251,7 +275,7 @@ private void ParseLineAbilityBracket(ReadOnlySpan<char> line, GameStrings locali
{
var itemName = line[(itemStart + 1)..].TrimStart();
if (!ParseItemName(itemName, localizationStrings))
InvalidLines.Add($"Unknown Item: {itemName}");
LogError(ItemUnrecognized, itemName);
line = line[..itemStart];
}
@@ -259,7 +283,7 @@ private void ParseLineAbilityBracket(ReadOnlySpan<char> line, GameStrings locali
var abilityEnd = line.IndexOf(']');
if (abilityEnd == -1 || line.Length == 1) // '[' should be present if ']' is; length check.
{
InvalidLines.Add($"Invalid Ability declaration: {line}");
LogError(AbilityDeclaration, line);
return; // invalid line
}
@@ -267,7 +291,7 @@ private void ParseLineAbilityBracket(ReadOnlySpan<char> line, GameStrings locali
var abilityIndex = StringUtil.FindIndexIgnoreCase(localizationStrings.abilitylist, abilityName);
if (abilityIndex < 0)
{
InvalidLines.Add($"Unknown Ability: {abilityName}");
LogError(AbilityUnrecognized, abilityName);
return; // invalid line
}
Ability = abilityIndex;
@@ -296,12 +320,12 @@ private bool ParseLineAbility(ReadOnlySpan<char> value, ReadOnlySpan<string> abi
var index = StringUtil.FindIndexIgnoreCase(abilityNames, value);
if (index < 0)
{
InvalidLines.Add($"Unknown Ability: {value}");
LogError(AbilityUnrecognized, value);
return false;
}
if (Ability != -1 && Ability != index)
{
InvalidLines.Add($"Different ability already specified: {value}");
LogError(AbilityAlreadySpecified, value);
return false;
}
@@ -318,12 +342,12 @@ private bool ParseLineNature(ReadOnlySpan<char> value, ReadOnlySpan<string> natu
var nature = (Nature)index;
if (!nature.IsFixed())
{
InvalidLines.Add($"Invalid Nature: {value}");
LogError(NatureUnrecognized, value);
return false;
}
if (Nature != Nature.Random && Nature != nature)
{
InvalidLines.Add($"Different nature already specified: {value}");
LogError(NatureAlreadySpecified, value);
return false;
}
@@ -816,7 +840,7 @@ private void ParseFirstLine(ReadOnlySpan<char> first, GameStrings strings)
var speciesName = first[..itemSplit].TrimEnd();
if (!ParseItemName(itemName, strings))
InvalidLines.Add($"Unknown Item: {itemName}");
LogError(ItemUnrecognized, itemName);
ParseFirstLineNoItem(speciesName, strings);
}
else
@@ -1004,7 +1028,7 @@ private ReadOnlySpan<char> ParseLineMove(ReadOnlySpan<char> line, GameStrings st
if (IVs.AsSpan().ContainsAnyExcept(maxIV))
{
if (!HiddenPower.SetIVsForType(hpVal, IVs, Context))
InvalidLines.Add($"Invalid IVs for Hidden Power Type: {type}");
LogError(HiddenPowerIncompatibleIVs, type);
}
else if (hpVal >= 0)
{
@@ -1012,7 +1036,7 @@ private ReadOnlySpan<char> ParseLineMove(ReadOnlySpan<char> line, GameStrings st
}
else
{
InvalidLines.Add($"Invalid Hidden Power Type: {type}");
LogError(HiddenPowerUnknownType, type);
}
return hiddenPowerName;
}
@@ -1042,19 +1066,19 @@ private bool ParseLineEVs(ReadOnlySpan<char> line, BattleTemplateLocalization lo
var end = natureName.IndexOf(')');
if (end == -1)
{
InvalidLines.Add($"Invalid EV nature: {natureName}");
LogError(NatureEffortAmpDeclaration, natureName);
return false; // invalid line
}
natureName = natureName[..end].Trim();
var natureIndex = StringUtil.FindIndexIgnoreCase(localization.Strings.natures, natureName);
if (natureIndex == -1)
{
InvalidLines.Add($"Invalid EV nature: {natureName}");
LogError(NatureEffortAmpUnknown, natureName);
return false; // invalid line
}
if (Nature != Nature.Random) // specified in a separate Nature line
InvalidLines.Add($"EV nature ignored, specified previously: {natureName}");
LogError(NatureEffortAmpAlreadySpecified, natureName);
else
Nature = (Nature)natureIndex;
@@ -1074,7 +1098,7 @@ private bool ParseLineEVs(ReadOnlySpan<char> line, BattleTemplateLocalization lo
success &= ampNature;
if (ampNature && currentNature != Nature.Random && currentNature != Nature)
{
InvalidLines.Add($"EV +/- nature does not match specified nature: {currentNature}");
LogError(NatureEffortAmpConflictNature);
Nature = currentNature; // revert to original
}
return success;
@@ -1090,9 +1114,9 @@ private bool ParseLineIVs(ReadOnlySpan<char> line, BattleTemplateConfig config)
private bool AdjustNature(sbyte plus, sbyte minus)
{
if (plus == StatParseResult.NoStatAmp)
InvalidLines.Add("Invalid Nature adjustment, missing plus stat.");
LogError(NatureAmpNoPlus);
if (minus == StatParseResult.NoStatAmp)
InvalidLines.Add("Invalid Nature adjustment, missing minus stat.");
LogError(NatureAmpNoMinus);
else
Nature = NatureAmp.CreateNatureFromAmps(plus, minus);
return true;

View File

@@ -70,6 +70,9 @@ public static bool IsParadox(ushort species) => species is (>= (int)GreatTusk an
or (int)WalkingWake or (int)IronLeaves
or (int)GougingFire or (int)RagingBolt or (int)IronBoulder or (int)IronCrown;
/// <summary>
/// Checks if the <see cref="currentSpecies"/> is a species that evolved from a bi-gendered species into a single-gendered species/form.
/// </summary>
public static bool IsFixedGenderFromDual(ushort currentSpecies) => currentSpecies switch
{
(int)Shedinja => true, // Genderless
@@ -84,7 +87,20 @@ public static bool IsParadox(ushort species) => species is (>= (int)GreatTusk an
(int)Meowstic => true, // (M/F) form specific
(int)Salazzle => true, // (F)
(int)Oinkologne => true, // (M/F) form specific
(int)Basculegion => true, // (M/F) form specific
_ => false,
};
/// <summary>
/// Checks if the <see cref="currentSpecies"/> is a species that has each gender as a different form.
/// </summary>
public static bool IsFormGenderSpecific(ushort currentSpecies) => currentSpecies switch
{
(int)Meowstic => true,
(int)Indeedee => true,
(int)Basculegion => true,
(int)Oinkologne => true,
_ => false,
};
}

View File

@@ -0,0 +1,23 @@
{
"LineLength": "Line exceeded the maximum supported length: {0}",
"TokenUnknown": "Unrecognized: {0}",
"TokenFailParse": "Parse Failure: {0}",
"MoveCountTooMany": "Too many moves specified: {0}",
"MoveSlotAlreadyUsed": "Move slot already used: {0}",
"MoveDuplicate": "Duplicate move specified: {0}",
"MoveUnrecognized": "Move not recognized: {0}",
"ItemUnrecognized": "Held item not recognized: {0}",
"AbilityDeclaration": "Ability already declared: {0}",
"AbilityUnrecognized": "Ability not recognized: {0}",
"AbilityAlreadySpecified": "Ability already specified: {0}",
"NatureUnrecognized": "Nature not recognized: {0}",
"NatureAlreadySpecified": "Nature already specified: {0}",
"HiddenPowerUnknownType": "Hidden Power type not recognized: {0}",
"HiddenPowerIncompatibleIVs": "Hidden Power type incompatible with IVs: {0}",
"NatureEffortAmpDeclaration": "Nature / effort amp already declared: {0}",
"NatureEffortAmpUnknown": "Unknown nature effort amp: {0}",
"NatureEffortAmpAlreadySpecified": "Nature effort amp already specified: {0}",
"NatureEffortAmpConflictNature": "Declared effort amp conflicts with previously specified nature.",
"NatureAmpNoPlus": "Missing '+' nature amp.",
"NatureAmpNoMinus": "Missing '-' nature amp."
}

View File

@@ -0,0 +1,23 @@
{
"LineLength": "Line exceeded the maximum supported length: {0}",
"TokenUnknown": "Unrecognized: {0}",
"TokenFailParse": "Parse Failure: {0}",
"MoveCountTooMany": "Too many moves specified: {0}",
"MoveSlotAlreadyUsed": "Move slot already used: {0}",
"MoveDuplicate": "Duplicate move specified: {0}",
"MoveUnrecognized": "Move not recognized: {0}",
"ItemUnrecognized": "Held item not recognized: {0}",
"AbilityDeclaration": "Ability already declared: {0}",
"AbilityUnrecognized": "Ability not recognized: {0}",
"AbilityAlreadySpecified": "Ability already specified: {0}",
"NatureUnrecognized": "Nature not recognized: {0}",
"NatureAlreadySpecified": "Nature already specified: {0}",
"HiddenPowerUnknownType": "Hidden Power type not recognized: {0}",
"HiddenPowerIncompatibleIVs": "Hidden Power type incompatible with IVs: {0}",
"NatureEffortAmpDeclaration": "Nature / effort amp already declared: {0}",
"NatureEffortAmpUnknown": "Unknown nature effort amp: {0}",
"NatureEffortAmpAlreadySpecified": "Nature effort amp already specified: {0}",
"NatureEffortAmpConflictNature": "Declared effort amp conflicts with previously specified nature.",
"NatureAmpNoPlus": "Missing '+' nature amp.",
"NatureAmpNoMinus": "Missing '-' nature amp."
}

View File

@@ -0,0 +1,23 @@
{
"LineLength": "Line exceeded the maximum supported length: {0}",
"TokenUnknown": "Unrecognized: {0}",
"TokenFailParse": "Parse Failure: {0}",
"MoveCountTooMany": "Too many moves specified: {0}",
"MoveSlotAlreadyUsed": "Move slot already used: {0}",
"MoveDuplicate": "Duplicate move specified: {0}",
"MoveUnrecognized": "Move not recognized: {0}",
"ItemUnrecognized": "Held item not recognized: {0}",
"AbilityDeclaration": "Ability already declared: {0}",
"AbilityUnrecognized": "Ability not recognized: {0}",
"AbilityAlreadySpecified": "Ability already specified: {0}",
"NatureUnrecognized": "Nature not recognized: {0}",
"NatureAlreadySpecified": "Nature already specified: {0}",
"HiddenPowerUnknownType": "Hidden Power type not recognized: {0}",
"HiddenPowerIncompatibleIVs": "Hidden Power type incompatible with IVs: {0}",
"NatureEffortAmpDeclaration": "Nature / effort amp already declared: {0}",
"NatureEffortAmpUnknown": "Unknown nature effort amp: {0}",
"NatureEffortAmpAlreadySpecified": "Nature effort amp already specified: {0}",
"NatureEffortAmpConflictNature": "Declared effort amp conflicts with previously specified nature.",
"NatureAmpNoPlus": "Missing '+' nature amp.",
"NatureAmpNoMinus": "Missing '-' nature amp."
}

View File

@@ -0,0 +1,23 @@
{
"LineLength": "Line exceeded the maximum supported length: {0}",
"TokenUnknown": "Unrecognized: {0}",
"TokenFailParse": "Parse Failure: {0}",
"MoveCountTooMany": "Too many moves specified: {0}",
"MoveSlotAlreadyUsed": "Move slot already used: {0}",
"MoveDuplicate": "Duplicate move specified: {0}",
"MoveUnrecognized": "Move not recognized: {0}",
"ItemUnrecognized": "Held item not recognized: {0}",
"AbilityDeclaration": "Ability already declared: {0}",
"AbilityUnrecognized": "Ability not recognized: {0}",
"AbilityAlreadySpecified": "Ability already specified: {0}",
"NatureUnrecognized": "Nature not recognized: {0}",
"NatureAlreadySpecified": "Nature already specified: {0}",
"HiddenPowerUnknownType": "Hidden Power type not recognized: {0}",
"HiddenPowerIncompatibleIVs": "Hidden Power type incompatible with IVs: {0}",
"NatureEffortAmpDeclaration": "Nature / effort amp already declared: {0}",
"NatureEffortAmpUnknown": "Unknown nature effort amp: {0}",
"NatureEffortAmpAlreadySpecified": "Nature effort amp already specified: {0}",
"NatureEffortAmpConflictNature": "Declared effort amp conflicts with previously specified nature.",
"NatureAmpNoPlus": "Missing '+' nature amp.",
"NatureAmpNoMinus": "Missing '-' nature amp."
}

View File

@@ -0,0 +1,23 @@
{
"LineLength": "Line exceeded the maximum supported length: {0}",
"TokenUnknown": "Unrecognized: {0}",
"TokenFailParse": "Parse Failure: {0}",
"MoveCountTooMany": "Too many moves specified: {0}",
"MoveSlotAlreadyUsed": "Move slot already used: {0}",
"MoveDuplicate": "Duplicate move specified: {0}",
"MoveUnrecognized": "Move not recognized: {0}",
"ItemUnrecognized": "Held item not recognized: {0}",
"AbilityDeclaration": "Ability already declared: {0}",
"AbilityUnrecognized": "Ability not recognized: {0}",
"AbilityAlreadySpecified": "Ability already specified: {0}",
"NatureUnrecognized": "Nature not recognized: {0}",
"NatureAlreadySpecified": "Nature already specified: {0}",
"HiddenPowerUnknownType": "Hidden Power type not recognized: {0}",
"HiddenPowerIncompatibleIVs": "Hidden Power type incompatible with IVs: {0}",
"NatureEffortAmpDeclaration": "Nature / effort amp already declared: {0}",
"NatureEffortAmpUnknown": "Unknown nature effort amp: {0}",
"NatureEffortAmpAlreadySpecified": "Nature effort amp already specified: {0}",
"NatureEffortAmpConflictNature": "Declared effort amp conflicts with previously specified nature.",
"NatureAmpNoPlus": "Missing '+' nature amp.",
"NatureAmpNoMinus": "Missing '-' nature amp."
}

View File

@@ -0,0 +1,23 @@
{
"LineLength": "Line exceeded the maximum supported length: {0}",
"TokenUnknown": "Unrecognized: {0}",
"TokenFailParse": "Parse Failure: {0}",
"MoveCountTooMany": "Too many moves specified: {0}",
"MoveSlotAlreadyUsed": "Move slot already used: {0}",
"MoveDuplicate": "Duplicate move specified: {0}",
"MoveUnrecognized": "Move not recognized: {0}",
"ItemUnrecognized": "Held item not recognized: {0}",
"AbilityDeclaration": "Ability already declared: {0}",
"AbilityUnrecognized": "Ability not recognized: {0}",
"AbilityAlreadySpecified": "Ability already specified: {0}",
"NatureUnrecognized": "Nature not recognized: {0}",
"NatureAlreadySpecified": "Nature already specified: {0}",
"HiddenPowerUnknownType": "Hidden Power type not recognized: {0}",
"HiddenPowerIncompatibleIVs": "Hidden Power type incompatible with IVs: {0}",
"NatureEffortAmpDeclaration": "Nature / effort amp already declared: {0}",
"NatureEffortAmpUnknown": "Unknown nature effort amp: {0}",
"NatureEffortAmpAlreadySpecified": "Nature effort amp already specified: {0}",
"NatureEffortAmpConflictNature": "Declared effort amp conflicts with previously specified nature.",
"NatureAmpNoPlus": "Missing '+' nature amp.",
"NatureAmpNoMinus": "Missing '-' nature amp."
}

View File

@@ -0,0 +1,23 @@
{
"LineLength": "Line exceeded the maximum supported length: {0}",
"TokenUnknown": "Unrecognized: {0}",
"TokenFailParse": "Parse Failure: {0}",
"MoveCountTooMany": "Too many moves specified: {0}",
"MoveSlotAlreadyUsed": "Move slot already used: {0}",
"MoveDuplicate": "Duplicate move specified: {0}",
"MoveUnrecognized": "Move not recognized: {0}",
"ItemUnrecognized": "Held item not recognized: {0}",
"AbilityDeclaration": "Ability already declared: {0}",
"AbilityUnrecognized": "Ability not recognized: {0}",
"AbilityAlreadySpecified": "Ability already specified: {0}",
"NatureUnrecognized": "Nature not recognized: {0}",
"NatureAlreadySpecified": "Nature already specified: {0}",
"HiddenPowerUnknownType": "Hidden Power type not recognized: {0}",
"HiddenPowerIncompatibleIVs": "Hidden Power type incompatible with IVs: {0}",
"NatureEffortAmpDeclaration": "Nature / effort amp already declared: {0}",
"NatureEffortAmpUnknown": "Unknown nature effort amp: {0}",
"NatureEffortAmpAlreadySpecified": "Nature effort amp already specified: {0}",
"NatureEffortAmpConflictNature": "Declared effort amp conflicts with previously specified nature.",
"NatureAmpNoPlus": "Missing '+' nature amp.",
"NatureAmpNoMinus": "Missing '-' nature amp."
}

View File

@@ -0,0 +1,23 @@
{
"LineLength": "Line exceeded the maximum supported length: {0}",
"TokenUnknown": "Unrecognized: {0}",
"TokenFailParse": "Parse Failure: {0}",
"MoveCountTooMany": "Too many moves specified: {0}",
"MoveSlotAlreadyUsed": "Move slot already used: {0}",
"MoveDuplicate": "Duplicate move specified: {0}",
"MoveUnrecognized": "Move not recognized: {0}",
"ItemUnrecognized": "Held item not recognized: {0}",
"AbilityDeclaration": "Ability already declared: {0}",
"AbilityUnrecognized": "Ability not recognized: {0}",
"AbilityAlreadySpecified": "Ability already specified: {0}",
"NatureUnrecognized": "Nature not recognized: {0}",
"NatureAlreadySpecified": "Nature already specified: {0}",
"HiddenPowerUnknownType": "Hidden Power type not recognized: {0}",
"HiddenPowerIncompatibleIVs": "Hidden Power type incompatible with IVs: {0}",
"NatureEffortAmpDeclaration": "Nature / effort amp already declared: {0}",
"NatureEffortAmpUnknown": "Unknown nature effort amp: {0}",
"NatureEffortAmpAlreadySpecified": "Nature effort amp already specified: {0}",
"NatureEffortAmpConflictNature": "Declared effort amp conflicts with previously specified nature.",
"NatureAmpNoPlus": "Missing '+' nature amp.",
"NatureAmpNoMinus": "Missing '-' nature amp."
}

View File

@@ -0,0 +1,23 @@
{
"LineLength": "Line exceeded the maximum supported length: {0}",
"TokenUnknown": "Unrecognized: {0}",
"TokenFailParse": "Parse Failure: {0}",
"MoveCountTooMany": "Too many moves specified: {0}",
"MoveSlotAlreadyUsed": "Move slot already used: {0}",
"MoveDuplicate": "Duplicate move specified: {0}",
"MoveUnrecognized": "Move not recognized: {0}",
"ItemUnrecognized": "Held item not recognized: {0}",
"AbilityDeclaration": "Ability already declared: {0}",
"AbilityUnrecognized": "Ability not recognized: {0}",
"AbilityAlreadySpecified": "Ability already specified: {0}",
"NatureUnrecognized": "Nature not recognized: {0}",
"NatureAlreadySpecified": "Nature already specified: {0}",
"HiddenPowerUnknownType": "Hidden Power type not recognized: {0}",
"HiddenPowerIncompatibleIVs": "Hidden Power type incompatible with IVs: {0}",
"NatureEffortAmpDeclaration": "Nature / effort amp already declared: {0}",
"NatureEffortAmpUnknown": "Unknown nature effort amp: {0}",
"NatureEffortAmpAlreadySpecified": "Nature effort amp already specified: {0}",
"NatureEffortAmpConflictNature": "Declared effort amp conflicts with previously specified nature.",
"NatureAmpNoPlus": "Missing '+' nature amp.",
"NatureAmpNoMinus": "Missing '-' nature amp."
}

View File

@@ -439,8 +439,16 @@ private void ClickShowdownImportPKM(object? sender, EventArgs e)
var invalid = set.InvalidLines;
if (invalid.Count != 0)
WinFormsUtil.Alert(MsgSimulatorInvalid, string.Join(Environment.NewLine, invalid));
{
var localization = BattleTemplateParseErrorLocalization.Get(CurrentLanguage);
var sb = new System.Text.StringBuilder();
foreach (var line in invalid)
{
var error = line.Humanize(localization);
sb.AppendLine(error);
}
WinFormsUtil.Alert(MsgSimulatorInvalid, sb.ToString());
}
PKME_Tabs.LoadShowdownSet(set);
}