Fix translated forme names

Official pokemon names look like:

"Galarian Darmanitan (Zen Mode)"

Which is hard to fit into tables and also don't work well because we
already put them in parentheses in battle text.

"Go! Darmy (Galarian Darmanitan (Zen Mode))!"

So we no longer use parentheses in other languages, and also do a
straightforward find/replace to make the names short. These aren't
official but official names are insane and I don't think anyone really
uses them.
This commit is contained in:
Guangcong Luo
2026-08-26 22:47:48 -07:00
parent 7415c5ac38
commit bb40d4cdbd
35 changed files with 46678 additions and 11516 deletions

View File

@@ -23,6 +23,9 @@ export const TermNames: { [id: string]: TranslationString } = {
ability: "Fähigkeit", // NEEDS QC
abilities: "Fähigkeiten", // NEEDS QC
hiddenability: "Versteckte Fähigkeit", // NEEDS QC
team: "Team",
teams: "Teams",
teamslist: "Teamliste", // NEEDS QC
type: "Typ",
types: "Typen",
@@ -34,11 +37,16 @@ export const TermNames: { [id: string]: TranslationString } = {
egggroup: "Ei-Gruppe", // NEEDS QC
egggroups: "Ei-Gruppen", // NEEDS QC
tag: "Etikett", // NEEDS QC
article: "Artikel", // NEEDS QC
articles: "Artikel", // NEEDS QC
tier: "Tier",
tiers: "Tiers",
color: "Farbe",
form: "Form", // NEEDS QC
forme: "Form", // NEEDS QC
dexnum: "Pokédex-Nr.", // NEEDS QC
gen: "Generation", // NEEDS QC
generation: "Generation",
gennum: "Gen [NUMBER]",
evolution: "Entwicklung", // NEEDS QC
preevolution: "Vorentwicklung", // NEEDS QC
doesnotevolve: "Entwickelt sich nicht", // NEEDS QC
@@ -64,6 +72,9 @@ export const TermNames: { [id: string]: TranslationString } = {
weak: "Schwäche", // NEEDS QC: unofficial
resist: "Resistenz", // NEEDS QC: unofficial
immune: "Immunität", // NEEDS QC: unofficial
nicknamespecies: "[NICKNAME] ([SPECIES])",
speciesforme: "[FORME]-[SPECIES]", // NEEDS QC
};
export const StatNames: { [id: string]: TranslationString } = {

File diff suppressed because it is too large Load Diff

5157
data/text/de/pokedex.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,9 @@ export const TermNames: { [id: string]: TranslationString } = {
ability: "Habilidad", // NEEDS QC
abilities: "Habilidades", // NEEDS QC
hiddenability: "Habilidad oculta", // NEEDS QC
team: "Equipo", // NEEDS QC
teams: "Equipos", // NEEDS QC
teamslist: "Lista de equipos", // NEEDS QC
type: "Tipo",
types: "Tipos",
@@ -34,11 +37,16 @@ export const TermNames: { [id: string]: TranslationString } = {
egggroup: "Grupo Huevo", // NEEDS QC
egggroups: "Grupos Huevo", // NEEDS QC
tag: "Etiqueta", // NEEDS QC
article: "Artículo", // NEEDS QC
articles: "Artículos", // NEEDS QC
tier: "Tier",
tiers: "Tiers",
color: "Color",
form: "Forma", // NEEDS QC
forme: "Forma", // NEEDS QC
dexnum: "N.º Pokédex", // NEEDS QC
gen: "Generación", // NEEDS QC
generation: "Generación", // NEEDS QC
gennum: "Gen [NUMBER]",
evolution: "Evolución", // NEEDS QC
preevolution: "Preevolución", // NEEDS QC
doesnotevolve: "No evoluciona", // NEEDS QC
@@ -64,6 +72,9 @@ export const TermNames: { [id: string]: TranslationString } = {
weak: "Debilidad", // NEEDS QC: unofficial
resist: "Resistencia", // NEEDS QC: unofficial
immune: "Inmunidad", // NEEDS QC: unofficial
nicknamespecies: "[NICKNAME] ([SPECIES])",
speciesforme: "[SPECIES] [FORME]", // NEEDS QC
};
export const StatNames: { [id: string]: TranslationString } = {

File diff suppressed because it is too large Load Diff

5076
data/text/es/pokedex.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,9 @@ export const TermNames: { [id: string]: TranslationString } = {
ability: "Talent", // NEEDS QC
abilities: "Talents", // NEEDS QC
hiddenability: "Talent caché", // NEEDS QC
team: "Équipe", // NEEDS QC
teams: "Équipes", // NEEDS QC
teamslist: "Liste des équipes", // NEEDS QC
type: "Type",
types: "Types",
@@ -34,11 +37,16 @@ export const TermNames: { [id: string]: TranslationString } = {
egggroup: "Groupe dŒuf", // NEEDS QC
egggroups: "Groupes dŒufs", // NEEDS QC
tag: "Étiquette", // NEEDS QC
article: "Article",
articles: "Articles",
tier: "Tier",
tiers: "Tiers",
color: "Couleur",
form: "Forme", // NEEDS QC
forme: "Forme", // NEEDS QC
dexnum: "N° Pokédex", // NEEDS QC
gen: "Génération", // NEEDS QC
generation: "Génération", // NEEDS QC
gennum: "Gen [NUMBER]",
evolution: "Évolution", // NEEDS QC
preevolution: "Pré-évolution", // NEEDS QC
doesnotevolve: "N'évolue pas", // NEEDS QC
@@ -64,6 +72,9 @@ export const TermNames: { [id: string]: TranslationString } = {
weak: "Faiblesse", // NEEDS QC: unofficial
resist: "Résistance", // NEEDS QC: unofficial
immune: "Immunité", // NEEDS QC: unofficial
nicknamespecies: "[NICKNAME] ([SPECIES])",
speciesforme: "[SPECIES] [FORME]", // NEEDS QC
};
export const StatNames: { [id: string]: TranslationString } = {

File diff suppressed because it is too large Load Diff

5157
data/text/fr/pokedex.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,9 @@ export const TermNames: { [id: string]: TranslationString } = {
ability: "Abilità", // NEEDS QC
abilities: "Abilità", // NEEDS QC
hiddenability: "Abilità speciale", // NEEDS QC
team: "Squadra", // NEEDS QC
teams: "Squadre", // NEEDS QC
teamslist: "Elenco squadre", // NEEDS QC
type: "Tipo",
types: "Tipi",
@@ -34,11 +37,16 @@ export const TermNames: { [id: string]: TranslationString } = {
egggroup: "Gruppo Uova", // NEEDS QC
egggroups: "Gruppi Uova", // NEEDS QC
tag: "Etichetta", // NEEDS QC
article: "Articolo", // NEEDS QC
articles: "Articoli", // NEEDS QC
tier: "Tier",
tiers: "Tier", // NEEDS QC
color: "Colore",
form: "Forma", // NEEDS QC
forme: "Forma", // NEEDS QC
dexnum: "N. Pokédex", // NEEDS QC
gen: "Generazione", // NEEDS QC
generation: "Generazione", // NEEDS QC
gennum: "Gen [NUMBER]",
evolution: "Evoluzione", // NEEDS QC
preevolution: "Preevoluzione", // NEEDS QC
doesnotevolve: "Non si evolve", // NEEDS QC
@@ -64,6 +72,9 @@ export const TermNames: { [id: string]: TranslationString } = {
weak: "Debolezza", // NEEDS QC: unofficial
resist: "Resistenza", // NEEDS QC: unofficial
immune: "Immunità", // NEEDS QC: unofficial
nicknamespecies: "[NICKNAME] ([SPECIES])",
speciesforme: "[SPECIES] [FORME]", // NEEDS QC
};
export const StatNames: { [id: string]: TranslationString } = {

File diff suppressed because it is too large Load Diff

5079
data/text/it/pokedex.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,9 @@ export const TermNames: { [id: string]: TranslationString } = {
ability: "特性", // NEEDS QC
abilities: "特性", // NEEDS QC
hiddenability: "隠れ特性", // NEEDS QC
team: "チーム", // NEEDS QC
teams: "チーム", // NEEDS QC
teamslist: "チーム一覧", // NEEDS QC
type: "タイプ",
types: "タイプ",
@@ -34,11 +37,16 @@ export const TermNames: { [id: string]: TranslationString } = {
egggroup: "タマゴグループ", // NEEDS QC
egggroups: "タマゴグループ", // NEEDS QC
tag: "タグ", // NEEDS QC
article: "記事", // NEEDS QC
articles: "記事", // NEEDS QC
tier: "ティア", // NEEDS QC
tiers: "ティア", // NEEDS QC
color: "色",
form: "すがた", // NEEDS QC
forme: "フォルム", // NEEDS QC
dexnum: "図鑑No.", // NEEDS QC
gen: "世代", // NEEDS QC
generation: "世代", // NEEDS QC
gennum: "[NUMBER]世代", // NEEDS QC
evolution: "進化", // NEEDS QC
preevolution: "進化前", // NEEDS QC
doesnotevolve: "進化しない", // NEEDS QC
@@ -64,6 +72,9 @@ export const TermNames: { [id: string]: TranslationString } = {
weak: "弱点", // NEEDS QC: unofficial
resist: "半減", // NEEDS QC: unofficial
immune: "無効", // NEEDS QC: unofficial
nicknamespecies: "[NICKNAME][SPECIES]", // NEEDS QC
speciesforme: "[SPECIES]・[FORME]", // NEEDS QC
};
export const StatNames: { [id: string]: TranslationString } = {

File diff suppressed because it is too large Load Diff

5181
data/text/ja/pokedex.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,9 @@ export const TermNames: { [id: string]: TranslationString } = {
ability: "특성", // NEEDS QC
abilities: "특성", // NEEDS QC
hiddenability: "숨겨진 특성", // NEEDS QC
team: "팀", // NEEDS QC
teams: "팀", // NEEDS QC
teamslist: "팀 목록", // NEEDS QC
type: "타입",
types: "타입",
@@ -34,11 +37,16 @@ export const TermNames: { [id: string]: TranslationString } = {
egggroup: "알그룹", // NEEDS QC
egggroups: "알그룹", // NEEDS QC
tag: "태그", // NEEDS QC
article: "기사", // NEEDS QC
articles: "기사", // NEEDS QC
tier: "티어", // NEEDS QC
tiers: "티어", // NEEDS QC
color: "색",
form: "모습", // NEEDS QC
forme: "폼", // NEEDS QC
dexnum: "도감 No.", // NEEDS QC
gen: "세대", // NEEDS QC
generation: "세대", // NEEDS QC
gennum: "[NUMBER]세대", // NEEDS QC
evolution: "진화", // NEEDS QC
preevolution: "진화 전", // NEEDS QC
doesnotevolve: "진화하지 않음", // NEEDS QC
@@ -64,6 +72,9 @@ export const TermNames: { [id: string]: TranslationString } = {
weak: "약점", // NEEDS QC: unofficial
resist: "반감", // NEEDS QC: unofficial
immune: "무효", // NEEDS QC: unofficial
nicknamespecies: "[NICKNAME]([SPECIES])", // NEEDS QC
speciesforme: "[SPECIES]-[FORME]",
};
export const StatNames: { [id: string]: TranslationString } = {

File diff suppressed because it is too large Load Diff

5181
data/text/ko/pokedex.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,9 @@ export const TermNames: { [id: string]: TranslationString } = {
ability: "Ability",
abilities: "Abilities",
hiddenability: "Hidden Ability",
team: "Team",
teams: "Teams",
teamslist: "Teams List",
type: "Type",
types: "Types",
@@ -37,11 +40,16 @@ export const TermNames: { [id: string]: TranslationString } = {
egggroup: "Egg Group",
egggroups: "Egg Groups",
tag: "Tag",
article: "Article",
articles: "Articles",
tier: "Tier",
tiers: "Tiers",
color: "Color",
form: "Form",
forme: "Forme",
dexnum: "Dex#",
gen: "Gen",
generation: "Generation",
gennum: "Gen [NUMBER]",
evolution: "Evolution",
preevolution: "Pre-Evolution",
doesnotevolve: "Does Not Evolve",
@@ -67,6 +75,11 @@ export const TermNames: { [id: string]: TranslationString } = {
weak: "Weak",
resist: "Resist",
immune: "Immune",
// used in switch-in messages in battles
nicknamespecies: "[NICKNAME] ([SPECIES])",
// this one's not used in code; just a reference for how pokedex.ts should be filled
speciesforme: "[SPECIES]-[FORME]",
};
export const StatNames: { [id: string]: TranslationString } = {

File diff suppressed because it is too large Load Diff

5218
data/text/pokedex.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,9 @@ export const TermNames: { [id: string]: TranslationString } = {
ability: "特性", // NEEDS QC
abilities: "特性", // NEEDS QC
hiddenability: "隐藏特性", // NEEDS QC
team: "队伍", // NEEDS QC
teams: "队伍", // NEEDS QC
teamslist: "队伍列表", // NEEDS QC
type: "属性",
types: "属性",
@@ -34,11 +37,16 @@ export const TermNames: { [id: string]: TranslationString } = {
egggroup: "蛋组", // NEEDS QC
egggroups: "蛋组", // NEEDS QC
tag: "标签", // NEEDS QC
article: "文章", // NEEDS QC
articles: "文章", // NEEDS QC
tier: "级别", // NEEDS QC
tiers: "分级", // NEEDS QC
color: "颜色",
form: "样子", // NEEDS QC
forme: "形态", // NEEDS QC
dexnum: "图鉴No.", // NEEDS QC
gen: "世代", // NEEDS QC
generation: "世代", // NEEDS QC
gennum: "[NUMBER]代", // NEEDS QC
evolution: "进化", // NEEDS QC
preevolution: "进化前", // NEEDS QC
doesnotevolve: "不进化", // NEEDS QC
@@ -64,6 +72,9 @@ export const TermNames: { [id: string]: TranslationString } = {
weak: "弱点", // NEEDS QC: unofficial
resist: "抵抗", // NEEDS QC: unofficial
immune: "免疫", // NEEDS QC: unofficial
nicknamespecies: "[NICKNAME][SPECIES]", // NEEDS QC
speciesforme: "[SPECIES]-[FORME]",
};
export const StatNames: { [id: string]: TranslationString } = {

File diff suppressed because it is too large Load Diff

5181
data/text/zh-cn/pokedex.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,9 @@ export const TermNames: { [id: string]: TranslationString } = {
ability: "特性", // NEEDS QC
abilities: "特性", // NEEDS QC
hiddenability: "隱藏特性", // NEEDS QC
team: "隊伍", // NEEDS QC
teams: "隊伍", // NEEDS QC
teamslist: "隊伍列表", // NEEDS QC
type: "屬性",
types: "屬性",
@@ -34,11 +37,16 @@ export const TermNames: { [id: string]: TranslationString } = {
egggroup: "蛋群", // NEEDS QC
egggroups: "蛋群", // NEEDS QC
tag: "標籤", // NEEDS QC
article: "文章", // NEEDS QC
articles: "文章", // NEEDS QC
tier: "級別", // NEEDS QC
tiers: "分級", // NEEDS QC
color: "顏色",
form: "樣子", // NEEDS QC
forme: "形態", // NEEDS QC
dexnum: "圖鑑No.", // NEEDS QC
gen: "世代", // NEEDS QC
generation: "世代", // NEEDS QC
gennum: "[NUMBER]代", // NEEDS QC
evolution: "進化", // NEEDS QC
preevolution: "進化前", // NEEDS QC
doesnotevolve: "不進化", // NEEDS QC
@@ -64,6 +72,9 @@ export const TermNames: { [id: string]: TranslationString } = {
weak: "弱點", // NEEDS QC: unofficial
resist: "抵抗", // NEEDS QC: unofficial
immune: "免疫", // NEEDS QC: unofficial
nicknamespecies: "[NICKNAME][SPECIES]", // NEEDS QC
speciesforme: "[SPECIES]-[FORME]",
};
export const StatNames: { [id: string]: TranslationString } = {

File diff suppressed because it is too large Load Diff

5181
data/text/zh-tw/pokedex.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -664,7 +664,7 @@ export const commands: Chat.ChatCommands = {
}
details = {
[TL.term.dexnum]: String(pokemon.num),
[TL.term.gen]: String(pokemon.gen) || 'CAP',
[TL.term.generation]: String(pokemon.gen) || 'CAP',
[TL.term.height]: TL.term.numm.replace('[NUMBER]', `${pokemon.heightm}`),
};
details[TL.term.weight] = `${TL.term.numkg.replace('[NUMBER]', `${pokemon.weighthg / 10}`)} <em>(${weighthit} BP)</em>`;
@@ -726,7 +726,7 @@ export const commands: Chat.ChatCommands = {
if (showDetails) {
description = dex.text.get(item, textLanguage).desc;
details = {
[TL.term.gen]: String(item.gen),
[TL.term.generation]: String(item.gen),
};
if (dex.gen >= 4) {
@@ -764,7 +764,7 @@ export const commands: Chat.ChatCommands = {
description = dex.text.get(move, textLanguage).desc;
details = {
[TL.tag.priority]: String(move.priority),
[TL.term.gen]: String(move.gen) || 'CAP',
[TL.term.generation]: String(move.gen) || 'CAP',
};
const pastGensOnly = (move.isNonstandard === "Past" && dex.gen >= 8);
@@ -856,7 +856,7 @@ export const commands: Chat.ChatCommands = {
if (showDetails) {
description = dex.text.get(ability, textLanguage).desc;
details = {
[TL.term.gen]: String(ability.gen) || 'CAP',
[TL.term.generation]: String(ability.gen) || 'CAP',
};
if (ability.flags['cantsuppress']) details["&#10003; Not affected by Gastro Acid"] = "";
if (ability.flags['breakable']) details["&#10003; Ignored by Mold Breaker"] = "";

View File

@@ -318,6 +318,9 @@ export type Translator = {
color: { [id: string]: string },
status: { [id: string]: string },
target: { [id: string]: string },
stat: { [id: string]: string },
statShort: { [id: string]: string },
statMedium: { [id: string]: string },
};
// These classes need to be declared here because they aren't hoisted
@@ -1829,6 +1832,9 @@ export const Chat = new class {
color: text.ColorNames,
status: text.StatusNames,
target: text.TargetNames,
stat: text.StatNames,
statShort: text.StatShortNames,
statMedium: text.StatMediumNames,
}
);
this.translators.set(lang, translator);

View File

@@ -41,7 +41,8 @@ export function assignMissingFields(self: AnyObject, data: AnyObject) {
}
}
export type EffectText = ResolvedAbilityText | ResolvedItemText | ResolvedMoveText | ResolvedNameText;
export type EffectText =
ResolvedAbilityText | ResolvedItemText | ResolvedMoveText | ResolvedNameText | ResolvedSpeciesText;
export type TextLanguage = 'en' | 'en-afd' | 'de' | 'es' | 'fr' | 'it' | 'ja' | 'ko' | 'zh-cn' | 'zh-tw';
export const OTHER_NAME_TABLES = [
@@ -71,7 +72,8 @@ export class DexText {
get(effect: Item, lang?: TextLanguage): ResolvedItemText;
get(effect: Ability, lang?: TextLanguage): ResolvedAbilityText;
get(effect: Move, lang?: TextLanguage): ResolvedMoveText;
get(effect: Species | Nature | TypeInfo | TagData, lang?: TextLanguage): ResolvedNameText;
get(effect: Species, lang?: TextLanguage): ResolvedSpeciesText;
get(effect: Nature | TypeInfo | TagData, lang?: TextLanguage): ResolvedNameText;
get(effect: TextEffect, lang?: TextLanguage): EffectText;
get(
effect: TextEffect, lang: TextLanguage = 'en'
@@ -82,8 +84,12 @@ export class DexText {
let table: EffectTextTable;
switch (effect.effectType) {
case 'Pokemon': {
const name = this.dex.loadTextData(lang).PokedexNames[effect.id] || effect.name;
return { name };
const species = effect;
return this.dex.loadTextData(lang).Pokedex[effect.id] || {
name: species.name,
baseSpecies: species.baseSpecies,
...(species.forme ? { forme: species.forme } : {}),
};
}
case 'Nature': return { name: this.otherName('NatureNames', effect.name, lang) };
case 'Type': case 'EffectType': return { name: this.otherName('TypeNames', effect.name, lang) };

View File

@@ -89,14 +89,14 @@ interface RawTextTableData extends Record<OtherNameTable, DexTable<TranslationSt
Abilities: DexTable<AbilityText>;
Items: DexTable<ItemText>;
Moves: DexTable<MoveText>;
PokedexNames: DexTable<TranslationString>;
Pokedex: DexTable<SpeciesText>;
Default: DexTable<DefaultText>;
}
interface TextTableData extends Record<OtherNameTable, DexTable<string>> {
Abilities: DexTable<ResolvedAbilityText>;
Items: DexTable<ResolvedItemText>;
Moves: DexTable<ResolvedMoveText>;
PokedexNames: DexTable<string>;
Pokedex: DexTable<ResolvedSpeciesText>;
Default: DexTable<DefaultText>;
}
@@ -518,12 +518,12 @@ export class ModdedDex {
const englishData = this.loadRawTextData();
const localizedData = lang === 'en' ? englishData : this.loadRawTextData(lang);
return (dexes['base'].textCache[cacheKey] = {
PokedexNames: this.resolveNameTable(englishData.PokedexNames, localizedData.PokedexNames),
...this.resolveOtherNameTables(englishData, localizedData),
Pokedex: this.resolvePokedexTable(englishData.Pokedex, localizedData.Pokedex),
Moves: this.resolveTextTable(englishData.Moves, localizedData.Moves),
Abilities: this.resolveTextTable(englishData.Abilities, localizedData.Abilities),
Items: this.resolveTextTable(englishData.Items, localizedData.Items),
Default: localizedData.Default,
...this.resolveOtherNameTables(englishData, localizedData),
});
}
@@ -533,12 +533,12 @@ export class ModdedDex {
const langDir = lang === 'en' ? `` : `${lang}/`;
const optional = lang !== 'en';
const otherNameTables = Object.fromEntries(OTHER_NAME_TABLES.map(table => [
table, this.loadTextFile(`${langDir}other-names`, table, optional) || {},
table, this.loadTextFile(`${langDir}names`, table, optional) || {},
])) as Pick<RawTextTableData, OtherNameTable>;
const data: RawTextTableData = {
PokedexNames: this.loadTextFile(
`${langDir}pokedex-names`, 'PokedexNames', optional
) as Record<string, TranslationString>,
Pokedex: this.loadTextFile(
`${langDir}pokedex`, 'PokedexText', optional
) as DexTable<SpeciesText>,
...otherNameTables,
Moves: this.loadTextFile(`${langDir}moves`, 'MovesText', optional) as DexTable<MoveText>,
Abilities: this.loadTextFile(`${langDir}abilities`, 'AbilitiesText', optional) as DexTable<AbilityText>,
@@ -549,6 +549,40 @@ export class ModdedDex {
return (dexes['base'].rawTextCache[lang] = data);
}
private resolvePokedexTable(
englishTable: DexTable<SpeciesText>, localizedTable: DexTable<SpeciesText>
): DexTable<ResolvedSpeciesText> {
const FIELDS = ['name', 'baseSpecies', 'forme', 'grammar'] as const;
const merge = (english?: SpeciesText, localized?: SpeciesText) => {
const entry: Partial<ResolvedSpeciesText> = {};
for (const field of FIELDS) {
const value = localized?.[field] ?? english?.[field];
if (value) entry[field] = value;
}
return entry;
};
const mergedTable: DexTable<Partial<ResolvedSpeciesText>> = {};
for (const id in englishTable) {
mergedTable[id] = merge(englishTable[id], localizedTable[id]);
}
for (const id in localizedTable) {
if (!(id in englishTable)) mergedTable[id] = merge(undefined, localizedTable[id]);
}
const table: DexTable<ResolvedSpeciesText> = {};
for (const id in mergedTable) {
const entry = mergedTable[id];
const species = this.species.get(id);
const baseEntry = mergedTable[toID(species.baseSpecies)];
table[id] = {
...entry,
name: entry.name || species.name,
baseSpecies: baseEntry?.baseSpecies ?? baseEntry?.name ?? species.baseSpecies,
};
}
return table;
}
private resolveNameTable(
englishTable: Record<string, TranslationString>, localizedTable: Record<string, TranslationString>
): Record<string, string> {

View File

@@ -487,6 +487,22 @@ type AbilityText = TextFile<ConditionTextData & {
}>;
type MoveText = TextFile<MoveTextData>;
type ItemText = TextFile<ConditionTextData>;
interface SpeciesText {
/**
* Full species + short forme name - "Wormadam-Plant"
*
* Not the actual full official name, but the one Showdown uses that's
* reasonably concise and mostly fits into teambuilder. Includes formes for
* many but not all base formes. Can't contain parentheses because it's
* designed to be used like "Go! Planty (Wormadam-Plant)".
*/
name?: TranslationString;
/** Species name by itself, without forme - "Wormadam" */
baseSpecies?: TranslationString;
/** Official forme name - "Plant Cloak" */
forme?: TranslationString;
grammar?: TranslationString;
}
type DefaultText = AnyObject;
type ResolvedText<T extends BasicTextData> = T & { name: string, desc: string, shortDesc: string };
@@ -494,6 +510,7 @@ type ResolvedAbilityText = ResolvedText<AbilityText>;
type ResolvedItemText = ResolvedText<ItemText>;
type ResolvedMoveText = ResolvedText<MoveText>;
type ResolvedNameText = { name: string };
type ResolvedSpeciesText = { name: string, baseSpecies: string, forme?: string, grammar?: string };
declare namespace RandomTeamsTypes {
export interface TeamDetails {

View File

@@ -27,6 +27,23 @@ describe('Chat', () => {
assert.equal(TL(Dex.items.get('Leftovers')), 'たべのこし');
assert.equal(TL(Dex.moves.get('Tackle')), 'たいあたり');
assert.equal(TL(Dex.species.get('Pikachu')), 'ピカチュウ');
const text = Dex.loadTextData('ja');
for (const [property, table] of Object.entries({
term: 'TermNames',
type: 'TypeNames',
nature: 'NatureNames',
gender: 'GenderNames',
egggroup: 'EggGroupNames',
tag: 'TagNames',
color: 'ColorNames',
status: 'StatusNames',
target: 'TargetNames',
stat: 'StatNames',
statShort: 'StatShortNames',
statMedium: 'StatMediumNames',
})) {
assert.equal(TL[property], text[table]);
}
});
it('should localize data HTML', () => {

View File

@@ -126,6 +126,59 @@ describe('Dex data', () => {
}
});
it('should have unique Pokedex names and formes within each species', () => {
const languages = ['en', ...fs.readdirSync(`${Dex.dataDir}/text`, { withFileTypes: true })
.filter(entry => entry.isDirectory())
.map(entry => entry.name)];
for (const language of languages) {
const text = Dex.loadTextData(language).Pokedex;
const speciesValues = new Map();
for (const id in text) {
const entry = text[id];
const species = Dex.species.get(id);
let values = speciesValues.get(species.baseSpecies);
if (!values) {
values = { name: new Map(), forme: new Map() };
speciesValues.set(species.baseSpecies, values);
}
for (const field of ['name', 'forme']) {
const value = entry[field];
if (typeof value !== 'string') continue;
const duplicate = values[field].get(value);
assert.equal(
duplicate, undefined,
`${language} ${species.baseSpecies} formes ${duplicate} and ${id} have the same ${field} "${value}"`
);
values[field].set(value, id);
}
}
}
});
it('should only have necessary baseSpecies text on base species entries', () => {
const languages = ['en', ...fs.readdirSync(`${Dex.dataDir}/text`, { withFileTypes: true })
.filter(entry => entry.isDirectory())
.map(entry => entry.name)];
for (const language of languages) {
const path = language === 'en' ? 'pokedex' : `${language}/pokedex`;
const rawText = Dex.loadTextFile(path, 'PokedexText', language !== 'en');
for (const id in rawText) {
const entry = rawText[id];
const species = Dex.species.get(id);
if (entry.baseSpecies !== undefined) {
assert.equal(
species.name, species.baseSpecies,
`${language} ${id} has baseSpecies text, but is not a base species entry`
);
assert.notEqual(
entry.baseSpecies, entry.name,
`${language} ${id} should omit baseSpecies text when it is the same as its name`
);
}
}
}
});
it('should have valid Items entries', () => {
const Items = Dex.data.Items;
for (const itemid in Items) {

View File

@@ -91,8 +91,14 @@ describe('Dex#getMove', () => {
describe('DexText#get', () => {
it(`should translate species, items, abilities, and moves`, () => {
assert.deepEqual(Dex.text.get(Dex.species.get('Pikachu'), 'ja'), { name: 'ピカチュウ' });
assert.equal(Dex.loadTextData('ja').PokedexNames.pikachu, 'ピカチュウ');
const pikachuText = Dex.loadTextData('ja').Pokedex.pikachu;
assert.deepEqual(pikachuText, {
name: 'ピカチュウ', baseSpecies: 'ピカチュウ',
});
assert.equal(Dex.text.get(Dex.species.get('Pikachu'), 'ja'), pikachuText);
assert.deepEqual(Dex.loadTextData('ja').Pokedex.unownb, {
name: 'アンノーン-B', baseSpecies: 'アンノーン', forme: 'B',
});
assert.equal(Dex.text.get(Dex.items.get('Leftovers'), 'ja').name, 'たべのこし');
assert.equal(Dex.text.get(Dex.abilities.get('Levitate'), 'ja').name, 'ふゆう');
assert.equal(Dex.text.get(Dex.moves.get('Tackle'), 'ja').name, 'たいあたり');