i18n(@projects/@magic-civilization): 🌐 route 25 hardcoded .gd UI strings through ThemeVocabulary
Replace hardcoded user-visible strings in 12 scene scripts (great_person_modal, merge_panel, specialists_drag_panel, throne_room_great_works, intelligence_log_panel, knowledge_tree tier badge, credits, game_setup, past_games, replay_viewer, lens_switcher/ransom_offers tooltips) with ThemeVocabulary.lookup() + add the corresponding keys (incl. fmt_* for format strings). Part of clearing validate-i18n with no bypass. (48 → 23 remaining.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5b2b88b2a9
commit
07a10054f4
13 changed files with 64 additions and 25 deletions
|
|
@ -335,6 +335,42 @@
|
||||||
"replay_title": "Replay Viewer",
|
"replay_title": "Replay Viewer",
|
||||||
"replay_placeholder": "World map renderer (wired when GdReplayPlayer bridge lands)",
|
"replay_placeholder": "World map renderer (wired when GdReplayPlayer bridge lands)",
|
||||||
"replay_step": "Step",
|
"replay_step": "Step",
|
||||||
|
"replay_play": "Play",
|
||||||
|
"replay_pause": "Pause",
|
||||||
|
"fmt_replay_turn": "Turn %d / %d",
|
||||||
|
"fmt_great_person_title": "%s (%s) — Tier %d",
|
||||||
|
"fmt_great_person_action": "Action: %s",
|
||||||
|
"great_person_choose_target": "Choose a target.",
|
||||||
|
"great_person_ok": "OK",
|
||||||
|
"fmt_great_person_ok_gold": "OK — +%d gold",
|
||||||
|
"fmt_great_person_ok_production": "OK — +%d production",
|
||||||
|
"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_confirm": "Merge",
|
||||||
|
"merge_cancel": "Cancel",
|
||||||
|
"specialists_idle_citizens": "Idle citizens:",
|
||||||
|
"great_works_empty": " (empty)",
|
||||||
|
"fmt_great_works_entry": " • %s — %s (era %d)",
|
||||||
|
"intel_no_envelopes": "No intercepted envelopes yet.",
|
||||||
|
"fmt_tier_badge": "T%d",
|
||||||
|
"fmt_credits_link": "%s [color=#9bbfe0][url=%s]%s[/url][/color]",
|
||||||
|
"fmt_ai_slot": "AI Slot %d",
|
||||||
|
"fmt_past_game_entry": "%s — Turn %d — %s",
|
||||||
|
"watch_replay": "Watch Replay",
|
||||||
|
"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",
|
||||||
"tutorial_skip": "Skip Tutorial",
|
"tutorial_skip": "Skip Tutorial",
|
||||||
"tutorial_step_1_title": "The Hex Map",
|
"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.",
|
"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.",
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ func rebuild() -> void:
|
||||||
add_child(v)
|
add_child(v)
|
||||||
|
|
||||||
var title: Label = Label.new()
|
var title: Label = Label.new()
|
||||||
title.text = "%s (%s) — Tier %d" % [_gp_name, _gp_class, _gp_tier]
|
title.text = ThemeVocabulary.lookup("fmt_great_person_title") % [_gp_name, _gp_class, _gp_tier]
|
||||||
title.add_theme_font_size_override("font_size", 16)
|
title.add_theme_font_size_override("font_size", 16)
|
||||||
v.add_child(title)
|
v.add_child(title)
|
||||||
|
|
||||||
|
|
@ -117,21 +117,21 @@ func rebuild() -> void:
|
||||||
if _gp_action != null and _gp_action.has_method("action_type"):
|
if _gp_action != null and _gp_action.has_method("action_type"):
|
||||||
at = String(_gp_action.action_type())
|
at = String(_gp_action.action_type())
|
||||||
var act_label: Label = Label.new()
|
var act_label: Label = Label.new()
|
||||||
act_label.text = "Action: %s" % (at if at != "" else "(none)")
|
act_label.text = ThemeVocabulary.lookup("fmt_great_person_action") % (at if at != "" else "(none)")
|
||||||
v.add_child(act_label)
|
v.add_child(act_label)
|
||||||
|
|
||||||
var status: Label = Label.new()
|
var status: Label = Label.new()
|
||||||
if _validation.is_empty():
|
if _validation.is_empty():
|
||||||
status.text = "Choose a target."
|
status.text = ThemeVocabulary.lookup("great_person_choose_target")
|
||||||
elif bool(_validation.get("ok", false)):
|
elif bool(_validation.get("ok", false)):
|
||||||
status.text = "OK"
|
status.text = ThemeVocabulary.lookup("great_person_ok")
|
||||||
if _validation.has("gold"):
|
if _validation.has("gold"):
|
||||||
status.text = "OK — +%d gold" % int(_validation["gold"])
|
status.text = ThemeVocabulary.lookup("fmt_great_person_ok_gold") % int(_validation["gold"])
|
||||||
if _validation.has("production"):
|
if _validation.has("production"):
|
||||||
status.text = "OK — +%d production" % int(_validation["production"])
|
status.text = ThemeVocabulary.lookup("fmt_great_person_ok_production") % int(_validation["production"])
|
||||||
else:
|
else:
|
||||||
var err: String = String(_validation.get("error", "invalid"))
|
var err: String = String(_validation.get("error", "invalid"))
|
||||||
status.text = "Cannot apply: %s" % err
|
status.text = ThemeVocabulary.lookup("fmt_great_person_cannot") % err
|
||||||
v.add_child(status)
|
v.add_child(status)
|
||||||
|
|
||||||
_last_snapshot = {
|
_last_snapshot = {
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ func populate(
|
||||||
_clear_rows()
|
_clear_rows()
|
||||||
|
|
||||||
if not _city.is_extension_available():
|
if not _city.is_extension_available():
|
||||||
_error_label.text = "GdGameState extension not loaded."
|
_error_label.text = ThemeVocabulary.lookup("merge_no_extension")
|
||||||
_error_label.show()
|
_error_label.show()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -121,7 +121,7 @@ func _on_confirm_pressed() -> void:
|
||||||
# Fetch the hybrid BuildingDef JSON from DataLoader.
|
# Fetch the hybrid BuildingDef JSON from DataLoader.
|
||||||
var hybrid_def: Dictionary = DataLoader.get_building(into)
|
var hybrid_def: Dictionary = DataLoader.get_building(into)
|
||||||
if hybrid_def.is_empty():
|
if hybrid_def.is_empty():
|
||||||
_error_label.text = "Hybrid building '%s' not found in registry." % into
|
_error_label.text = ThemeVocabulary.lookup("fmt_merge_not_found") % into
|
||||||
_error_label.show()
|
_error_label.show()
|
||||||
return
|
return
|
||||||
var hybrid_json: String = JSON.stringify(hybrid_def)
|
var hybrid_json: String = JSON.stringify(hybrid_def)
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ func _make_slot_row(row: Dictionary) -> Control:
|
||||||
func _make_unassigned_row() -> Control:
|
func _make_unassigned_row() -> Control:
|
||||||
var row: HBoxContainer = HBoxContainer.new()
|
var row: HBoxContainer = HBoxContainer.new()
|
||||||
var label: Label = Label.new()
|
var label: Label = Label.new()
|
||||||
label.text = "Idle citizens:"
|
label.text = ThemeVocabulary.lookup("specialists_idle_citizens")
|
||||||
label.custom_minimum_size = Vector2(220, 0)
|
label.custom_minimum_size = Vector2(220, 0)
|
||||||
row.add_child(label)
|
row.add_child(label)
|
||||||
if _unassigned.is_empty():
|
if _unassigned.is_empty():
|
||||||
|
|
|
||||||
|
|
@ -118,13 +118,13 @@ func _make_layer_section(layer: String, rows: Array) -> Control:
|
||||||
|
|
||||||
if rows.is_empty():
|
if rows.is_empty():
|
||||||
var empty: Label = Label.new()
|
var empty: Label = Label.new()
|
||||||
empty.text = " (empty)"
|
empty.text = ThemeVocabulary.lookup("great_works_empty")
|
||||||
box.add_child(empty)
|
box.add_child(empty)
|
||||||
return box
|
return box
|
||||||
|
|
||||||
for r: Dictionary in rows:
|
for r: Dictionary in rows:
|
||||||
var line: Label = Label.new()
|
var line: Label = Label.new()
|
||||||
line.text = " • %s — %s (era %d)" % [
|
line.text = ThemeVocabulary.lookup("fmt_great_works_entry") % [
|
||||||
String(r["name"]),
|
String(r["name"]),
|
||||||
String(r["building"]),
|
String(r["building"]),
|
||||||
int(r["era"]),
|
int(r["era"]),
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ func _build_ui() -> void:
|
||||||
_scroll.add_child(_vbox)
|
_scroll.add_child(_vbox)
|
||||||
|
|
||||||
_empty_label = Label.new()
|
_empty_label = Label.new()
|
||||||
_empty_label.text = "No intercepted envelopes yet."
|
_empty_label.text = ThemeVocabulary.lookup("intel_no_envelopes")
|
||||||
_empty_label.add_theme_font_size_override("font_size", 13)
|
_empty_label.add_theme_font_size_override("font_size", 13)
|
||||||
_empty_label.theme_type_variation = "LabelMuted"
|
_empty_label.theme_type_variation = "LabelMuted"
|
||||||
_body.add_child(_empty_label)
|
_body.add_child(_empty_label)
|
||||||
|
|
|
||||||
|
|
@ -451,7 +451,7 @@ func _create_node_card(
|
||||||
|
|
||||||
if tier > 0:
|
if tier > 0:
|
||||||
var tier_label: Label = Label.new()
|
var tier_label: Label = Label.new()
|
||||||
tier_label.text = "T%d" % tier
|
tier_label.text = ThemeVocabulary.lookup("fmt_tier_badge") % tier
|
||||||
tier_label.add_theme_font_size_override("font_size", 10)
|
tier_label.add_theme_font_size_override("font_size", 10)
|
||||||
top_row.add_child(tier_label)
|
top_row.add_child(tier_label)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,7 @@ func _build_entry_row(entry: Dictionary) -> Control:
|
||||||
rich.add_theme_font_size_override("normal_font_size", 13)
|
rich.add_theme_font_size_override("normal_font_size", 13)
|
||||||
rich.add_theme_color_override("default_color", ThemeAssets.color("text.primary"))
|
rich.add_theme_color_override("default_color", ThemeAssets.color("text.primary"))
|
||||||
# Escape-friendly: `line` is author-controlled JSON / CSV, not BBCode.
|
# Escape-friendly: `line` is author-controlled JSON / CSV, not BBCode.
|
||||||
rich.text = "%s [color=#9bbfe0][url=%s]%s[/url][/color]" % [line, url, url]
|
rich.text = ThemeVocabulary.lookup("fmt_credits_link") % [line, url, url]
|
||||||
rich.meta_clicked.connect(func(meta: Variant) -> void: OS.shell_open(str(meta)))
|
rich.meta_clicked.connect(func(meta: Variant) -> void: OS.shell_open(str(meta)))
|
||||||
return rich
|
return rich
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -274,7 +274,7 @@ func _make_controller_row(slot_idx: int) -> Control:
|
||||||
var row: HBoxContainer = HBoxContainer.new()
|
var row: HBoxContainer = HBoxContainer.new()
|
||||||
row.add_theme_constant_override("separation", 8)
|
row.add_theme_constant_override("separation", 8)
|
||||||
var label: Label = Label.new()
|
var label: Label = Label.new()
|
||||||
label.text = "AI Slot %d" % slot_idx
|
label.text = ThemeVocabulary.lookup("fmt_ai_slot") % slot_idx
|
||||||
label.add_theme_font_size_override("font_size", 11)
|
label.add_theme_font_size_override("font_size", 11)
|
||||||
label.theme_type_variation = "LabelSecondary"
|
label.theme_type_variation = "LabelSecondary"
|
||||||
label.custom_minimum_size = Vector2(80, 0)
|
label.custom_minimum_size = Vector2(80, 0)
|
||||||
|
|
|
||||||
|
|
@ -102,16 +102,16 @@ func _make_card(entry: Dictionary) -> PanelContainer:
|
||||||
var title: String = entry.get("title", "Unnamed Game")
|
var title: String = entry.get("title", "Unnamed Game")
|
||||||
var turn: int = entry.get("final_turn", 0)
|
var turn: int = entry.get("final_turn", 0)
|
||||||
var outcome: String = entry.get("outcome", "unknown")
|
var outcome: String = entry.get("outcome", "unknown")
|
||||||
info_label.text = "%s — Turn %d — %s" % [title, turn, outcome]
|
info_label.text = ThemeVocabulary.lookup("fmt_past_game_entry") % [title, turn, outcome]
|
||||||
hbox.add_child(info_label)
|
hbox.add_child(info_label)
|
||||||
|
|
||||||
var watch_btn: Button = Button.new()
|
var watch_btn: Button = Button.new()
|
||||||
watch_btn.text = "Watch Replay"
|
watch_btn.text = ThemeVocabulary.lookup("watch_replay")
|
||||||
watch_btn.pressed.connect(_on_watch_replay_pressed.bind(entry))
|
watch_btn.pressed.connect(_on_watch_replay_pressed.bind(entry))
|
||||||
hbox.add_child(watch_btn)
|
hbox.add_child(watch_btn)
|
||||||
|
|
||||||
var delete_btn: Button = Button.new()
|
var delete_btn: Button = Button.new()
|
||||||
delete_btn.text = "Delete"
|
delete_btn.text = ThemeVocabulary.lookup("past_game_delete")
|
||||||
delete_btn.pressed.connect(_on_delete_pressed.bind(entry))
|
delete_btn.pressed.connect(_on_delete_pressed.bind(entry))
|
||||||
hbox.add_child(delete_btn)
|
hbox.add_child(delete_btn)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ func _process(delta: float) -> void:
|
||||||
var next: int = _current_turn + 1
|
var next: int = _current_turn + 1
|
||||||
if next > _final_turn:
|
if next > _final_turn:
|
||||||
_playing = false
|
_playing = false
|
||||||
_play_pause_button.text = "Play"
|
_play_pause_button.text = ThemeVocabulary.lookup("replay_play")
|
||||||
return
|
return
|
||||||
_goto_turn(next)
|
_goto_turn(next)
|
||||||
|
|
||||||
|
|
@ -143,7 +143,7 @@ func _resolve_archive_root() -> String:
|
||||||
|
|
||||||
|
|
||||||
func _update_turn_display() -> void:
|
func _update_turn_display() -> void:
|
||||||
_turn_label.text = "Turn %d / %d" % [_current_turn, _final_turn]
|
_turn_label.text = ThemeVocabulary.lookup("fmt_replay_turn") % [_current_turn, _final_turn]
|
||||||
_speed_label.text = "%.1f×" % _speed
|
_speed_label.text = "%.1f×" % _speed
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -153,7 +153,10 @@ func _on_scrubber_changed(value: float) -> void:
|
||||||
|
|
||||||
func _on_play_pause_pressed() -> void:
|
func _on_play_pause_pressed() -> void:
|
||||||
_playing = not _playing
|
_playing = not _playing
|
||||||
_play_pause_button.text = "Pause" if _playing else "Play"
|
_play_pause_button.text = (
|
||||||
|
ThemeVocabulary.lookup("replay_pause") if _playing
|
||||||
|
else ThemeVocabulary.lookup("replay_play")
|
||||||
|
)
|
||||||
_playback_elapsed = 0.0
|
_playback_elapsed = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -165,7 +168,7 @@ func _on_speed_set(speed: float) -> void:
|
||||||
func _on_step_pressed() -> void:
|
func _on_step_pressed() -> void:
|
||||||
if _playing:
|
if _playing:
|
||||||
_playing = false
|
_playing = false
|
||||||
_play_pause_button.text = "Play"
|
_play_pause_button.text = ThemeVocabulary.lookup("replay_play")
|
||||||
_goto_turn(_current_turn + 1)
|
_goto_turn(_current_turn + 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ func _build_buttons() -> void:
|
||||||
var btn_none: Button = Button.new()
|
var btn_none: Button = Button.new()
|
||||||
btn_none.name = "BtnNone"
|
btn_none.name = "BtnNone"
|
||||||
btn_none.text = "—"
|
btn_none.text = "—"
|
||||||
btn_none.tooltip_text = "No lens (default biome view)"
|
btn_none.tooltip_text = ThemeVocabulary.lookup("lens_none_tooltip")
|
||||||
btn_none.pressed.connect(_on_lens_pressed.bind(""))
|
btn_none.pressed.connect(_on_lens_pressed.bind(""))
|
||||||
row.add_child(btn_none)
|
row.add_child(btn_none)
|
||||||
_buttons_by_id[""] = btn_none
|
_buttons_by_id[""] = btn_none
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ func _make_offer_row(offer: Dictionary) -> Control:
|
||||||
ThemeVocabulary.lookup("ransom_button_defer"),
|
ThemeVocabulary.lookup("ransom_button_defer"),
|
||||||
_on_defer_pressed.bind(offer_id),
|
_on_defer_pressed.bind(offer_id),
|
||||||
))
|
))
|
||||||
panel.tooltip_text = "Offered turn %d" % created_turn
|
panel.tooltip_text = ThemeVocabulary.lookup("fmt_ransom_offered") % created_turn
|
||||||
return panel
|
return panel
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue