extends Node # UIStrings — loads UI display text from data/ui-strings.yaml. # Access: UIStrings.get_text("hud.mode_label") -> "Mode" # Falls back to the key itself if not found, with a push_warning. const STRINGS_PATH: String = "res://data/ui-strings.yaml" var _strings: Dictionary = {} var _loaded: bool = false func _ready() -> void: _load_strings() func _load_strings() -> void: if not FileAccess.file_exists(STRINGS_PATH): push_warning("UIStrings: file not found: %s" % STRINGS_PATH) return var file := FileAccess.open(STRINGS_PATH, FileAccess.READ) if file == null: push_warning("UIStrings: failed to open: %s" % STRINGS_PATH) return var text := file.get_as_text() file.close() _strings = _parse_yaml(text) _loaded = true print("UIStrings: loaded %d strings from %s" % [_strings.size(), STRINGS_PATH]) ## Get a UI string by dotted key. Returns the key itself if not found. func get_text(key: String) -> String: if _strings.has(key): return _strings[key] push_warning("UIStrings: missing key '%s'" % key) return key ## Check if a key exists. func has_key(key: String) -> bool: return _strings.has(key) ## Get all keys. func get_all_keys() -> PackedStringArray: return PackedStringArray(_strings.keys()) ## Reload from disk (useful for hot-reload during development). func reload() -> void: _strings.clear() _loaded = false _load_strings() ## Parse YAML with arbitrary nesting depth. ## Returns flat Dictionary with dotted keys: { "section.sub.key": "value" }. static func _parse_yaml(text: String) -> Dictionary: var strings := {} var stack: Array = [] # [[indent, key], ...] for line in text.split("\n"): var stripped := line.strip_edges(false, true) if stripped.is_empty() or stripped.begins_with("#"): continue var indent := line.length() - line.lstrip(" ").length() var content := stripped.strip_edges() var colon_pos := content.find(":") if colon_pos < 0: continue var key := content.substr(0, colon_pos).strip_edges() var val := content.substr(colon_pos + 1).strip_edges() # Trailing comment without a value — treat as section header if val.begins_with("#"): val = "" # Pop sections at same or deeper indent while stack.size() > 0 and stack.back()[0] >= indent: stack.pop_back() if val.is_empty(): # Section header — push onto stack stack.push_back([indent, key]) else: # Leaf value — extract from quotes or strip inline comment if val.begins_with("\""): var end_quote := val.find("\"", 1) if end_quote > 0: val = val.substr(1, end_quote - 1) else: val = val.substr(1) else: var comment_pos := val.find(" #") if comment_pos >= 0: val = val.substr(0, comment_pos).strip_edges() var dotted_key := "" for entry in stack: dotted_key += entry[1] + "." dotted_key += key strings[dotted_key] = val return strings