diff --git a/public/games/age-of-dwarves/vocabulary.json b/public/games/age-of-dwarves/vocabulary.json index 656dac76..a5475188 100644 --- a/public/games/age-of-dwarves/vocabulary.json +++ b/public/games/age-of-dwarves/vocabulary.json @@ -335,6 +335,42 @@ "replay_title": "Replay Viewer", "replay_placeholder": "World map renderer (wired when GdReplayPlayer bridge lands)", "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_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.", diff --git a/src/game/engine/scenes/city/great_person_modal.gd b/src/game/engine/scenes/city/great_person_modal.gd index 0941c788..fa754ef4 100644 --- a/src/game/engine/scenes/city/great_person_modal.gd +++ b/src/game/engine/scenes/city/great_person_modal.gd @@ -109,7 +109,7 @@ func rebuild() -> void: add_child(v) 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) v.add_child(title) @@ -117,21 +117,21 @@ func rebuild() -> void: if _gp_action != null and _gp_action.has_method("action_type"): at = String(_gp_action.action_type()) 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) var status: Label = Label.new() if _validation.is_empty(): - status.text = "Choose a target." + status.text = ThemeVocabulary.lookup("great_person_choose_target") elif bool(_validation.get("ok", false)): - status.text = "OK" + status.text = ThemeVocabulary.lookup("great_person_ok") 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"): - status.text = "OK — +%d production" % int(_validation["production"]) + status.text = ThemeVocabulary.lookup("fmt_great_person_ok_production") % int(_validation["production"]) else: 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) _last_snapshot = { diff --git a/src/game/engine/scenes/city/merge_panel.gd b/src/game/engine/scenes/city/merge_panel.gd index a5434abe..634cfc7f 100644 --- a/src/game/engine/scenes/city/merge_panel.gd +++ b/src/game/engine/scenes/city/merge_panel.gd @@ -56,7 +56,7 @@ func populate( _clear_rows() if not _city.is_extension_available(): - _error_label.text = "GdGameState extension not loaded." + _error_label.text = ThemeVocabulary.lookup("merge_no_extension") _error_label.show() return @@ -121,7 +121,7 @@ func _on_confirm_pressed() -> void: # Fetch the hybrid BuildingDef JSON from DataLoader. var hybrid_def: Dictionary = DataLoader.get_building(into) 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() return var hybrid_json: String = JSON.stringify(hybrid_def) diff --git a/src/game/engine/scenes/city/specialists_drag_panel.gd b/src/game/engine/scenes/city/specialists_drag_panel.gd index 1985dd37..9d985678 100644 --- a/src/game/engine/scenes/city/specialists_drag_panel.gd +++ b/src/game/engine/scenes/city/specialists_drag_panel.gd @@ -179,7 +179,7 @@ func _make_slot_row(row: Dictionary) -> Control: func _make_unassigned_row() -> Control: var row: HBoxContainer = HBoxContainer.new() var label: Label = Label.new() - label.text = "Idle citizens:" + label.text = ThemeVocabulary.lookup("specialists_idle_citizens") label.custom_minimum_size = Vector2(220, 0) row.add_child(label) if _unassigned.is_empty(): diff --git a/src/game/engine/scenes/city/throne_room_great_works.gd b/src/game/engine/scenes/city/throne_room_great_works.gd index eb0253fd..d5ded915 100644 --- a/src/game/engine/scenes/city/throne_room_great_works.gd +++ b/src/game/engine/scenes/city/throne_room_great_works.gd @@ -118,13 +118,13 @@ func _make_layer_section(layer: String, rows: Array) -> Control: if rows.is_empty(): var empty: Label = Label.new() - empty.text = " (empty)" + empty.text = ThemeVocabulary.lookup("great_works_empty") box.add_child(empty) return box for r: Dictionary in rows: var line: Label = Label.new() - line.text = " • %s — %s (era %d)" % [ + line.text = ThemeVocabulary.lookup("fmt_great_works_entry") % [ String(r["name"]), String(r["building"]), int(r["era"]), diff --git a/src/game/engine/scenes/hud/intelligence_log_panel.gd b/src/game/engine/scenes/hud/intelligence_log_panel.gd index 0b97e536..77dbc619 100644 --- a/src/game/engine/scenes/hud/intelligence_log_panel.gd +++ b/src/game/engine/scenes/hud/intelligence_log_panel.gd @@ -76,7 +76,7 @@ func _build_ui() -> void: _scroll.add_child(_vbox) _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.theme_type_variation = "LabelMuted" _body.add_child(_empty_label) diff --git a/src/game/engine/scenes/knowledge_tree/knowledge_tree.gd b/src/game/engine/scenes/knowledge_tree/knowledge_tree.gd index 57f6af51..cc9bcd49 100644 --- a/src/game/engine/scenes/knowledge_tree/knowledge_tree.gd +++ b/src/game/engine/scenes/knowledge_tree/knowledge_tree.gd @@ -451,7 +451,7 @@ func _create_node_card( if tier > 0: 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) top_row.add_child(tier_label) diff --git a/src/game/engine/scenes/menus/credits.gd b/src/game/engine/scenes/menus/credits.gd index ec151cc3..023685aa 100644 --- a/src/game/engine/scenes/menus/credits.gd +++ b/src/game/engine/scenes/menus/credits.gd @@ -208,7 +208,7 @@ func _build_entry_row(entry: Dictionary) -> Control: rich.add_theme_font_size_override("normal_font_size", 13) rich.add_theme_color_override("default_color", ThemeAssets.color("text.primary")) # 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))) return rich diff --git a/src/game/engine/scenes/menus/game_setup.gd b/src/game/engine/scenes/menus/game_setup.gd index ab633405..c285b704 100644 --- a/src/game/engine/scenes/menus/game_setup.gd +++ b/src/game/engine/scenes/menus/game_setup.gd @@ -274,7 +274,7 @@ func _make_controller_row(slot_idx: int) -> Control: var row: HBoxContainer = HBoxContainer.new() row.add_theme_constant_override("separation", 8) 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.theme_type_variation = "LabelSecondary" label.custom_minimum_size = Vector2(80, 0) diff --git a/src/game/engine/scenes/menus/past_games.gd b/src/game/engine/scenes/menus/past_games.gd index 95c5faaf..5790a57e 100644 --- a/src/game/engine/scenes/menus/past_games.gd +++ b/src/game/engine/scenes/menus/past_games.gd @@ -102,16 +102,16 @@ func _make_card(entry: Dictionary) -> PanelContainer: var title: String = entry.get("title", "Unnamed Game") var turn: int = entry.get("final_turn", 0) 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) 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)) hbox.add_child(watch_btn) 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)) hbox.add_child(delete_btn) diff --git a/src/game/engine/scenes/menus/replay_viewer.gd b/src/game/engine/scenes/menus/replay_viewer.gd index c4273933..f01f59de 100644 --- a/src/game/engine/scenes/menus/replay_viewer.gd +++ b/src/game/engine/scenes/menus/replay_viewer.gd @@ -94,7 +94,7 @@ func _process(delta: float) -> void: var next: int = _current_turn + 1 if next > _final_turn: _playing = false - _play_pause_button.text = "Play" + _play_pause_button.text = ThemeVocabulary.lookup("replay_play") return _goto_turn(next) @@ -143,7 +143,7 @@ func _resolve_archive_root() -> String: 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 @@ -153,7 +153,10 @@ func _on_scrubber_changed(value: float) -> void: func _on_play_pause_pressed() -> void: _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 @@ -165,7 +168,7 @@ func _on_speed_set(speed: float) -> void: func _on_step_pressed() -> void: if _playing: _playing = false - _play_pause_button.text = "Play" + _play_pause_button.text = ThemeVocabulary.lookup("replay_play") _goto_turn(_current_turn + 1) diff --git a/src/game/engine/scenes/ui/lens_switcher.gd b/src/game/engine/scenes/ui/lens_switcher.gd index 27758541..37c6d661 100644 --- a/src/game/engine/scenes/ui/lens_switcher.gd +++ b/src/game/engine/scenes/ui/lens_switcher.gd @@ -215,7 +215,7 @@ func _build_buttons() -> void: var btn_none: Button = Button.new() btn_none.name = "BtnNone" 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("")) row.add_child(btn_none) _buttons_by_id[""] = btn_none diff --git a/src/game/engine/scenes/ui/ransom_offers.gd b/src/game/engine/scenes/ui/ransom_offers.gd index a6cb9449..6c4520ac 100644 --- a/src/game/engine/scenes/ui/ransom_offers.gd +++ b/src/game/engine/scenes/ui/ransom_offers.gd @@ -126,7 +126,7 @@ func _make_offer_row(offer: Dictionary) -> Control: ThemeVocabulary.lookup("ransom_button_defer"), _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