i18n(@projects/@magic-civilization): 🌐 route remaining .tscn UI text through ThemeVocabulary — i18n gate GREEN

Wire the static .tscn label/button text in 5 scenes (end_game_summary section
titles + footer, merge_panel, city_screen merge button, game_setup section
headers, past_games) through ThemeVocabulary.lookup() in their controllers'
_ready, and remove the hardcoded .tscn text. Add the corresponding vocab keys;
drop 3 redundant endgame_* keys (footer buttons already use endgame_footer_*).

validate-i18n now passes: 145 scenes scanned, 0 hardcoded UI strings.
This clears the i18n verify gate with no bypass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Natalie 2026-06-19 17:42:38 -05:00
parent 07a10054f4
commit 30f12f5e7e
11 changed files with 42 additions and 27 deletions

View file

@ -347,7 +347,7 @@
"fmt_great_person_cannot": "Cannot apply: %s",
"merge_no_extension": "GdGameState extension not loaded.",
"fmt_merge_not_found": "Hybrid building '%s' not found in registry.",
"merge_buildings": "Merge Buildings",
"merge_buildings_title": "Merge Buildings",
"merge_confirm": "Merge",
"merge_cancel": "Cancel",
"specialists_idle_citizens": "Idle citizens:",
@ -362,15 +362,19 @@
"past_game_delete": "Delete",
"lens_none_tooltip": "No lens (default biome view)",
"fmt_ransom_offered": "Offered turn %d",
"endgame_game_over": "Game Over",
"endgame_winner": "Winner",
"endgame_your_clan": "Your Clan",
"endgame_final_standings": "Final Standings",
"endgame_score_over_time": "Score Over Time",
"endgame_awards": "Awards",
"endgame_timeline": "Timeline",
"endgame_view_map": "View Map",
"endgame_save_archive": "Save to Archive",
"past_games_title": "Past Games",
"past_games_outcome": "Outcome:",
"past_games_sort": "Sort:",
"past_games_empty": "No past games found.",
"past_games_back": "Back",
"setup_world_shape_section": "WORLD SHAPE",
"setup_ai_controller_section": "AI CONTROLLER PER SLOT",
"tutorial_skip": "Skip Tutorial",
"tutorial_step_1_title": "The Hex Map",
"tutorial_step_1_body": "The world is carved into hex tiles. Click a tile to inspect it. Right-click (or click a destination) to move your selected unit. Drag with the middle mouse button to pan; scroll to zoom.",

View file

@ -125,6 +125,7 @@ func _ready() -> void:
EventBus.building_placement_confirmed.connect(_on_building_placement_confirmed)
_buildings_list.item_selected.connect(_on_building_selected)
_merge_btn.pressed.connect(_on_merge_btn_pressed)
_merge_btn.text = ThemeVocabulary.lookup("merge_buildings_title", "Merge Buildings")
_merge_panel.refresh_requested.connect(_refresh)
EventBus.buildings_merged.connect(_on_buildings_merged)
_building_panel.set_rally_pressed.connect(_on_building_panel_set_rally)

View file

@ -578,7 +578,6 @@ layout_mode = 2
size_flags_horizontal = 0
visible = false
theme_override_colors/font_color = Color(0.9, 0.75, 0.3, 1)
text = "Merge Buildings"
[node name="CloseButton" type="Button" parent="Panel/Margin/VBox/FooterRow"]
unique_name_in_owner = true

View file

@ -36,6 +36,7 @@ func _ready() -> void:
_close_button.pressed.connect(_on_close_pressed)
_title_label.text = ThemeVocabulary.lookup("merge_buildings_title", "Merge Buildings")
_confirm_button.text = ThemeVocabulary.lookup("merge_confirm", "Merge")
_close_button.text = ThemeVocabulary.lookup("merge_cancel", "Cancel")
_error_label.hide()
hide()

View file

@ -19,7 +19,6 @@ layout_mode = 2
[node name="TitleLabel" type="Label" parent="MarginContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "Merge Buildings"
[node name="RowsContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer"]
unique_name_in_owner = true
@ -38,9 +37,7 @@ layout_mode = 2
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
text = "Merge"
[node name="CloseButton" type="Button" parent="MarginContainer/VBoxContainer/ButtonRow"]
unique_name_in_owner = true
layout_mode = 2
text = "Cancel"

View file

@ -66,6 +66,7 @@ var _awards: Array[Dictionary] = []
func _ready() -> void:
_wire_footer_labels()
_wire_static_labels()
_wire_footer_signals()
EventBus.game_over.connect(_on_game_over)
process_mode = Node.PROCESS_MODE_ALWAYS
@ -350,6 +351,25 @@ func _wire_footer_labels() -> void:
_main_menu_button.text = ThemeVocabulary.lookup("victory_menu_main_menu")
## Static hero/section title text — i18n via ThemeVocabulary (the banner is
## re-set per GameOverReason in setup(); this is the pre-setup default).
func _wire_static_labels() -> void:
_banner_label.text = ThemeVocabulary.lookup("endgame_banner_gameover")
var root: Node = self
(root.get_node("Margin/Root/HeroStrip/WinnerCard/WinnerVBox/WinnerTitle") as Label).text = \
ThemeVocabulary.lookup("endgame_winner")
(root.get_node("Margin/Root/HeroStrip/PlayerCard/PlayerVBox/PlayerTitle") as Label).text = \
ThemeVocabulary.lookup("endgame_your_clan")
(root.get_node("Margin/Root/Body/LeftColumn/StandingsTitle") as Label).text = \
ThemeVocabulary.lookup("endgame_final_standings")
(root.get_node("Margin/Root/Body/LeftColumn/GraphTitle") as Label).text = \
ThemeVocabulary.lookup("endgame_score_over_time")
(root.get_node("Margin/Root/Body/RightColumn/AwardsTitle") as Label).text = \
ThemeVocabulary.lookup("endgame_awards")
(root.get_node("Margin/Root/Body/RightColumn/TimelineTitle") as Label).text = \
ThemeVocabulary.lookup("endgame_timeline")
func _wire_footer_signals() -> void:
_view_map_button.pressed.connect(_on_view_map)
_watch_replay_button.pressed.connect(_on_watch_replay)

View file

@ -40,7 +40,6 @@ theme_override_constants/separation = 14
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 32
text = "Game Over"
horizontal_alignment = 1
[node name="HeroStrip" type="HBoxContainer" parent="Margin/Root"]
@ -61,7 +60,6 @@ theme_override_constants/separation = 4
layout_mode = 2
theme_override_font_sizes/font_size = 13
theme_override_colors/font_color = Color(0.85, 0.76, 0.52, 1)
text = "Winner"
horizontal_alignment = 1
[node name="WinnerNameLabel" type="Label" parent="Margin/Root/HeroStrip/WinnerCard/WinnerVBox"]
@ -83,7 +81,6 @@ theme_override_constants/separation = 4
layout_mode = 2
theme_override_font_sizes/font_size = 13
theme_override_colors/font_color = Color(0.6, 0.72, 0.85, 1)
text = "Your Clan"
horizontal_alignment = 1
[node name="PlayerNameLabel" type="Label" parent="Margin/Root/HeroStrip/PlayerCard/PlayerVBox"]
@ -106,7 +103,6 @@ theme_override_constants/separation = 8
layout_mode = 2
theme_override_font_sizes/font_size = 16
theme_override_colors/font_color = Color(0.9, 0.84, 0.65, 1)
text = "Final Standings"
[node name="StandingsScroll" type="ScrollContainer" parent="Margin/Root/Body/LeftColumn"]
layout_mode = 2
@ -122,7 +118,6 @@ theme_override_constants/separation = 2
layout_mode = 2
theme_override_font_sizes/font_size = 16
theme_override_colors/font_color = Color(0.9, 0.84, 0.65, 1)
text = "Score Over Time"
[node name="SummaryGraphArea" type="Control" parent="Margin/Root/Body/LeftColumn"]
unique_name_in_owner = true
@ -138,7 +133,6 @@ theme_override_constants/separation = 8
layout_mode = 2
theme_override_font_sizes/font_size = 16
theme_override_colors/font_color = Color(0.9, 0.84, 0.65, 1)
text = "Awards"
[node name="AwardsScroll" type="ScrollContainer" parent="Margin/Root/Body/RightColumn"]
layout_mode = 2
@ -154,7 +148,6 @@ theme_override_constants/separation = 10
layout_mode = 2
theme_override_font_sizes/font_size = 16
theme_override_colors/font_color = Color(0.9, 0.84, 0.65, 1)
text = "Timeline"
[node name="TimelineScroll" type="ScrollContainer" parent="Margin/Root/Body/RightColumn"]
layout_mode = 2
@ -175,28 +168,23 @@ alignment = 2
unique_name_in_owner = true
custom_minimum_size = Vector2(140, 38)
layout_mode = 2
text = "View Map"
[node name="WatchReplayButton" type="Button" parent="Margin/Root/Footer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(140, 38)
layout_mode = 2
text = "Watch Replay"
[node name="SaveArchiveButton" type="Button" parent="Margin/Root/Footer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(140, 38)
layout_mode = 2
text = "Save to Archive"
[node name="ExportJsonButton" type="Button" parent="Margin/Root/Footer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(140, 38)
layout_mode = 2
text = "Export JSON"
[node name="MainMenuButton" type="Button" parent="Margin/Root/Footer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(140, 38)
layout_mode = 2
text = "Main Menu"

View file

@ -50,6 +50,11 @@ var _controller_selections: Dictionary = {}
func _ready() -> void:
%TitleLabel.text = ThemeVocabulary.lookup("game_title")
%SubtitleLabel.text = ThemeVocabulary.lookup("game_subtitle")
var setup_vbox: Node = $CenterContainer/VBoxContainer
(setup_vbox.get_node("WorldShapeSectionLabel") as Label).text = \
ThemeVocabulary.lookup("setup_world_shape_section")
(setup_vbox.get_node("AiControllerSectionLabel") as Label).text = \
ThemeVocabulary.lookup("setup_ai_controller_section")
_populate_option_button(%MapTypeOption, ["Continents", "Pangaea", "Team Continents", "FFA Cross", "FFA Plus"])
_populate_option_button(%MapSizeOption, ["Duel (2)", "Tiny (4)", "Small (4)", "Standard (8)", "Large (10)", "Huge (12)"])
_populate_world_shape_dropdowns()

View file

@ -138,7 +138,6 @@ custom_minimum_size = Vector2(0, 8)
[node name="WorldShapeSectionLabel" type="Label" parent="CenterContainer/VBoxContainer"]
layout_mode = 2
text = "WORLD SHAPE"
theme_override_font_sizes/font_size = 11
theme_override_colors/font_color = Color(0.7, 0.62, 0.42, 1)
@ -225,7 +224,6 @@ unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 11
theme_override_colors/font_color = Color(0.7, 0.62, 0.42, 1)
text = "AI CONTROLLER PER SLOT"
[node name="AiControllerList" type="VBoxContainer" parent="CenterContainer/VBoxContainer"]
unique_name_in_owner = true

View file

@ -22,6 +22,13 @@ func _ready() -> void:
_outcome_filter.item_selected.connect(_on_outcome_filter_selected)
_sort_filter.item_selected.connect(_on_sort_filter_selected)
_back_button.text = ThemeVocabulary.lookup("past_games_back")
_empty_label.text = ThemeVocabulary.lookup("past_games_empty")
var vbox: Node = $MarginContainer/VBoxContainer
(vbox.get_node("TitleLabel") as Label).text = ThemeVocabulary.lookup("past_games_title")
(vbox.get_node("FilterRow/OutcomeLabel") as Label).text = ThemeVocabulary.lookup("past_games_outcome")
(vbox.get_node("FilterRow/SortLabel") as Label).text = ThemeVocabulary.lookup("past_games_sort")
# Populate filter dropdowns.
_outcome_filter.add_item("All outcomes")
_outcome_filter.add_item("Victory")

View file

@ -38,7 +38,6 @@ theme_override_constants/separation = 16
[node name="TitleLabel" type="Label" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
text = "Past Games"
theme_override_font_sizes/font_size = 36
theme_override_colors/font_color = Color(0.9, 0.8, 0.4, 1)
horizontal_alignment = 1
@ -52,7 +51,6 @@ theme_override_constants/separation = 16
[node name="OutcomeLabel" type="Label" parent="MarginContainer/VBoxContainer/FilterRow"]
layout_mode = 2
text = "Outcome:"
[node name="OutcomeFilter" type="OptionButton" parent="MarginContainer/VBoxContainer/FilterRow"]
unique_name_in_owner = true
@ -65,7 +63,6 @@ size_flags_horizontal = 3
[node name="SortLabel" type="Label" parent="MarginContainer/VBoxContainer/FilterRow"]
layout_mode = 2
text = "Sort:"
[node name="SortFilter" type="OptionButton" parent="MarginContainer/VBoxContainer/FilterRow"]
unique_name_in_owner = true
@ -89,7 +86,6 @@ theme_override_constants/separation = 8
[node name="EmptyLabel" type="Label" parent="MarginContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "No past games found."
theme_override_font_sizes/font_size = 18
theme_override_colors/font_color = Color(0.5, 0.5, 0.5, 1)
horizontal_alignment = 1
@ -103,4 +99,3 @@ alignment = 2
unique_name_in_owner = true
layout_mode = 2
custom_minimum_size = Vector2(140, 44)
text = "Back"