Complete GUI localization coverage and add headless contracts - #1527
Complete GUI localization coverage and add headless contracts#1527francescofugazzi wants to merge 52 commits into
Conversation
Replace hardcoded user-facing labels across the viewport overlays, rendering and sequencer panels, window controls, camera toolbar, editor context menus, console, marketplace, and downloader with localization lookups. Add the corresponding English keys and language-specific values for all shipped locales while preserving technical identifiers and format names where appropriate. Extend the locale completeness checker to reject multiple JSON keys on one physical line, then normalize all reported locale files. Validation: locale completeness check, JSON parsing, and git diff check passed.
Move dynamic configuration, export, conversion, checkpoint, RAD LOD, scene history, CLI logging, filtering, clipboard, and log export messages into localization keys. Preserve formatting placeholders across all shipped locales and add language-specific translations for the Scene and runtime feedback strings. Apply clang-format to the modified C++ files and validate locale completeness, JSON parsing, placeholder consistency, and repository whitespace.
Move fixed import, export, video export, mesh-to-splat, and splat simplification progress states out of async task manager literals and into the runtime localization catalog. Add typed runtime keys and provide language-specific translations for initialization, start, completion, failure, cancellation, and apply states across all supported locale files.
Move asynchronous task progress states, export and video status messages, COLMAP prerequisites, rendering failures, import failures, and training fallback errors into the runtime localization catalog. Add translated entries for every supported locale and keep the typed string key registry aligned with the runtime catalog.
Move remaining asynchronous task, CUDA compatibility, modal, and Python console feedback through the localization catalog with translations for every shipped locale. Add a recursive UI hardcoded-text audit with exact and regex allowlist support, plus a reviewed baseline for backend diagnostics and canonical asset names.
Move Python Console, scene graph, sequencer, rendering, image preview, video export, and HUD strings to locale keys across every supported language. Improve the UI hardcoding audit to preserve UTF-8 symbols, ignore encoded graphical literals, and retain reviewed internal diagnostics in the allowlist. The audit now reports no likely hardcoded UI strings.
Keep the sequencer distance delta in meters with the standard m symbol instead of translating the unit name.
Localize remaining viewport, depth, export, marketplace, asset manager, sequencer, rendering and event messages across every shipped locale. Refresh RML resources after a language change at a safe UI boundary and make cached toolbar, selection, transform, GT comparison, import and video overlay state depend on language_generation so labels and tooltips no longer require an interaction to update. Replace unsafe localized std::format call sites with runtime formatLocalized support, fix Python marketplace interpolation and downloader startup handling, and add missing rendering section labels including translated LOD headings. Extend the hardcoded UI scanner to cover Python UI bindings while excluding reviewed dynamic formatting patterns; retain the remaining import watcher strings as explicit candidates for the next localization pass.
Document the canonical locale completeness check, including its one-key-per-line JSON formatting requirement. Add contributor guidance for the hardcoded UI scanner, reviewed allowlist exceptions, fail-on-candidates enforcement, and language-generation cache invalidation.
Add a CMake/CTest entry point for deterministic localization validation without building test binaries.\n\nCover locale key and placeholder parity, JSON formatting, literal localization references, RML directives, hardcoded UI audit output, localized scan-message formatting, and language-generation cache invalidation.\n\nLocalize remaining import-panel scan statuses and Asset Manager URL errors across shipped locales, reuse existing console status keys, tighten the hardcoded UI scanner, and document the target in the build guide.
MrNeRF
left a comment
There was a problem hiding this comment.
Thanks for this huge effort — this is a big and genuinely useful pass. The locale data itself is in really good shape: all 10 files have exact key parity with en.json, zero placeholder mismatches, correct terminology in ja/ko/zh, and the deferred-refresh idea is the right approach. Dutch and Italian are particularly strong.
A few things need fixing before merge (details in the inline comments):
Must fix
- Task stage strings are now translated, but
src/app/mcp_runtime_tools.cppandexport_panel.pystill compare against English"Complete"/"Failed"/"Cancelled"— in a non-English session finished jobs report as idle and exports never register in the Asset Manager. Stage should stay a stable token, translated only for display. LocalizationManagerhas no locking but is now called from worker threads (exports, video encode, folder scans) — a language switch during an export can crash.LOCFis a barestd::vformat: one broken placeholder in a locale file (or vialf.ui.loc_set) throws, and two call sites sit inside catch blocks on worker threads, which terminates the app.- The new contract test spawns
pythonand fails on python3-only machines (sys.executable). selection_controls.pytrackslanguage_generationbut never dirties its labels — the stale-language bug stays in that panel.- The startup overlay language picker doesn't trigger the new deferred refresh — first-run users keep an English UI until restart.
- Neither new check runs in CI yet — wiring
check_ui_hardcodedandLocalizationContractsinto the workflows would let them actually guard.
Also worth a pass
- de/fr/es still have 25
runtime.*values in plain English (it has them all translated — nice reference). A few German strings lost umlauts:"Schluesselbild","Alle loeschen"→"Keyframe","Alle löschen". - Small pluralization losses ("Showing 1 assets", "plugins"), and en.json
disk_space_dialog.checkpoint_save_failed="Checkpoint Save Failed (Iteration"is a fragment —"… (Iteration {})"would make it translatable. - The scanner's clean result is mostly it being too forgiving (single words always skipped) — see inline notes; still valuable as a review queue.
- Not from this PR: the Ubuntu CI failures are SDL3/vcpkg infrastructure — a re-run should clear them.
With the stage-token, threading and format-fallback issues sorted, this will be a great step for the app. Thanks again!
| { | ||
| const std::lock_guard lock(export_state_.mutex); | ||
| export_state_.stage = "Complete"; | ||
| export_state_.stage = LOC(lichtfeld::Strings::Runtime::TASK_COMPLETE); |
There was a problem hiding this comment.
These stage values are also read as plain English elsewhere: mcp_runtime_tools.cpp checks stage == "Complete" / "Failed" / "Cancelled", and export_panel.py:758 checks "Complete" before registering the export. Once translated (de: "Abgeschlossen"), non-English sessions never see a job as finished. Suggest keeping stage as a fixed token and translating only at display time.
| } | ||
| } catch (const std::exception& e) { | ||
| error_msg = std::string("COLMAP export crashed with exception: ") + e.what(); | ||
| error_msg = LOCF(lichtfeld::Strings::Runtime::TASK_FAILED_DETAIL, e.what()); |
There was a problem hiding this comment.
Two risks here: this runs in a catch on a worker thread, and LOCF is a bare std::vformat that throws on a broken placeholder — an uncaught throw here ends the process. Also LocalizationManager has no locking, so a language switch during an export races with these lookups.
| #define LOC(key) lfs::event::LocalizationManager::getInstance().get(key) | ||
| template <typename... Args> | ||
| [[nodiscard]] inline std::string formatLocalized(const std::string_view key, Args&&... args) { | ||
| return std::vformat(LocalizationManager::getInstance().get(key), std::make_format_args(args...)); |
There was a problem hiding this comment.
std::vformat throws std::format_error if a translation has a bad placeholder (and plugins can set arbitrary strings via lf.ui.loc_set). A try/catch falling back to the English text would make bad locale data harmless instead of fatal. Same applies to the fmt::runtime(LOC(...)) site in rml_sequencer_overlay.cpp.
|
|
||
|
|
||
| def test_hardcoded_ui_audit_has_no_candidates(): | ||
| result = subprocess.run(["python", str(ROOT / "tools" / "check_ui_hardcoded.py")], |
There was a problem hiding this comment.
"python" doesn't exist on most Linux boxes (only python3), so this test fails with FileNotFoundError. sys.executable fixes it.
|
|
||
|
|
||
| def _fields(text): | ||
| return {name.split(".", 1)[0].split("[", 1)[0] for _, name, _, _ in string.Formatter().parse(text) if name} |
There was a problem hiding this comment.
Filtering on if name drops auto-numbered {} fields, so "Failed: {}" vs "Fehler: {} und {}" pass this check. check_locale_completeness.py keeps them — same approach would work here.
| "environment_mode": "main_panel.environment", | ||
| "environment_map_path": "main_panel.preset", | ||
| "environment_exposure": "main_panel.ppisp_exposure", | ||
| "environment_rotation_degrees": "transform.rotation", |
There was a problem hiding this comment.
In Chinese this key is 旋转: (fullwidth colon) and _entry_label only strips ASCII :, so the row renders as 旋转::.
| const std::string export_button = "Overwrite"; | ||
| lfs::core::ModalRequest request; | ||
| request.title = "Export COLMAP sparse"; | ||
| request.title = LOC(lichtfeld::Strings::Window::EXPORT); |
There was a problem hiding this comment.
The title became generic ("Export" instead of which export), while "Overwrite", "Cancel" and "This writes" in the same modal stay English — worth finishing this dialog in one go.
| with self._scan_state_lock: | ||
| while len(self._scan_log) <= index: | ||
| self._scan_log.append({"status": "Queued", "path": ""}) | ||
| self._scan_log.append({"status": _tr("watch_dirs.scan_log_queued"), "path": ""}) |
There was a problem hiding this comment.
_tr() is called per padded row while holding _scan_state_lock — hoisting it above the loop avoids repeated FFI calls under the lock.
| model.bind_func("depth_view_disable_label", lambda: "Disable Depth Map") | ||
| model.bind_func( | ||
| "depth_view_disable_label", | ||
| lambda: _ui_label("toolbar.depth_mode_disable", "Disable Depth Map"), |
There was a problem hiding this comment.
This borrows toolbar.depth_mode_disable ("Disable Depth Mode") for the Depth Map tool — a small mismatch, and it couples two unrelated buttons to one key.
| status = f"Downloading... {int(percent * 100)}% ({_format_bytes(downloaded)} / {_format_bytes(total_size)}) {speed_str}" | ||
| status = f"{lf.ui.tr('asset_manager.import_button_downloading')} {int(percent * 100)}% ({_format_bytes(downloaded)} / {_format_bytes(total_size)}) {speed_str}" | ||
| if eta_str: | ||
| status += f" ETA: {eta_str}" |
There was a problem hiding this comment.
The ETA: ... suffix (and the extraction messages below: "Extracting... {i}/{total} files", "Extraction complete", "(size unknown)") are still English right next to the newly translated part.
Use the active Python interpreter for nested contract commands and preserve automatic std::format placeholders during locale comparison. Make Selection Controls rebuild localized bindings when language_generation changes, allow localization contracts to coexist with regular and Unicode test configurations, and run the scanner plus headless contracts in the locale workflow. Expand the hardcoded UI audit to cover single-word UI text, f-strings, status/progress sinks, class labels, and RML title/placeholder attributes. Add focused audit fixtures and fix relative --root handling so newly exposed UI text cannot be hidden by permissive heuristics.
Separate machine-readable terminal outcomes from localized progress labels for scene export, dataset import, video export, and mesh-to-splat conversion. MCP task payloads now expose stable outcome tokens instead of inferring status from translated stage text, and the export panel uses the stable completion outcome. Synchronize localization reads, language switches, and overrides. Return thread-local translation copies so locale map replacement cannot invalidate strings in use, and make formatted localization fall back safely for malformed format strings. Localize the remaining VRAM HUD labels, GT normal mode, and URL download/extraction progress in every shipped locale. Add contracts preventing localized stage comparisons from returning to MCP task status handling.
…copy Teach the hardcoded UI scanner to recognize fallback labels supplied to _ui_label, _tr, and _trf alongside a localization key. These fallbacks preserve compatibility when a translation is unavailable and are not independent user-facing bypasses. Extend the scanner contract fixture so it verifies that a real hardcoded label is still reported while the equivalent declared localized fallback is excluded.
Move ui.normal into the color and depth option group in every locale, restore canonical four-space indentation, and localize viewport export status messages with formatted keys across all shipped languages. Extend locale validation to reject odd JSON key indentation and validate RML translation directives against every shipped locale, including non-empty translated values.
Route plugin installer progress updates through a safe localized formatter and add named-placeholder messages for downloads, environment creation, dependency synchronization, clone operations, and updates in every shipped locale. Teach the hardcoded UI audit to ignore only static panel class labels when the same file demonstrably refreshes that panel through a localized binding or set_panel_label call. This removes already-localized metadata without suppressing real UI strings.
Add optional localization keys to tool, submode, and pivot definitions. Their labels are resolved only when serializing metadata for the UI, so builtin tools retain stable identifiers and update with the active language instead of translating during module import. Localize all builtin tool names and the Python Scripts panel title in every shipped locale. Strengthen the UI hardcode audit with AST-based docstring exclusion and recognition of label/key declaration pairs, retaining reports for labels without an associated localization key.
Record the remaining scanner exclusions as narrow, commented exceptions for Python outline formatting, language-server implementation identifiers, canonical shortcut and URL syntax, renderer backend names, and a CSS class token. The hardcoded UI audit now completes with zero candidates while retaining coverage for user-facing literals.
…strings Replace the sequencer overlay's direct fmt::runtime(LOC(...)) calls with the guarded LOCF helper so malformed localized format strings fall back safely instead of propagating formatting exceptions. Add a localization contract that rejects direct fmt::runtime(LOC(...)) usage anywhere in C++ sources.
Re-read language_generation after forwarding startup overlay input, because selecting a language mutates localization state during that input phase. The splash now updates localized text and refreshes its cache in the same frame. Add a headless contract requiring the post-input language refresh path.
Track language_generation in the VRAM HUD and refresh cached localized iteration and empty-state text when the active locale changes. Menu and tooltip paths already refresh through generation or dynamic resolution.
Add a shared plural-form helper for localized count messages, including Polish one/few/other forms. Update marketplace registry and asset result summaries to select the correct localized phrase, and repair the remaining Python tr() count formatting call. Extend the localization contracts to validate the supported plural categories and catalog keys.
Map every PluginState displayed by marketplace cards to an explicit localized label, covering installing, loading, active, unloaded, error, and disabled states across all shipped locales.
Add a dedicated localized Python console error for a second run request instead of reusing the running-state label. Register the key in the native key catalog and every shipped locale.
Replace hardcoded undo, redo, total, and GPU history fragments with complete locale-specific summary messages in every shipped language.
Localize grouped and untitled history fallbacks and pass translated undo or redo labels into history stack rows instead of hardcoded English tokens.
Use the shared interaction-safety guard for pending localization refreshes so modal dialogs and open menus retain the request until a safe frame. Add a localization contract for the full guard.
Translate Python operator labels that are localization keys before registering their native property metadata, preventing raw Sequencer and action keys from appearing in operator consumers.
Replace duplicated hardcoded COLMAP export confirmation labels in the Python export panel and native scene graph with shared localized keys, including a dedicated overwrite action in every shipped locale.
Treat fullwidth colons as existing label punctuation in the rendering panel and move Watch Directories localization lookups outside scan-state locks. Add a contract for the fullwidth-colon behavior.
Align the German locale with maintainer review feedback by using the established Keyframe terminology instead of the ASCII transliteration Schluesselbild, and restore umlauts in sequencer actions and tooltips. Correct the remaining Alle loeschen label to Alle löschen while preserving one-key-per-line locale formatting.
Restore the missing string-concatenation operator before the localized COLMAP sparse-output warning. This fixes the scene graph element compilation failure while preserving escaped localized text in the modal body.
Keep localization keys intact while Python operators are registered, instead of resolving them before the UI is rendered. This avoids startup lookups for sequencer labels and preserves dynamic localization at the rendering boundary. Add a localization contract that prevents property registration from resolving label keys prematurely.
Make RmlPythonPanelAdapter request an animation frame whenever the active locale differs from the document locale. Cached dirty-policy panels now reach prepareForRender without requiring pointer movement or another input event. The frame reloads the panel document through the existing language-aware lifecycle, refreshing RML translation directives, bound localized labels, placeholders, and tooltips. This fixes the Asset Manager refresh path and applies the same guarantee to every Python-backed RML panel. Add a headless localization contract that verifies cached Python panels cannot remain idle across a language-generation change.
Track the language used to load each RmlPanelHost document and reload the document whenever the active language changes. Apply the synchronization before normal panel drawing, direct rendering, layout preparation, and cached direct rendering. This prevents cached native panels, including Video Export, from continuing to display the previous locale until pointer input or another invalidation occurs. Request an animation frame while a native RML panel has an outdated language so the refresh is scheduled immediately even when no pointer event is received. Add a localization contract that protects both the cached and uncached native RML rendering paths.
…wing Revert the per-panel language synchronization introduced in 8660115. Reloading an RML document from draw, direct-draw, layout, and cached-composite paths can invalidate RmlUI elements while the current frame still holds references to them. The crash dump recorded after switching language with several panels open is an access violation and makes that approach unsafe. Keep the existing centralized localization refresh path, which performs panel resource reloads at the coordinated GUI-manager boundary, and retain the separate cached Python-panel frame invalidation from 7b56e46.
Keep the full interaction-safety guard for runtime localization reloads, but release focus retained by completed RML button and select interactions before evaluating the pending refresh. Editable text fields remain focused and continue to defer the reload. This avoids invalidating a document during active text editing, menus, or modal interactions while preventing inactive focus from blocking static @tr: content indefinitely. Request the centralized refresh after a language change from the startup picker as well as from the Python language API. This lets Video Extractor and Sequencer reload their static RML labels instead of updating only their dynamic labels. Add contracts for focus cleanup, the full safety guard, and the startup language-switch refresh path.
StartupOverlay is initialized with RmlUIManager, which deliberately does not own the GuiManager localization-refresh API. Remove the invalid call that prevented startup_overlay.cpp from compiling. Keep the focus-cleanup localization fix and adjust its contract to verify the implemented non-text-focus behavior rather than an unavailable startup callback.
Remove the non-text focus cleanup added by 1e8f9d8. The reported access violation persists when a language switch clears RML focus and then reloads resources in the same GUI cycle with multiple panels open. Restore the established full interaction guard without mutating focus state. This deliberately leaves the Video Extractor and Sequencer static-label issue unresolved until it can be fixed through a refresh path that is separated from live RmlUI document and frame lifetimes.
…oads Preserve the localization keys behind parse-time @tr: directives as data attributes while loading RML documents. Cover translated element text as well as title and placeholder attributes. Refresh those translations directly on every live RmlUI document when the application language generation changes. This keeps document instances, event listeners, focused controls, select state, and cached component references alive instead of unloading resources during an active GUI lifetime. Replace the runtime localization reload path with the in-place document refresh while retaining full resource reloads for development RML and locale hot reloads. Detect language-generation changes centrally so switches initiated from either the startup picker or Python API reach static RML content in Video Extractor, Sequencer, and the other RML surfaces. Extend localization contracts to require the in-place path, reject destructive resource reloads from runtime language changes, and verify that every shipped @tr: directive has a supported text or attribute shape.
Include rml_document_utils.hpp in rmlui_manager.cpp so the runtime document refresh can resolve the rml_documents namespace and refreshLocalizedContent declaration under MSVC.
…e changes Make Video Extractor request an animation frame whenever the active language differs from the language used by its last synchronized render. Expose the same localization-frame demand from the Sequencer panel and include it in SequencerUIManager animation scheduling. This ensures the in-place DOM translations are composited immediately instead of waiting for mouse movement, playback, or another unrelated panel invalidation. Add contracts covering both native cached-panel scheduling paths.
Reuse the existing UI layout-settle frame budget after an in-place localization refresh. Keeping three consecutive GUI frames active lets translated DOM updates propagate through measurement, layout, cached rendering, and final composition without falling back to the idle event-loop timeout between stages. Extend the runtime localization contract to require the settle-frame scheduling.
Synchronize each RmlUI select control after its translated option elements are updated so the closed dropdown displays the active locale immediately. Apply the behavior in the shared live-document refresh path, covering Video Extractor values such as Frame Interval, PNG (lossless), and Original as well as other RML selects without reloading documents or dispatching selection changes. Extend the localization contracts to require selected-label synchronization and verify formatting, locale completeness, hardcoded UI scanning, and all localization contracts.
Refresh RmlUI selectvalue content directly after translated options change so closed Video Extractor dropdowns update immediately during runtime language switches. Complete the remaining review follow-ups by translating the residual German, French, and Spanish runtime messages, preserving checkpoint placeholders, localizing log entry wording, adding the dedicated Depth Map disable label, and keeping operator metadata stable for non-GUI consumers. Extend the localization contract for the selectvalue refresh and document the intentional Play/Pause operator exception in the hardcoded UI allowlist.
Update every cached Video Extractor select option when the runtime language changes, including the visible selectvalue for the currently selected option. Cover extraction mode, output format, resolution, sharpness controls, and window candidate presets so open dialogs no longer retain English labels after switching locale. Add a headless localization contract for the cached select refresh and representative Video Extractor keys.
Document the count-sensitive locale-key convention, the current Polish one/few/other rule, and the contract-driven process for adding future grammar rules. Clarify that headless localization contracts cover plural-form behavior.
Fall back to the English locale text when a localized format string is invalid, while retaining thread-local result storage for worker-thread callers. Cache navigation toolbar labels by language generation and cache the HUD iteration label instead of resolving it on every update. Remove trailing colons from the compact Iteration label in every shipped locale and add contracts for all three regression paths.
|
Thanks for the detailed review. I went through every inline thread and added follow-up commits covering the remaining functional, safety, validation, runtime-refresh, and documentation points. Review follow-ups addressedStable task state and worker-thread safety
Contracts, CMake, and CI
Runtime language refreshes
Remaining localization and terminology
Hardcoded UI audit and documentation
ValidationThe following checks pass: python tests/python/test_localization_contracts.py
python tools/check_locale_completeness.py
python tools/check_ui_hardcoded.py --fail-on-candidates
clang-format --dry-run --Werror <all modified C/C++ files>
git diff --checkThe locale checker validates 2028 English keys across all shipped locales, and the hardcoded UI audit reports no likely user-facing hardcoded strings. Manual validationRuntime language switching was manually checked with open cached panels, including Video Extractor dropdowns, toolbar tooltips, Asset Manager, Sequencer, and Python panels. No stale labels, delayed refreshes, or crashes were observed. |
|
Thanks for the thorough follow-up — most of the round-1 list is properly closed. The A few things still block merge, mostly fallout from the stage/outcome split and the new RML refresh. Must fix1. Completed exports no longer register with the Asset Manager — now in every language, including English. 2. MCP/CLI dataset imports never report 3. 4. Also worth a pass
Minor
Items 1–4 are the merge blockers; the rest can follow. Thanks again for keeping at this — the locale data itself is in great shape. |
Complete the four merge-critical localization fixes identified by the upstream maintainer. Publish stable export outcomes through both Python state bridges, reset dataset import outcomes on each load, and record completed or failed dataset results for MCP and CLI consumers. Add the missing common editing actions to every shipped locale and extend literal localization contracts to cover direct LocalizationManager lookups. Prime the initial RML refresh generation, traverse non-DOM RmlUi children, and protect dynamically written content from being overwritten during language changes. Harden Python translation formatting against malformed plugin overrides, localize MCP operator labels and copied-log counts, restore missing LOD status strings and HUD punctuation, enforce the hardcoded UI audit in CI, remove obsolete COLMAP helpers, and clean up the unused string key. Validation: localization contracts, locale completeness, hardcoded UI audit, Python compilation checks, clang-format dry-run with Werror, and git diff check.
Complete the export state contract used by Python panels by adding the stable outcome field to OverlayExportState and populating it from AsyncTaskManager. This keeps the Python UI path consistent with the AppStore state bridge and allows completed exports to be registered by the Asset Manager. Validation: localization contracts, clang-format dry-run with Werror, and git diff check. Build was not run.
Resolve the remaining upstream localization review findings across native and Python UI paths. - Report the actual export outcome through Python overlay state and task bridges. - Format COLMAP overwrite warnings for the detected binary or text sparse model. - Harden RML translation refreshes against dynamic content and translated select options. - Consolidate translation directive preprocessing into one regex traversal. - Preserve direct localization lookup lifetime documentation and operator label resolution. - Add regression contracts for dynamic RML content, directive handling, and COLMAP formats. - Keep all shipped locales synchronized with the new positional formatting placeholder. - Remove unrelated whitespace churn and normalize touched RML and C++ formatting. Validation performed: localization contracts, locale completeness, hardcoded UI audit, Python syntax checks, clang-format, and git diff --check.
Add the missing environment map, exposure, and rotation labels to every shipped locale. Extend localization contracts to inspect literal LOCALE_KEY dictionaries in Python panels, preventing indirect mapping references from bypassing key validation. Validation performed: localization contracts, locale completeness, hardcoded UI audit, Python syntax checks, and git diff --check.
|
I went through all the listed points and hopefully addressed the remaining issues in the latest local commits. The implemented fixes include:
The English locale currently contains 2042 localization keys. All shipped locales are checked against the English key set, placeholder signatures, plural forms, and one-key-per-line formatting. Validation completed locally:
|
Summary
This pull request completes the GUI localization pass and strengthens localization handling across native C++, RML, Python panels, asynchronous operations, MCP/UI bridges, and shipped locale resources.
It also introduces headless localization contracts and hardcoded-UI audits to prevent localization regressions from being reintroduced.
The English locale currently contains 2042 localization keys, with matching key sets and placeholder contracts across all shipped locales.
Localization coverage
The changes move remaining user-facing GUI text into the localization resources, including:
All shipped locales are kept structurally aligned with
en.json.Runtime language switching
Runtime language switching now updates:
titleandplaceholderattributes;<option>elements;Dynamic application-owned content is preserved during localization refreshes. Values such as paths, progress values, frame counts, FPS values, and other runtime-generated content are not overwritten by stale localized text.
The refresh path also handles the initial language generation correctly, including startup when the generation value is zero.
Python panel localization
Python panels now use explicit localization mappings for their labels.
The Rendering Panel includes dedicated keys for:
The localization contracts inspect literal Python
LOCALE_KEYdictionaries so indirect mapping references cannot bypass validation.Python formatting is protected with a safe formatter that supports both positional and named placeholders and gracefully handles malformed translator-controlled format strings.
Stable task outcomes
Task state now separates stable machine-readable outcomes from localized display stages.
Export and dataset operations expose stable outcomes such as:
idle;running;completed;failed;cancelled.Localized stage text remains presentation-only and is not used for program logic.
The export outcome is propagated consistently through:
COLMAP export handling
COLMAP overwrite dialogs now detect the actual source sparse format.
Binary sources report:
cameras.bin;images.bin;points3D.bin.Text sources report:
cameras.txt;images.txt;points3D.txt.The message uses a shared positional localization placeholder and is translated in every shipped locale.
Localization validation tools
The localization validation suite now checks:
LOCALE_KEYdictionaries;The hardcoded UI audit supports CI enforcement through
--fail-on-candidates.CI and headless contracts
The repository includes a headless localization contract target that can be run without Torch-dependent tests.
The contracts are also suitable for CI execution and fail when localization regressions are detected.
Example commands:
Documentation
Localization conventions, validation tools, hardcoded UI audit rules, allowlist policy, formatting rules, runtime refresh behavior, and language-specific grammar handling are documented in:
CONTRIBUTING.md;docs/building_and_distribution.md;Validation performed
The following checks pass locally:
git diff --check.A full application build and runtime verification should be performed before merge, with particular attention to: