Depends on jfberry/PoracleNG#217 (8a4ed111), but not on the rest of it — the route is additive, so this can land as soon as a build carrying it is available.
GET /api/masterdata/grunts?locale= now serves the composed display name for 415 grunts across 16 locales, taken from the pogo-translations grunt_<id> keys:
"10": { "grunt_type": "dark", "name": "Unlicht - Rüpel (Weiblich)",
"short_name": "Unlicht ♀", "type": "Unlicht", "grunt": "Rüpel", "gender": 2 }
That replaces most of GRUNT_DISPLAY_KEYS, which we've maintained by hand in eleven locale files. Confirmed against pogo-translations directly: the type halves match ours exactly in German. The one divergence is decoy ("Täusch-Rüpel (Männlich)" upstream against our "Köder-Rüpel"). Take theirs — one vocabulary beats two that agree most of the time.
Scope
Proxy the route through MasterDataController, alongside monsters/items/moves, and fetch it in the same forkJoin in MasterDataService. The locale is the display language, so it refetches and re-emits on a language change like monsters already does (#771).
Index on grunt_type, not the map key. The response is keyed by grunt id and several ids share one grunt_type (regular vs event Giovanni, male/female pairs). After #209, grunt_type is what a tracking rule read hands us, so the map key is the wrong join column.
Delete 24 of the 26 GRUNT_DISPLAY_KEYS entries and their strings in all eleven locale files.
everything and boss stay. They aren't grunts, they're filter vocabulary — a rule can target every invasion or only boss encounters — so there is no grunt_<id> for either and there never will be. getGruntDisplayKey keeps a small local table for exactly those two.
Delete the gender suffix logic. getGruntDisplayName appends a translated (Male)/(Female) for GENDER_FIXED_GRUNT_TYPES (mixed, decoy). Upstream name already carries it, so keeping ours would double it up. GENDER_FIXED_GRUNT_TYPES itself stays — isGenderFixed still drives whether the gender dropdown renders.
Consumers: invasion-add-dialog, invasion-edit-dialog, invasion-list (two sites), profile-overview.component.ts:472.
Decide: the gender-0 case
One grunt_type maps to several names. ("mixed", gender 0) is what !invasion mixed stores, so it is not a rare shape, and picking either the male or the female entry as a representative would misreport a rule that matches both.
Compose type plus our own "any gender" string for that case rather than picking one. That keeps a small number of our own strings, which is why the eleven locale files shrink rather than lose their invasion section.
Watch for
A missing route must not blank the UI. Same fallback shape as monsters: an older Poracle, or one that can't be reached, keeps whatever label we can still produce. Falling through to INVASIONS.UNKNOWN_GRUNT for all 24 deleted names because a fetch failed is worse than what we have now.
This is a gain, not just a deletion. The 24 names were never our whole vocabulary — the 24 npc_*, blanche, candela, spark, gruntb and player team leader have always rendered as "Unknown grunt". A rule created from the bot holding npc_3 becomes displayable for the first time. Test one.
Depends on jfberry/PoracleNG#217 (
8a4ed111), but not on the rest of it — the route is additive, so this can land as soon as a build carrying it is available.GET /api/masterdata/grunts?locale=now serves the composed display name for 415 grunts across 16 locales, taken from the pogo-translationsgrunt_<id>keys:That replaces most of
GRUNT_DISPLAY_KEYS, which we've maintained by hand in eleven locale files. Confirmed against pogo-translations directly: the type halves match ours exactly in German. The one divergence isdecoy("Täusch-Rüpel (Männlich)" upstream against our "Köder-Rüpel"). Take theirs — one vocabulary beats two that agree most of the time.Scope
Proxy the route through
MasterDataController, alongside monsters/items/moves, and fetch it in the sameforkJoininMasterDataService. The locale is the display language, so it refetches and re-emits on a language change like monsters already does (#771).Index on
grunt_type, not the map key. The response is keyed by grunt id and several ids share onegrunt_type(regular vs event Giovanni, male/female pairs). After #209,grunt_typeis what a tracking rule read hands us, so the map key is the wrong join column.Delete 24 of the 26
GRUNT_DISPLAY_KEYSentries and their strings in all eleven locale files.everythingandbossstay. They aren't grunts, they're filter vocabulary — a rule can target every invasion or only boss encounters — so there is nogrunt_<id>for either and there never will be.getGruntDisplayKeykeeps a small local table for exactly those two.Delete the gender suffix logic.
getGruntDisplayNameappends a translated(Male)/(Female)forGENDER_FIXED_GRUNT_TYPES(mixed,decoy). Upstreamnamealready carries it, so keeping ours would double it up.GENDER_FIXED_GRUNT_TYPESitself stays —isGenderFixedstill drives whether the gender dropdown renders.Consumers:
invasion-add-dialog,invasion-edit-dialog,invasion-list(two sites),profile-overview.component.ts:472.Decide: the gender-0 case
One
grunt_typemaps to severalnames.("mixed", gender 0)is what!invasion mixedstores, so it is not a rare shape, and picking either the male or the female entry as a representative would misreport a rule that matches both.Compose
typeplus our own "any gender" string for that case rather than picking one. That keeps a small number of our own strings, which is why the eleven locale files shrink rather than lose their invasion section.Watch for
A missing route must not blank the UI. Same fallback shape as monsters: an older Poracle, or one that can't be reached, keeps whatever label we can still produce. Falling through to
INVASIONS.UNKNOWN_GRUNTfor all 24 deleted names because a fetch failed is worse than what we have now.This is a gain, not just a deletion. The 24 names were never our whole vocabulary — the 24
npc_*,blanche,candela,spark,gruntbandplayer team leaderhave always rendered as "Unknown grunt". A rule created from the bot holdingnpc_3becomes displayable for the first time. Test one.