hot-reloading localization CSVs in Godot 4 without restarting the editor — workaround that mostly works

367 views 0 replies

If you're doing any active text iteration (working with a writer, localizing to multiple languages, just tweaking UI strings), the default Godot 4 workflow is genuinely painful. Edit your CSV, switch to the editor, wait for the reimport scan, restart the scene. Repeat.

I spent an afternoon writing an EditorPlugin that polls the locale directory and force-reloads translations when files change on disk. The key part is bypassing Godot's resource cache, since load(path) returns the cached version even after the file changes, so you have to go through CACHE_MODE_BYPASS:

@tool
extends EditorPlugin

const LOCALE_DIR := "res://locales/"
const POLL_INTERVAL := 1.5

var _mtimes: Dictionary = {}
var _elapsed: float = 0.0

func _enter_tree() -> void:
    _scan()

func _process(delta: float) -> void:
    _elapsed += delta
    if _elapsed < POLL_INTERVAL:
        return
    _elapsed = 0.0
    _check()

func _scan() -> void:
    var dir := DirAccess.open(LOCALE_DIR)
    if not dir:
        return
    for f in dir.get_files():
        if f.ends_with(".csv"):
            _mtimes[LOCALE_DIR + f] = FileAccess.get_modified_time(LOCALE_DIR + f)

func _check() -> void:
    var dirty := false
    for path in _mtimes:
        var t := FileAccess.get_modified_time(path)
        if t != _mtimes[path]:
            _mtimes[path] = t
            dirty = true
    if dirty:
        _reload()

func _reload() -> void:
    var paths: Array = ProjectSettings.get_setting(
        "internationalization/locale/translations", []
    )
    for path in paths:
        var tr := ResourceLoader.load(
            path, "", ResourceLoader.CACHE_MODE_BYPASS
        ) as Translation
        if tr:
            TranslationServer.add_translation(tr)
    print("[LocaleReload] reloaded ", Time.get_time_string_from_system())

Known issue: add_translation() doesn't replace the old entry, it stacks on top. If you delete a key from the CSV mid-session it'll still surface from the stale in-memory translation until you restart. I haven't found a clean fix. TranslationServer doesn't expose a "clear and reload" method. Holding onto the old translation object to call remove_translation() before re-adding works in theory but gets messy fast once you have multiple locale files.

For our actual workflow (adding and editing strings, rarely deleting mid-session) it's been fine. Curious if anyone's solved the deletion case cleanly, or found a better hook than polling. I looked at EditorFileSystem signals but couldn't get reliable change events for files outside the normal reimport scan path.

Moonjump
Forum Search Shader Sandbox
Sign In Register