Template:TranslationDef: Difference between revisions

From Heroes of Might and Magic: Olden Era Official Wiki
obelisk-bot: uploading initial Cargo templates
 
obelisk-bot: Adding new tables for hero skill rolls
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
<noinclude>
<noinclude>
A single shared table for '''all per-entity i18n payloads'''. Replaces the parallel <code>UnitTranslation</code>, <code>FactionTranslation</code>,
A single shared table for '''all per-entity i18n payloads''', stored in '''long format''' — one row per <code>(target, language)</code> pair rather than one row per target with a column per language. English is stored here too (<code>language=en</code>), so a language-aware query is a uniform <code>WHERE language=…</code> filter with no special-casing.
<code>AttackPassiveTranslation</code>, … tables we used to ship — same shape,
one row per (entity-type, entity-id) pair, discriminated by <code>type</code>. See D-026.


The bot never extracts <code>TranslationDef</code> rows on their own; each parent entity's emit function appends a <code>{{TranslationDef | type=… | …}}</code> call to its own page, right after the entity's structural row. The
The bot never extracts <code>TranslationDef</code> rows on their own; each parent entity's emit function appends one <code><nowiki>{{TranslationDef | … }}</nowiki></code> call '''per language''' to its own page, right after the entity's structural row. The <code><nowiki>{{TranslationDef}}</nowiki></code> MediaWiki template's job is to call <code>#cargo_store</code> on this table.
<code>{{TranslationDef}}</code> MediaWiki template's job is to call <code>#cargo_store</code>
on this table.


== Schema ==
== Schema ==
{{#cargo_declare:_table=Translation
{{#cargo_declare:_table=Translation
| target_id = String
| type = String
| type = String
| target_id = String
| subtype = String
| name_sid = String
| variant = String
| desc_sid = String
| language = String
| pt_br_name = String
| name = String
| pt_br_desc = Wikitext
| description = Wikitext
| cs_name = String
| bonus_description = Wikitext
| cs_desc = Wikitext
| fr_name = String
| fr_desc = Wikitext
| de_name = String
| de_desc = Wikitext
| hu_name = String
| hu_desc = Wikitext
| it_name = String
| it_desc = Wikitext
| ja_name = String
| ja_desc = Wikitext
| ko_name = String
| ko_desc = Wikitext
| pl_name = String
| pl_desc = Wikitext
| ru_name = String
| ru_desc = Wikitext
| es_name = String
| es_desc = Wikitext
| tr_name = String
| tr_desc = Wikitext
| uk_name = String
| uk_desc = Wikitext
| zh_cn_name = String
| zh_cn_desc = Wikitext
| zh_tw_name = String
| zh_tw_desc = Wikitext
}}
}}


<pre><nowiki>
<pre><nowiki>
{{TranslationDef
{{TranslationDef
| type = String              <!-- e.g. unit, faction, hero, hero_class, attack_passive -->
| target_id = String        <!-- the parent entity's id (Unit.id, Faction.id, …); for catch-all Entry rows (attack_archetype, ui, …) duplicates subtype so the join column is uniformly non-null -->
| target_id = String        <!-- the parent entity's id (Unit.id, Faction.id, etc.) -->
| type = String             <!-- e.g. unit, faction, hero, attack_archetype, spell_rank -->
| name_sid = String
| subtype = String           <!-- secondary key component; sparse -->
| desc_sid = String
| variant = String           <!-- tertiary key component; sparse -->
 
| language = String         <!-- en, pt_br, cs, fr, de, hu, it, ja, ko, pl, ru, es, tr, uk, zh_cn, zh_tw -->
<!-- Per-language pairs. Each language gets a name + desc column. -->
| name = String
| pt_br_name = String
| description = Wikitext
| pt_br_desc = Wikitext
| bonus_description = Wikitext   <!-- sparse: only spell-rank rows populate this -->
| cs_name = String
| cs_desc = Wikitext
| fr_name = String
| fr_desc = Wikitext
| de_name = String
| de_desc = Wikitext
| hu_name = String
| hu_desc = Wikitext
| it_name = String
| it_desc = Wikitext
| ja_name = String
| ja_desc = Wikitext
| ko_name = String
| ko_desc = Wikitext
| pl_name = String
| pl_desc = Wikitext
| ru_name = String
| ru_desc = Wikitext
| es_name = String
| es_desc = Wikitext
| tr_name = String
| tr_desc = Wikitext
| uk_name = String
| uk_desc = Wikitext
| zh_cn_name = String
| zh_cn_desc = Wikitext
| zh_tw_name = String
| zh_tw_desc = Wikitext
}}
}}
</nowiki></pre>
</nowiki></pre>


== Field notes ==
== Field notes ==
* '''Primary key is <code>(type, target_id)</code>''' as a tuple. Joins from parent entities include the type discriminator: e.g. <code>Unit.id = Translation.target_id WHERE Translation.type='unit'</code>.
* '''Primary key is <code>(target_id, type, subtype, variant, language)</code>''' as a tuple. <code>type</code> / <code>subtype</code> / <code>variant</code> are tiers of the same discriminator pyramid; <code>target_id</code> is the entity id and ''usually'' pairs with a <code>type</code>, but that coupling isn't enforced by the architecture. Most entities use only <code>target_id</code> + <code>type</code>; catch-all [[Template:EntryDef|Entry]] rows are identified by <code>(type, subtype, variant)</code> conceptually, but the emitter duplicates <code>subtype</code> into <code>target_id</code> so self-joins across languages work uniformly (Cargo stores blank parameter values as SQL <code>NULL</code>, and <code>NULL = NULL</code> doesn't match in a join); per-level rows (spell ranks, law levels, skill levels) carry the level in <code>variant</code>.
'' ''*English defaults are ''not'' here.** They live on each parent entity's row (<code>Unit.name</code>, <code>Faction.name</code>, <code>HeroClass.name</code>, etc.) so the most common query — "give me the English label for this unit" — needs only a single-table read. Translations are the i18n payload only.
* '''Joins from parent entities''' include the type discriminator and the language filter, e.g. <code>Unit.id = Translation.target_id WHERE Translation.type='unit' AND Translation.language='{{{lang|en}}}'</code>.
* '''Language-code mapping''' (game directory → two-letter code):
* '''English is in this table''' as <code>language=en</code> — it is not a special case. Parent entity tables no longer carry inline English <code>name</code>/<code>description</code> columns; all display text for every language comes from here.
* '''<code>name</code> is <code>String</code>, <code>description</code>/<code>bonus_description</code> are <code>Wikitext</code>''' — descriptions may carry HTML-bold/italic from the L10n corpus, which the bot's resolver converts to wiki markup (<code>'''</code> / <code>''</code>) before storing. Names are plain.
* '''Sparse output''': any value column the bot couldn't resolve is omitted. A missing translation is an absent row, not an empty column.
* '''No <code>name_sid</code>/<code>desc_sid</code> here.''' The source SIDs are language-independent and live on the parent entity's own row (or its thin <code>Entry</code>/<code>SpellRank</code> stub) for traceability. They are not part of the translation lookup path.
* '''Language-code mapping''' (game directory → code):
** <code>english</code> → <code>en</code>
** <code>BRportugese</code> → <code>pt_br</code>
** <code>BRportugese</code> → <code>pt_br</code>
** <code>czech</code> → <code>cs</code>, <code>french</code> → <code>fr</code>, <code>german</code> → <code>de</code>, <code>hungarian</code> → <code>hu</code>, <code>italian</code> → <code>it</code>, <code>japanese</code> → <code>ja</code>, <code>korean</code> → <code>ko</code>, <code>polish</code> → <code>pl</code>, <code>russian</code> → <code>ru</code>, <code>spanish</code> → <code>es</code>, <code>turkish</code> → <code>tr</code>, <code>ukrainian</code> → <code>uk</code>
** <code>czech</code> → <code>cs</code>, <code>french</code> → <code>fr</code>, <code>german</code> → <code>de</code>, <code>hungarian</code> → <code>hu</code>, <code>italian</code> → <code>it</code>, <code>japanese</code> → <code>ja</code>, <code>korean</code> → <code>ko</code>, <code>polish</code> → <code>pl</code>, <code>russian</code> → <code>ru</code>, <code>spanish</code> → <code>es</code>, <code>turkish</code> → <code>tr</code>, <code>ukrainian</code> → <code>uk</code>
** <code>zhCN</code> → <code>zh_cn</code>, <code>zhTW</code> → <code>zh_tw</code>
** <code>zhCN</code> → <code>zh_cn</code>, <code>zhTW</code> → <code>zh_tw</code>
* '''Sparse output''': any per-language slot the bot couldn't resolve is omitted. Some entities (e.g. faction-shared <code>magic_desc</code> / <code>might_desc</code> for HeroClass) deliberately reuse a single SID across many parents — those parents will all carry the same per-language text on their own Translation row.
* '''The <code>desc</code> field is Wikitext''' because translations may include HTML-bold and HTML-italic tags from the L10n corpus, which the bot's resolver converts to wiki markup (<code>'''</code> and <code>''</code>) before storing.


== Entity types currently emitted ==
== Key-mapping examples ==
How different parent shapes map onto the key tiers:
 
{| class="wikitable"
{| class="wikitable"
! <code>type</code> !! Parent entity !! <code>target_id</code> !! Emit site
! Parent !! <code>target_id</code> !! <code>type</code> !! <code>subtype</code> !! <code>variant</code>
|-
| Unit || <code>Unit.id</code> || <code>unit</code> || ||
|-
| Faction || <code>Faction.id</code> || <code>faction</code> || ||
|-
| Hero || <code>Hero.id</code> || <code>hero</code> || ||
|-
|-
| <code>unit</code> || [[../Unit|<code>UnitDef</code>]] || <code>Unit.id</code> || <code>emit_unit_page</code>
| Hero motto || <code>Hero.id</code> || <code>hero_motto</code> || ||
|-
|-
| <code>unit_ability</code> || [[../UnitAbility|<code>UnitAbilityDef</code>]] || <code>UnitAbility.ability_id</code> (composite) || <code>emit_unit_page</code>
| Spell rank || <code>Spell.id</code> || <code>spell_rank</code> || || <code>level</code> (1-4)
|-
|-
| <code>attack_passive</code> || [[Template:AttackPassiveDef|<code>AttackPassiveDef</code>]] || <code>AttackPassive.attack_passive_id</code> || <code>emit_attack_passive_page</code>
| Attack archetype || <code>melee</code> / <code>ranged</code> / … (= subtype) || <code>attack_archetype</code> || <code>melee</code> / <code>ranged</code> / … ||
|-
|-
| <code>faction</code> || [[../Faction|<code>FactionDef</code>]] || <code>Faction.id</code> || <code>emit_faction_page</code>
| Law level || <code>Law.id</code> || <code>law_level</code> || || <code>level</code>
|}
|}


(Hero-side entries: <code>hero</code>, <code>hero_class</code>, <code>hero_specialization</code>,
The precise mapping per parent type is owned by that entity's emit function the schema just provides the columns.
<code>hero_sub_class</code> added as those entities come online.)


== Page layout ==
== Page layout ==
Translation rows do '''not''' live on their own wiki pages. Each row is emitted as a <code>{{TranslationDef | …}}</code> invocation on the parent entity's data page, immediately after the entity's structural row. The
Translation rows do '''not''' live on their own wiki pages. Each row is emitted as a <code><nowiki>{{TranslationDef | … }}</nowiki></code> invocation on the parent entity's data page, immediately after the entity's structural row — one invocation per language. The key tuple means Cargo can store many rows in a single table without page-naming collisions.
<code>(type, target_id)</code> discriminator means Cargo can store many rows in
a single table without page-naming collisions.
 
== Wiki template notes ==
<syntaxhighlight lang="mediawiki">
<noinclude>
Per-entity i18n payload, shared across all categories. Invoked from each parent entity's data page (Data:Unit/<id>, Data:Faction/<id>, etc.) — there is no Data:Translation/… page namespace.
</noinclude><includeonly>{{#cargo_store:_table=TranslationDef
| type={{{type|}}}
| target_id={{{target_id|}}}
| name_sid={{{name_sid|}}}
| desc_sid={{{desc_sid|}}}
| pt_br_name={{{pt_br_name|}}}
| pt_br_desc={{{pt_br_desc|}}}
| cs_name={{{cs_name|}}}
| cs_desc={{{cs_desc|}}}
| fr_name={{{fr_name|}}}
| fr_desc={{{fr_desc|}}}
| de_name={{{de_name|}}}
| de_desc={{{de_desc|}}}
| hu_name={{{hu_name|}}}
| hu_desc={{{hu_desc|}}}
| it_name={{{it_name|}}}
| it_desc={{{it_desc|}}}
| ja_name={{{ja_name|}}}
| ja_desc={{{ja_desc|}}}
| ko_name={{{ko_name|}}}
| ko_desc={{{ko_desc|}}}
| pl_name={{{pl_name|}}}
| pl_desc={{{pl_desc|}}}
| ru_name={{{ru_name|}}}
| ru_desc={{{ru_desc|}}}
| es_name={{{es_name|}}}
| es_desc={{{es_desc|}}}
| tr_name={{{tr_name|}}}
| tr_desc={{{tr_desc|}}}
| uk_name={{{uk_name|}}}
| uk_desc={{{uk_desc|}}}
| zh_cn_name={{{zh_cn_name|}}}
| zh_cn_desc={{{zh_cn_desc|}}}
| zh_tw_name={{{zh_tw_name|}}}
| zh_tw_desc={{{zh_tw_desc|}}}
}}</includeonly>
</syntaxhighlight>
 
== Notes ==


[[Category:Cargo Definitions]]
[[Category:Cargo Definitions]]
</noinclude><includeonly>{{#cargo_store:_table=Translation
</noinclude><includeonly>{{#cargo_store:_table=Translation
| target_id = {{{target_id|}}}
| type = {{{type|}}}
| type = {{{type|}}}
| target_id = {{{target_id|}}}
| subtype = {{{subtype|}}}
| name_sid = {{{name_sid|}}}
| variant = {{{variant|}}}
| desc_sid = {{{desc_sid|}}}
| language = {{{language|}}}
| pt_br_name = {{{pt_br_name|}}}
| name = {{{name|}}}
| pt_br_desc = {{{pt_br_desc|}}}
| description = {{{description|}}}
| cs_name = {{{cs_name|}}}
| bonus_description = {{{bonus_description|}}}
| cs_desc = {{{cs_desc|}}}
}}
| fr_name = {{{fr_name|}}}
Created <code>Translation</code> entry: <code>{{{target_id|}}}/{{{type|}}}/{{{subtype|}}}/{{{variant|}}} ({{{language|}}})</code></includeonly>
| fr_desc = {{{fr_desc|}}}
| de_name = {{{de_name|}}}
| de_desc = {{{de_desc|}}}
| hu_name = {{{hu_name|}}}
| hu_desc = {{{hu_desc|}}}
| it_name = {{{it_name|}}}
| it_desc = {{{it_desc|}}}
| ja_name = {{{ja_name|}}}
| ja_desc = {{{ja_desc|}}}
| ko_name = {{{ko_name|}}}
| ko_desc = {{{ko_desc|}}}
| pl_name = {{{pl_name|}}}
| pl_desc = {{{pl_desc|}}}
| ru_name = {{{ru_name|}}}
| ru_desc = {{{ru_desc|}}}
| es_name = {{{es_name|}}}
| es_desc = {{{es_desc|}}}
| tr_name = {{{tr_name|}}}
| tr_desc = {{{tr_desc|}}}
| uk_name = {{{uk_name|}}}
| uk_desc = {{{uk_desc|}}}
| zh_cn_name = {{{zh_cn_name|}}}
| zh_cn_desc = {{{zh_cn_desc|}}}
| zh_tw_name = {{{zh_tw_name|}}}
| zh_tw_desc = {{{zh_tw_desc|}}}
}}</includeonly>

Latest revision as of 01:14, 19 May 2026

A single shared table for all per-entity i18n payloads, stored in long format — one row per (target, language) pair rather than one row per target with a column per language. English is stored here too (language=en), so a language-aware query is a uniform WHERE language=… filter with no special-casing.

The bot never extracts TranslationDef rows on their own; each parent entity's emit function appends one {{TranslationDef | … }} call per language to its own page, right after the entity's structural row. The {{TranslationDef}} MediaWiki template's job is to call #cargo_store on this table.

Schema

This template defines the table "Translation". View table.

{{TranslationDef
| target_id = String         <!-- the parent entity's id (Unit.id, Faction.id, …); for catch-all Entry rows (attack_archetype, ui, …) duplicates subtype so the join column is uniformly non-null -->
| type = String              <!-- e.g. unit, faction, hero, attack_archetype, spell_rank -->
| subtype = String           <!-- secondary key component; sparse -->
| variant = String           <!-- tertiary key component; sparse -->
| language = String          <!-- en, pt_br, cs, fr, de, hu, it, ja, ko, pl, ru, es, tr, uk, zh_cn, zh_tw -->
| name = String
| description = Wikitext
| bonus_description = Wikitext   <!-- sparse: only spell-rank rows populate this -->
}}

Field notes

  • Primary key is (target_id, type, subtype, variant, language) as a tuple. type / subtype / variant are tiers of the same discriminator pyramid; target_id is the entity id and usually pairs with a type, but that coupling isn't enforced by the architecture. Most entities use only target_id + type; catch-all Entry rows are identified by (type, subtype, variant) conceptually, but the emitter duplicates subtype into target_id so self-joins across languages work uniformly (Cargo stores blank parameter values as SQL NULL, and NULL = NULL doesn't match in a join); per-level rows (spell ranks, law levels, skill levels) carry the level in variant.
  • Joins from parent entities include the type discriminator and the language filter, e.g. Unit.id = Translation.target_id WHERE Translation.type='unit' AND Translation.language='en'.
  • English is in this table as language=en — it is not a special case. Parent entity tables no longer carry inline English name/description columns; all display text for every language comes from here.
  • name is String, description/bonus_description are Wikitext' — descriptions may carry HTML-bold/italic from the L10n corpus, which the bot's resolver converts to wiki markup ( / ) before storing. Names are plain.
  • Sparse output: any value column the bot couldn't resolve is omitted. A missing translation is an absent row, not an empty column.
  • No name_sid/desc_sid here. The source SIDs are language-independent and live on the parent entity's own row (or its thin Entry/SpellRank stub) for traceability. They are not part of the translation lookup path.
  • Language-code mapping (game directory → code):
    • englishen
    • BRportugesept_br
    • czechcs, frenchfr, germande, hungarianhu, italianit, japaneseja, koreanko, polishpl, russianru, spanishes, turkishtr, ukrainianuk
    • zhCNzh_cn, zhTWzh_tw

Key-mapping examples

How different parent shapes map onto the key tiers:

Parent target_id type subtype variant
Unit Unit.id unit
Faction Faction.id faction
Hero Hero.id hero
Hero motto Hero.id hero_motto
Spell rank Spell.id spell_rank level (1-4)
Attack archetype melee / ranged / … (= subtype) attack_archetype melee / ranged / …
Law level Law.id law_level level

The precise mapping per parent type is owned by that entity's emit function — the schema just provides the columns.

Page layout

Translation rows do not live on their own wiki pages. Each row is emitted as a {{TranslationDef | … }} invocation on the parent entity's data page, immediately after the entity's structural row — one invocation per language. The key tuple means Cargo can store many rows in a single table without page-naming collisions.