This reference describes how the desktop client chooses its language, colours, fonts, and accessibility adjustments, and where it keeps these choices. It covers i18n/translator.py, i18n/en.json, i18n/tr.json, themes/theme_engine.py, themes/style.qss, core/accessibility_manager.py, core/settings_store.py, widgets/preferences_dialog.py, and widgets/accessibility_dialog.py under src/cluster_erp/desktop/. The web client's equivalents are on internationalisation and languages and themes, tokens, and accessibility.
Scope and source¶
- Defined in:
Translator(src/cluster_erp/desktop/i18n/translator.py:32),ThemeEngineandTHEMES(src/cluster_erp/desktop/themes/theme_engine.py:661-881),AccessibilityManager(src/cluster_erp/desktop/core/accessibility_manager.py:207),SettingsStore(src/cluster_erp/desktop/core/settings_store.py:8). - Counts: language keys were counted as the top-level keys of each JSON file; theme tokens as the keys of each
THEMESpalette; stylesheet tokens as the distinct@nameplaceholders instyle.qss, excluding the word in its header comment; icons as the keys of_ICON_PATHS; settings keys by searching for calls ofget,set, andremoveon the settings store andQSettings.
Language files¶
| File | Keys | _meta.name |
Source |
|---|---|---|---|
en.json |
2,077 | English |
src/cluster_erp/desktop/i18n/en.json key _meta.name |
tr.json |
2,077 | Türkçe |
src/cluster_erp/desktop/i18n/tr.json key _meta.name |
Both files have the same key set. The keys are flat strings with a dotted prefix. The largest prefixes are procurement (269 keys), error (177), bom_explorer (174), chat (123), nesting_job (74), resource (58), product (56), purq (54), logs (54), and common (48). The folder also holds an empty file en_keys.txt.
Translator¶
| Member | Behaviour | Source |
|---|---|---|
| constructor | Reads the setting language, default tr, loads en.json as the fallback and the chosen file as the main table |
src/cluster_erp/desktop/i18n/translator.py:35-55 |
t(key, fallback, **kwargs) |
Looks up the main table, then metadata labels in the current language or English, then en.json, then the given fallback, then the key itself; fills {name} placeholders |
src/cluster_erp/desktop/i18n/translator.py:57-67 |
register_meta(key, translations) |
Stores a metadata label for t |
src/cluster_erp/desktop/i18n/translator.py:69-70 |
set_language(locale) |
Saves the setting, reloads the table, and emits language_changed |
src/cluster_erp/desktop/i18n/translator.py:76-80 |
upper, title |
Use Turkish dotted and dotless i rules when the language is tr |
src/cluster_erp/desktop/i18n/translator.py:12-29, src/cluster_erp/desktop/i18n/translator.py:82-90 |
available_languages() |
Returns code and _meta.name for every *.json file in the folder |
src/cluster_erp/desktop/i18n/translator.py:92-99 |
A new desktop language is a new JSON file in src/cluster_erp/desktop/i18n/. The language selectors of the sign-in dialog and the preferences dialog list every file that available_languages finds (src/cluster_erp/desktop/widgets/login_dialog.py:214-226, src/cluster_erp/desktop/widgets/preferences_dialog.py:62-69). The desktop folder has no languages.json. The API client sends the current language as Accept-Language and follows every change (src/cluster_erp/desktop/main.py:169-172).
Widgets that show translated text connect to Translator.language_changed and set their texts again. For example MainWindow._retranslate_ui renames section tabs (src/cluster_erp/desktop/widgets/main_window.py:64, src/cluster_erp/desktop/widgets/main_window.py:628-648).
Theme engine¶
ThemeEngine reads the setting theme, default dark, and applies it at once (src/cluster_erp/desktop/themes/theme_engine.py:717-726). Applying a theme does four things (src/cluster_erp/desktop/themes/theme_engine.py:798-848):
- It merges the palette with the current overrides from accessibility.
- It sets a
QPalettefor the application from the merged values. - It writes the icon SVG files in the icon colour and the check-box SVG files, only when their colours changed.
- It reads
style.qss, replaces each@tokenwith its value, longest token name first, sets the stylesheet, and repolishes every widget.
set_overrides waits 150 ms before it applies, so several changes in a row cause one repaint (src/cluster_erp/desktop/themes/theme_engine.py:777-790). toggle switches between light and dark (src/cluster_erp/desktop/themes/theme_engine.py:880-881). current_colors returns the merged palette for widgets that paint themselves (src/cluster_erp/desktop/themes/theme_engine.py:875-878).
Theme tokens¶
Each palette has 24 keys (src/cluster_erp/desktop/themes/theme_engine.py:661-714). style.qss uses 20 of them plus cb-dir, the folder of the generated check-box images (src/cluster_erp/desktop/themes/theme_engine.py:844).
| Token | Dark | Light | In style.qss |
|---|---|---|---|
window |
#111113 |
#FBFBFC |
yes |
base |
#19191C |
#FFFFFF |
yes |
alternate-base |
#232327 |
#EDEDF0 |
yes |
text |
#ECECEF |
#1C1C1E |
yes |
dimmed |
#87878F |
#87878F |
yes |
muted |
#55555C |
#B0B0B8 |
yes |
accent |
#4C8EF7 |
#3E7BFA |
yes |
accent-hover |
#6BA1F8 |
#2E6AE6 |
yes |
highlight |
#2B4A7C |
#D5E4FD |
yes |
border |
#2A2A2E |
#E6E6E9 |
yes |
input |
#19191C |
#FFFFFF |
yes |
sidebar |
#111113 |
#F0F0F2 |
no |
sidebar-hover |
#1E1E21 |
#E6E6E9 |
no |
sidebar-active |
#252528 |
#DDDDE0 |
no |
danger |
#E5484D |
#E5484D |
yes |
warning |
#F5A623 |
#F5A623 |
yes |
success |
#30A46C |
#30A46C |
no |
button-gradient |
#2C2C31 |
#F4F4F7 |
yes |
button-shadow |
#18181B |
#C8C8CC |
yes |
primary-shadow |
#1E3560 |
#B0C4E0 |
yes |
font-family |
"Segoe UI" |
"Segoe UI" |
yes |
font-size |
9pt |
9pt |
yes |
font-size-small |
8pt |
8pt |
yes |
font-size-large |
13pt |
13pt |
yes |
The four keys marked no are read by widgets through current_colors, for example the sidebar (src/cluster_erp/desktop/widgets/sidebar.py:321-334) and the status bar (src/cluster_erp/desktop/widgets/status_bar.py:76-88).
Icons¶
_ICON_PATHS holds 123 icon glyphs as SVG path fragments (src/cluster_erp/desktop/themes/theme_engine.py:23-649). The engine writes them as files in a temporary folder in the dimmed colour, and icon(name) loads a file, or returns an empty icon for an unknown name (src/cluster_erp/desktop/themes/theme_engine.py:14, src/cluster_erp/desktop/themes/theme_engine.py:652-654, src/cluster_erp/desktop/themes/theme_engine.py:728-734). Metadata sections and actions name icons by these keys (src/cluster_erp/desktop/widgets/sidebar.py:202-205).
Accessibility settings¶
AccessibilityManager reads its settings under the prefix accessibility/, computes theme overrides, and emits settings_changed after every change (src/cluster_erp/desktop/core/accessibility_manager.py:195, src/cluster_erp/desktop/core/accessibility_manager.py:207-237). main() applies them before the sign-in dialog (src/cluster_erp/desktop/main.py:164-165).
| Setting | Values | Effect | Source |
|---|---|---|---|
font_scale |
small, default, large, xlarge |
Multiplies the 9 pt base font by 0.87, 1.0, 1.13, or 1.27 and scales the three font-size tokens | src/cluster_erp/desktop/core/accessibility_manager.py:12-17, src/cluster_erp/desktop/core/accessibility_manager.py:390-397 |
high_contrast |
off, light |
Replaces the palette with a high-contrast light palette | src/cluster_erp/desktop/core/accessibility_manager.py:19-39, src/cluster_erp/desktop/core/accessibility_manager.py:406-407 |
color_blind_mode |
off, protanopia, deuteranopia, tritanopia |
Replaces accent, highlight, danger, warning, and success colours | src/cluster_erp/desktop/core/accessibility_manager.py:41-66, src/cluster_erp/desktop/core/accessibility_manager.py:409-410 |
reading_bg |
off, peach, orange, yellow |
Tints background colours, with a light and a dark variant each | src/cluster_erp/desktop/core/accessibility_manager.py:68-158, src/cluster_erp/desktop/core/accessibility_manager.py:412-415 |
brightness |
50 to 150, default 100 | Lightens or darkens nine background keys | src/cluster_erp/desktop/core/accessibility_manager.py:161-189, src/cluster_erp/desktop/core/accessibility_manager.py:323-328, src/cluster_erp/desktop/core/accessibility_manager.py:417-423 |
dyslexia_font |
true or false | Switches to the first installed font of a list, with wider letter and word spacing | src/cluster_erp/desktop/core/accessibility_manager.py:192, src/cluster_erp/desktop/core/accessibility_manager.py:360-388 |
reduced_motion |
true or false | Stored and shown in the dialog | src/cluster_erp/desktop/core/accessibility_manager.py:295-298 |
welcomed |
set once | Hides the welcome banner of the accessibility dialog after the first visit | src/cluster_erp/desktop/core/accessibility_manager.py:269-274, src/cluster_erp/desktop/widgets/accessibility_dialog.py:233-234 |
A stored high-contrast value dark is migrated to light; any other unknown value becomes off (src/cluster_erp/desktop/core/accessibility_manager.py:221-229). reset_all removes the seven adjustable settings and applies the defaults (src/cluster_erp/desktop/core/accessibility_manager.py:330-352). When the theme changes, the overrides are computed again if any adjustment is active (src/cluster_erp/desktop/core/accessibility_manager.py:431-446).
The Accessibility dialog (key accessibility.title) offers segmented controls for font size, high contrast, colour-blind mode, and reading background, a brightness slider from 50 to 150, check boxes for reduced motion and the dyslexia font, and Reset All Settings (key accessibility.reset_all) with a confirmation (src/cluster_erp/desktop/widgets/accessibility_dialog.py:216-400, src/cluster_erp/desktop/widgets/accessibility_dialog.py:477-489). It opens from the sidebar, from the sign-in dialog, and through MainWindow._show_accessibility (src/cluster_erp/desktop/widgets/sidebar.py:129, src/cluster_erp/desktop/widgets/login_dialog.py:230-240, src/cluster_erp/desktop/widgets/main_window.py:614-624).
Preferences dialog¶
The Preferences dialog (key settings.title) applies each change at once and saves it (src/cluster_erp/desktop/widgets/preferences_dialog.py:37-133):
| Control | Setting | Source |
|---|---|---|
Theme (key settings.theme) |
theme, through ThemeEngine.apply |
src/cluster_erp/desktop/widgets/preferences_dialog.py:53-60, src/cluster_erp/desktop/widgets/preferences_dialog.py:102-105 |
Language (key settings.language) |
language, through Translator.set_language |
src/cluster_erp/desktop/widgets/preferences_dialog.py:62-69, src/cluster_erp/desktop/widgets/preferences_dialog.py:107-110 |
Timezone (key settings.timezone) |
timezone; an editable list of 16 common zones that exist on the system |
src/cluster_erp/desktop/widgets/preferences_dialog.py:71-82, src/cluster_erp/desktop/core/timezone_utils.py:8-30 |
Server URL (key settings.api_url) |
api_url; a change asks for a restart |
src/cluster_erp/desktop/widgets/preferences_dialog.py:84-89, src/cluster_erp/desktop/widgets/preferences_dialog.py:117-133 |
Local settings¶
SettingsStore wraps QSettings with the organisation ClusterERP and the application DesktopClient, and writes through at once (src/cluster_erp/desktop/core/settings_store.py:8-21). The client uses 16 keys:
| Key | Written by | Source |
|---|---|---|
api_url |
sign-in dialog, preferences dialog | src/cluster_erp/desktop/widgets/login_dialog.py:283, src/cluster_erp/desktop/widgets/preferences_dialog.py:123 |
server_urls |
sign-in dialog | src/cluster_erp/desktop/widgets/login_dialog.py:282 |
language |
Translator.set_language |
src/cluster_erp/desktop/i18n/translator.py:78 |
theme |
ThemeEngine.apply |
src/cluster_erp/desktop/themes/theme_engine.py:794 |
timezone |
preferences dialog | src/cluster_erp/desktop/widgets/preferences_dialog.py:112-126 |
window/geometry, window/state |
MainWindow.closeEvent |
src/cluster_erp/desktop/widgets/main_window.py:698-699 |
panes/layout |
PaneContainer.save_state |
src/cluster_erp/desktop/widgets/pane_container.py:28, src/cluster_erp/desktop/widgets/pane_container.py:270 |
accessibility/font_scale, high_contrast, reduced_motion, dyslexia_font, color_blind_mode, reading_bg, brightness, welcomed |
AccessibilityManager |
src/cluster_erp/desktop/core/accessibility_manager.py:207-352 |
get_user_timezone and BomExplorer read the same store through their own QSettings objects (src/cluster_erp/desktop/core/timezone_utils.py:37-43, src/cluster_erp/desktop/modules/bom/bom_explorer.py:1412-1419).
Constraints and validation¶
- Setters ignore values outside their lists, and brightness is clamped to 50 to 150 (
src/cluster_erp/desktop/core/accessibility_manager.py:278-328). - An unknown time zone name falls back to
UTC(src/cluster_erp/desktop/core/timezone_utils.py:37-43). - No code outside
AccessibilityManagerand the accessibility dialog readsreduced_motion, found by searchingsrc/cluster_erp/desktop/for the name. - Desktop language files are also checked by tests outside the desktop folder, for example for the presence of error keys (
tests/test_bom_bugfix_batch_2026_10_08.py:123-130).
Discrepancies¶
Terms introduced¶
| Term | Code identifier | Definition | Turkish term | Source |
|---|---|---|---|---|
| translator | Translator |
The desktop object that loads a language file and resolves keys and metadata labels. | çevirmen | src/cluster_erp/desktop/i18n/translator.py:32 |
| theme engine | ThemeEngine |
The desktop object that applies a palette, the stylesheet with tokens, and generated icons. | tema motoru | src/cluster_erp/desktop/themes/theme_engine.py:717 |
| theme token | @token |
A placeholder in style.qss that the theme engine replaces with a palette value. |
tema belirteci | src/cluster_erp/desktop/themes/theme_engine.py:844-846 |
| accessibility manager | AccessibilityManager |
The desktop object that stores accessibility choices and turns them into font changes and theme overrides. | erişilebilirlik yöneticisi | src/cluster_erp/desktop/core/accessibility_manager.py:207 |
| settings store | SettingsStore |
The wrapper around the local QSettings store of the desktop client. |
ayar deposu | src/cluster_erp/desktop/core/settings_store.py:8 |
Related pages¶
- Desktop client
- Run the desktop client
- Internationalisation and languages
- Themes, tokens, and accessibility
- Data-driven languages
Verified against commit 92f8051 on 2026-11-13.