-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathunity_localization_master.py
More file actions
2107 lines (1908 loc) · 102 KB
/
Copy pathunity_localization_master.py
File metadata and controls
2107 lines (1908 loc) · 102 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import json
import logging
import os
import sys
import urllib.request
import xml.etree.ElementTree as ET
from tkinter import filedialog, messagebox, simpledialog
import tkinter as tk
from tkinter import ttk
from xml.dom import minidom
# Prevent automatic proxy detection that may break translation requests on some PCs
urllib.request.getproxies = lambda: {}
try:
from deep_translator import GoogleTranslator
except Exception:
GoogleTranslator = None
APP_ID = "UnityLocalizationMaster"
DEFAULT_XML_LANGUAGES = ["en", "ru"]
UI_LANGUAGES = ["ru", "en", "de", "es", "fr"]
UI_LANGUAGE_NAMES = {
"ru": "Русский",
"en": "English",
"de": "Deutsch",
"es": "Español",
"fr": "Français",
}
UI_TEXTS = {
"en": {
"app_title": "Unity Localization Master",
"header_title": "Localization XML Editor",
"header_subtitle": "Create, edit and translate Unity-style XML localization files.",
"menu_file": "File",
"menu_new": "Create XML...",
"menu_open": "Open XML...",
"menu_save": "Save XML",
"menu_save_as": "Save XML As...",
"menu_exit": "Exit",
"menu_edit": "Edit",
"menu_new_entry": "New entry",
"menu_delete_entry": "Delete selected entry",
"menu_clear_form": "Clear form",
"menu_tools": "Tools",
"menu_add_language": "Add XML language",
"menu_auto_translate": "Auto-translate current entry",
"menu_batch_language": "Add language and translate all entries",
"menu_help": "Help",
"menu_about": "About",
"menu_ui_language": "Program language",
"toolbar_open": "Open XML",
"toolbar_create": "Create XML",
"toolbar_save": "Save XML",
"toolbar_add_language": "Add language",
"toolbar_batch": "Translate entire file",
"left_panel_title": "Entries",
"search": "Search",
"search_placeholder": "Search by key or text",
"refresh": "Refresh",
"new_entry": "New",
"delete_entry": "Delete",
"entry_id": "Entry key (ID)",
"source_language": "Source language for translation",
"translations": "Translations",
"editor_title_new": "New entry",
"editor_title_edit": "Editing: {key}",
"save_entry": "Save entry",
"clear_form": "Clear form",
"auto_translate": "Auto-translate current entry",
"batch_translate": "Add language and translate all entries",
"status_ready": "Ready",
"status_no_file": "No XML file selected",
"status_file_loaded": "Loaded: {path}",
"status_file_saved": "Saved: {path}",
"status_entry_saved": "Entry saved: {key}",
"status_entry_deleted": "Entry deleted: {key}",
"status_language_added": "XML language added: {lang}",
"status_search_results": "Found: {count}",
"status_translation_done": "Automatic translation completed",
"status_batch_done": "Batch translation completed: {lang}",
"table_id": "ID",
"table_preview": "English preview",
"warning": "Warning",
"error": "Error",
"info": "Information",
"confirm": "Confirmation",
"about_title": "About",
"about_text": "Unity Localization Master\n\nProduction-style desktop editor for XML localization files.\nFeatures:\n• XML entry browser\n• Interface localization into 5 languages\n• Automatic translation via Google Translator\n• Batch language generation\n• Ready for EXE packaging with PyInstaller",
"prompt_new_file": "Create a new localization XML file?",
"prompt_unsaved_switch": "The current form has unsaved changes. Save the entry before continuing?",
"prompt_unsaved_exit": "The current form has unsaved changes. Exit anyway?",
"prompt_delete_entry": "Delete entry '{key}'?",
"prompt_overwrite_entry": "Entry '{key}' already exists. Overwrite it?",
"prompt_replace_language": "Language '{lang}' already exists in the XML. Re-translate all entries for this language?",
"prompt_language_code": "Enter a language code (for example: de, fr, es, it, pt-BR):",
"prompt_language_code_all": "Enter a language code for batch translation (for example: ja, ko, tr, pt-BR):",
"msg_open_first": "Please open or create an XML file first.",
"msg_invalid_xml": "The selected XML file could not be read.\n\n{error}",
"msg_key_required": "Please enter an entry key (ID).",
"msg_entry_saved": "Entry '{key}' has been saved.",
"msg_entry_deleted": "Entry '{key}' has been deleted.",
"msg_nothing_selected": "Please select an entry first.",
"msg_language_exists": "This XML language already exists.",
"msg_language_added": "Language '{lang}' has been added to the editor.",
"msg_no_translator": "Translation is unavailable.\nInstall dependency: pip install deep-translator",
"msg_missing_source": "Source text is empty.",
"msg_missing_source_lang": "Source language field is not available.",
"msg_no_entries": "There are no entries in the XML file.",
"msg_batch_finished": "Language '{lang}' processed.\nTranslated: {ok}\nErrors/Skipped: {failed}\nTotal entries: {total}",
"msg_file_created": "A new XML file has been created.",
"msg_file_saved": "XML file has been saved.",
"msg_save_before_xml": "Save the current entry before saving the XML?",
"msg_search_empty": "Type text to filter the entry list.",
"msg_same_ui_language": "The program language is already selected.",
"progress_title": "Translation in progress",
"progress_batch_label": "Translating {total} entries into '{lang}'...",
"progress_current_label": "Translating current entry...",
"xml_language_list": "XML languages",
"current_file": "Current file",
"translator_status_on": "Translator: available",
"translator_status_off": "Translator: unavailable",
"field_text": "Text ({lang})",
"footer_hint": "Tip: double-click an entry in the list to edit it quickly.",
"language_program": "Program language",
},
"ru": {
"app_title": "Unity Localization Master",
"header_title": "Редактор XML-локализации",
"header_subtitle": "Создание, редактирование и перевод XML-файлов локализации в стиле Unity.",
"menu_file": "Файл",
"menu_new": "Создать XML...",
"menu_open": "Открыть XML...",
"menu_save": "Сохранить XML",
"menu_save_as": "Сохранить XML как...",
"menu_exit": "Выход",
"menu_edit": "Правка",
"menu_new_entry": "Новая запись",
"menu_delete_entry": "Удалить выбранную запись",
"menu_clear_form": "Очистить форму",
"menu_tools": "Инструменты",
"menu_add_language": "Добавить язык XML",
"menu_auto_translate": "Автоперевод текущей записи",
"menu_batch_language": "Добавить язык и перевести все записи",
"menu_help": "Справка",
"menu_about": "О программе",
"menu_ui_language": "Язык программы",
"toolbar_open": "Открыть XML",
"toolbar_create": "Создать XML",
"toolbar_save": "Сохранить XML",
"toolbar_add_language": "Добавить язык",
"toolbar_batch": "Перевести весь файл",
"left_panel_title": "Записи",
"search": "Поиск",
"search_placeholder": "Поиск по ключу или тексту",
"refresh": "Обновить",
"new_entry": "Новая",
"delete_entry": "Удалить",
"entry_id": "Ключ записи (ID)",
"source_language": "Исходный язык для перевода",
"translations": "Переводы",
"editor_title_new": "Новая запись",
"editor_title_edit": "Редактирование: {key}",
"save_entry": "Сохранить запись",
"clear_form": "Очистить форму",
"auto_translate": "Автоперевод текущей записи",
"batch_translate": "Добавить язык и перевести все записи",
"status_ready": "Готово",
"status_no_file": "XML-файл не выбран",
"status_file_loaded": "Загружено: {path}",
"status_file_saved": "Сохранено: {path}",
"status_entry_saved": "Запись сохранена: {key}",
"status_entry_deleted": "Запись удалена: {key}",
"status_language_added": "Язык XML добавлен: {lang}",
"status_search_results": "Найдено: {count}",
"status_translation_done": "Автоперевод завершён",
"status_batch_done": "Пакетный перевод завершён: {lang}",
"table_id": "ID",
"table_preview": "Предпросмотр EN",
"warning": "Внимание",
"error": "Ошибка",
"info": "Информация",
"confirm": "Подтверждение",
"about_title": "О программе",
"about_text": "Unity Localization Master\n\nДесктопный редактор XML-локализаций с оформлением под production.\nФункции:\n• список и поиск записей\n• локализация интерфейса на 5 языков\n• автоматический перевод через Google Translator\n• пакетное добавление языков\n• готовность к сборке в EXE через PyInstaller",
"prompt_new_file": "Создать новый XML-файл локализации?",
"prompt_unsaved_switch": "В текущей форме есть несохранённые изменения. Сохранить запись перед продолжением?",
"prompt_unsaved_exit": "В текущей форме есть несохранённые изменения. Всё равно выйти?",
"prompt_delete_entry": "Удалить запись '{key}'?",
"prompt_overwrite_entry": "Запись '{key}' уже существует. Перезаписать её?",
"prompt_replace_language": "Язык '{lang}' уже существует в XML. Перевести все записи для него заново?",
"prompt_language_code": "Введите код языка (например: de, fr, es, it, pt-BR):",
"prompt_language_code_all": "Введите код языка для пакетного перевода (например: ja, ko, tr, pt-BR):",
"msg_open_first": "Сначала откройте или создайте XML-файл.",
"msg_invalid_xml": "Не удалось прочитать выбранный XML-файл.\n\n{error}",
"msg_key_required": "Введите ключ записи (ID).",
"msg_entry_saved": "Запись '{key}' сохранена.",
"msg_entry_deleted": "Запись '{key}' удалена.",
"msg_nothing_selected": "Сначала выберите запись.",
"msg_language_exists": "Этот язык XML уже существует.",
"msg_language_added": "Язык '{lang}' добавлен в редактор.",
"msg_no_translator": "Перевод недоступен.\nУстановите зависимость: pip install deep-translator",
"msg_missing_source": "Исходный текст пуст.",
"msg_missing_source_lang": "Поле исходного языка недоступно.",
"msg_no_entries": "В XML-файле нет записей.",
"msg_batch_finished": "Язык '{lang}' обработан.\nПереведено: {ok}\nОшибок/пропущено: {failed}\nВсего записей: {total}",
"msg_file_created": "Новый XML-файл создан.",
"msg_file_saved": "XML-файл сохранён.",
"msg_save_before_xml": "Сохранить текущую запись перед сохранением XML?",
"msg_search_empty": "Введите текст, чтобы отфильтровать список записей.",
"msg_same_ui_language": "Этот язык программы уже выбран.",
"progress_title": "Идёт перевод",
"progress_batch_label": "Перевод {total} записей на '{lang}'...",
"progress_current_label": "Перевод текущей записи...",
"xml_language_list": "Языки XML",
"current_file": "Текущий файл",
"translator_status_on": "Переводчик: доступен",
"translator_status_off": "Переводчик: недоступен",
"field_text": "Текст ({lang})",
"footer_hint": "Совет: дважды кликните по записи в списке, чтобы быстро открыть её для редактирования.",
"language_program": "Язык программы",
},
"de": {
"app_title": "Unity Localization Master",
"header_title": "XML-Lokalisierungseditor",
"header_subtitle": "Unity-ähnliche XML-Lokalisierungsdateien erstellen, bearbeiten und übersetzen.",
"menu_file": "Datei",
"menu_new": "XML erstellen...",
"menu_open": "XML öffnen...",
"menu_save": "XML speichern",
"menu_save_as": "XML speichern unter...",
"menu_exit": "Beenden",
"menu_edit": "Bearbeiten",
"menu_new_entry": "Neuer Eintrag",
"menu_delete_entry": "Ausgewählten Eintrag löschen",
"menu_clear_form": "Formular leeren",
"menu_tools": "Werkzeuge",
"menu_add_language": "XML-Sprache hinzufügen",
"menu_auto_translate": "Aktuellen Eintrag automatisch übersetzen",
"menu_batch_language": "Sprache hinzufügen und alle Einträge übersetzen",
"menu_help": "Hilfe",
"menu_about": "Info",
"menu_ui_language": "Programmsprache",
"toolbar_open": "XML öffnen",
"toolbar_create": "XML erstellen",
"toolbar_save": "XML speichern",
"toolbar_add_language": "Sprache hinzufügen",
"toolbar_batch": "Gesamte Datei übersetzen",
"left_panel_title": "Einträge",
"search": "Suche",
"search_placeholder": "Nach Schlüssel oder Text suchen",
"refresh": "Aktualisieren",
"new_entry": "Neu",
"delete_entry": "Löschen",
"entry_id": "Eintrags-ID",
"source_language": "Quellsprache für Übersetzung",
"translations": "Übersetzungen",
"editor_title_new": "Neuer Eintrag",
"editor_title_edit": "Bearbeiten: {key}",
"save_entry": "Eintrag speichern",
"clear_form": "Formular leeren",
"auto_translate": "Aktuellen Eintrag automatisch übersetzen",
"batch_translate": "Sprache hinzufügen und alle Einträge übersetzen",
"status_ready": "Bereit",
"status_no_file": "Keine XML-Datei ausgewählt",
"status_file_loaded": "Geladen: {path}",
"status_file_saved": "Gespeichert: {path}",
"status_entry_saved": "Eintrag gespeichert: {key}",
"status_entry_deleted": "Eintrag gelöscht: {key}",
"status_language_added": "XML-Sprache hinzugefügt: {lang}",
"status_search_results": "Gefunden: {count}",
"status_translation_done": "Automatische Übersetzung abgeschlossen",
"status_batch_done": "Stapelübersetzung abgeschlossen: {lang}",
"table_id": "ID",
"table_preview": "EN-Vorschau",
"warning": "Warnung",
"error": "Fehler",
"info": "Information",
"confirm": "Bestätigung",
"about_title": "Info",
"about_text": "Unity Localization Master\n\nDesktop-Editor im Production-Stil für XML-Lokalisierungsdateien.\nFunktionen:\n• Eintragsliste und Suche\n• Oberfläche in 5 Sprachen\n• Automatische Übersetzung über Google Translator\n• Stapelweises Hinzufügen von Sprachen\n• Bereit für EXE-Build mit PyInstaller",
"prompt_new_file": "Neue XML-Lokalisierungsdatei erstellen?",
"prompt_unsaved_switch": "Im aktuellen Formular gibt es ungespeicherte Änderungen. Eintrag vor dem Fortfahren speichern?",
"prompt_unsaved_exit": "Im aktuellen Formular gibt es ungespeicherte Änderungen. Trotzdem beenden?",
"prompt_delete_entry": "Eintrag '{key}' löschen?",
"prompt_overwrite_entry": "Eintrag '{key}' existiert bereits. Überschreiben?",
"prompt_replace_language": "Sprache '{lang}' existiert bereits in der XML-Datei. Alle Einträge erneut für diese Sprache übersetzen?",
"prompt_language_code": "Sprachcode eingeben (z. B. de, fr, es, it, pt-BR):",
"prompt_language_code_all": "Sprachcode für Stapelübersetzung eingeben (z. B. ja, ko, tr, pt-BR):",
"msg_open_first": "Bitte zuerst eine XML-Datei öffnen oder erstellen.",
"msg_invalid_xml": "Die ausgewählte XML-Datei konnte nicht gelesen werden.\n\n{error}",
"msg_key_required": "Bitte eine Eintrags-ID eingeben.",
"msg_entry_saved": "Eintrag '{key}' wurde gespeichert.",
"msg_entry_deleted": "Eintrag '{key}' wurde gelöscht.",
"msg_nothing_selected": "Bitte zuerst einen Eintrag auswählen.",
"msg_language_exists": "Diese XML-Sprache existiert bereits.",
"msg_language_added": "Sprache '{lang}' wurde zum Editor hinzugefügt.",
"msg_no_translator": "Übersetzung ist nicht verfügbar.\nAbhängigkeit installieren: pip install deep-translator",
"msg_missing_source": "Quelltext ist leer.",
"msg_missing_source_lang": "Das Feld der Quellsprache ist nicht verfügbar.",
"msg_no_entries": "Die XML-Datei enthält keine Einträge.",
"msg_batch_finished": "Sprache '{lang}' verarbeitet.\nÜbersetzt: {ok}\nFehler/Übersprungen: {failed}\nGesamteinträge: {total}",
"msg_file_created": "Neue XML-Datei wurde erstellt.",
"msg_file_saved": "XML-Datei wurde gespeichert.",
"msg_save_before_xml": "Aktuellen Eintrag vor dem Speichern der XML-Datei speichern?",
"msg_search_empty": "Text eingeben, um die Eintragsliste zu filtern.",
"msg_same_ui_language": "Diese Programmsprache ist bereits ausgewählt.",
"progress_title": "Übersetzung läuft",
"progress_batch_label": "Übersetze {total} Einträge nach '{lang}'...",
"progress_current_label": "Aktuellen Eintrag übersetzen...",
"xml_language_list": "XML-Sprachen",
"current_file": "Aktuelle Datei",
"translator_status_on": "Übersetzer: verfügbar",
"translator_status_off": "Übersetzer: nicht verfügbar",
"field_text": "Text ({lang})",
"footer_hint": "Tipp: Doppelklicken Sie auf einen Eintrag in der Liste, um ihn schnell zu bearbeiten.",
"language_program": "Programmsprache",
},
"es": {
"app_title": "Unity Localization Master",
"header_title": "Editor de localización XML",
"header_subtitle": "Crea, edita y traduce archivos XML de localización estilo Unity.",
"menu_file": "Archivo",
"menu_new": "Crear XML...",
"menu_open": "Abrir XML...",
"menu_save": "Guardar XML",
"menu_save_as": "Guardar XML como...",
"menu_exit": "Salir",
"menu_edit": "Editar",
"menu_new_entry": "Nueva entrada",
"menu_delete_entry": "Eliminar entrada seleccionada",
"menu_clear_form": "Limpiar formulario",
"menu_tools": "Herramientas",
"menu_add_language": "Agregar idioma XML",
"menu_auto_translate": "Traducir automáticamente la entrada actual",
"menu_batch_language": "Agregar idioma y traducir todas las entradas",
"menu_help": "Ayuda",
"menu_about": "Acerca de",
"menu_ui_language": "Idioma del programa",
"toolbar_open": "Abrir XML",
"toolbar_create": "Crear XML",
"toolbar_save": "Guardar XML",
"toolbar_add_language": "Agregar idioma",
"toolbar_batch": "Traducir todo el archivo",
"left_panel_title": "Entradas",
"search": "Buscar",
"search_placeholder": "Buscar por clave o texto",
"refresh": "Actualizar",
"new_entry": "Nueva",
"delete_entry": "Eliminar",
"entry_id": "Clave de entrada (ID)",
"source_language": "Idioma de origen para traducción",
"translations": "Traducciones",
"editor_title_new": "Nueva entrada",
"editor_title_edit": "Editando: {key}",
"save_entry": "Guardar entrada",
"clear_form": "Limpiar formulario",
"auto_translate": "Traducir automáticamente la entrada actual",
"batch_translate": "Agregar idioma y traducir todas las entradas",
"status_ready": "Listo",
"status_no_file": "No se ha seleccionado un archivo XML",
"status_file_loaded": "Cargado: {path}",
"status_file_saved": "Guardado: {path}",
"status_entry_saved": "Entrada guardada: {key}",
"status_entry_deleted": "Entrada eliminada: {key}",
"status_language_added": "Idioma XML agregado: {lang}",
"status_search_results": "Encontrados: {count}",
"status_translation_done": "Traducción automática completada",
"status_batch_done": "Traducción masiva completada: {lang}",
"table_id": "ID",
"table_preview": "Vista previa EN",
"warning": "Advertencia",
"error": "Error",
"info": "Información",
"confirm": "Confirmación",
"about_title": "Acerca de",
"about_text": "Unity Localization Master\n\nEditor de escritorio con estilo production para archivos XML de localización.\nFunciones:\n• lista y búsqueda de entradas\n• interfaz traducida a 5 idiomas\n• traducción automática mediante Google Translator\n• generación masiva de idiomas\n• listo para compilar en EXE con PyInstaller",
"prompt_new_file": "¿Crear un nuevo archivo XML de localización?",
"prompt_unsaved_switch": "Hay cambios no guardados en el formulario actual. ¿Guardar la entrada antes de continuar?",
"prompt_unsaved_exit": "Hay cambios no guardados en el formulario actual. ¿Salir de todos modos?",
"prompt_delete_entry": "¿Eliminar la entrada '{key}'?",
"prompt_overwrite_entry": "La entrada '{key}' ya existe. ¿Sobrescribirla?",
"prompt_replace_language": "El idioma '{lang}' ya existe en el XML. ¿Volver a traducir todas las entradas para este idioma?",
"prompt_language_code": "Introduce un código de idioma (por ejemplo: de, fr, es, it, pt-BR):",
"prompt_language_code_all": "Introduce un código de idioma para traducción masiva (por ejemplo: ja, ko, tr, pt-BR):",
"msg_open_first": "Primero abre o crea un archivo XML.",
"msg_invalid_xml": "No se pudo leer el archivo XML seleccionado.\n\n{error}",
"msg_key_required": "Introduce una clave de entrada (ID).",
"msg_entry_saved": "La entrada '{key}' ha sido guardada.",
"msg_entry_deleted": "La entrada '{key}' ha sido eliminada.",
"msg_nothing_selected": "Primero selecciona una entrada.",
"msg_language_exists": "Este idioma XML ya existe.",
"msg_language_added": "El idioma '{lang}' ha sido agregado al editor.",
"msg_no_translator": "La traducción no está disponible.\nInstala la dependencia: pip install deep-translator",
"msg_missing_source": "El texto de origen está vacío.",
"msg_missing_source_lang": "El campo del idioma de origen no está disponible.",
"msg_no_entries": "No hay entradas en el archivo XML.",
"msg_batch_finished": "Idioma '{lang}' procesado.\nTraducidos: {ok}\nErrores/Omitidos: {failed}\nEntradas totales: {total}",
"msg_file_created": "Se ha creado un nuevo archivo XML.",
"msg_file_saved": "El archivo XML ha sido guardado.",
"msg_save_before_xml": "¿Guardar la entrada actual antes de guardar el XML?",
"msg_search_empty": "Escribe texto para filtrar la lista de entradas.",
"msg_same_ui_language": "Ese idioma del programa ya está seleccionado.",
"progress_title": "Traducción en progreso",
"progress_batch_label": "Traduciendo {total} entradas a '{lang}'...",
"progress_current_label": "Traduciendo la entrada actual...",
"xml_language_list": "Idiomas XML",
"current_file": "Archivo actual",
"translator_status_on": "Traductor: disponible",
"translator_status_off": "Traductor: no disponible",
"field_text": "Texto ({lang})",
"footer_hint": "Consejo: haz doble clic en una entrada de la lista para editarla rápidamente.",
"language_program": "Idioma del programa",
},
"fr": {
"app_title": "Unity Localization Master",
"header_title": "Éditeur de localisation XML",
"header_subtitle": "Créez, modifiez et traduisez des fichiers XML de localisation au style Unity.",
"menu_file": "Fichier",
"menu_new": "Créer un XML...",
"menu_open": "Ouvrir un XML...",
"menu_save": "Enregistrer le XML",
"menu_save_as": "Enregistrer le XML sous...",
"menu_exit": "Quitter",
"menu_edit": "Édition",
"menu_new_entry": "Nouvelle entrée",
"menu_delete_entry": "Supprimer l'entrée sélectionnée",
"menu_clear_form": "Effacer le formulaire",
"menu_tools": "Outils",
"menu_add_language": "Ajouter une langue XML",
"menu_auto_translate": "Traduire automatiquement l'entrée actuelle",
"menu_batch_language": "Ajouter une langue et traduire toutes les entrées",
"menu_help": "Aide",
"menu_about": "À propos",
"menu_ui_language": "Langue du programme",
"toolbar_open": "Ouvrir XML",
"toolbar_create": "Créer XML",
"toolbar_save": "Enregistrer XML",
"toolbar_add_language": "Ajouter une langue",
"toolbar_batch": "Traduire tout le fichier",
"left_panel_title": "Entrées",
"search": "Recherche",
"search_placeholder": "Rechercher par clé ou texte",
"refresh": "Actualiser",
"new_entry": "Nouvelle",
"delete_entry": "Supprimer",
"entry_id": "Clé de l'entrée (ID)",
"source_language": "Langue source pour la traduction",
"translations": "Traductions",
"editor_title_new": "Nouvelle entrée",
"editor_title_edit": "Modification : {key}",
"save_entry": "Enregistrer l'entrée",
"clear_form": "Effacer le formulaire",
"auto_translate": "Traduire automatiquement l'entrée actuelle",
"batch_translate": "Ajouter une langue et traduire toutes les entrées",
"status_ready": "Prêt",
"status_no_file": "Aucun fichier XML sélectionné",
"status_file_loaded": "Chargé : {path}",
"status_file_saved": "Enregistré : {path}",
"status_entry_saved": "Entrée enregistrée : {key}",
"status_entry_deleted": "Entrée supprimée : {key}",
"status_language_added": "Langue XML ajoutée : {lang}",
"status_search_results": "Trouvés : {count}",
"status_translation_done": "Traduction automatique terminée",
"status_batch_done": "Traduction par lot terminée : {lang}",
"table_id": "ID",
"table_preview": "Aperçu EN",
"warning": "Avertissement",
"error": "Erreur",
"info": "Information",
"confirm": "Confirmation",
"about_title": "À propos",
"about_text": "Unity Localization Master\n\nÉditeur de bureau de style production pour les fichiers XML de localisation.\nFonctionnalités :\n• liste et recherche d'entrées\n• interface localisée en 5 langues\n• traduction automatique via Google Translator\n• ajout massif de langues\n• prêt pour une compilation EXE avec PyInstaller",
"prompt_new_file": "Créer un nouveau fichier XML de localisation ?",
"prompt_unsaved_switch": "Le formulaire actuel contient des modifications non enregistrées. Enregistrer l'entrée avant de continuer ?",
"prompt_unsaved_exit": "Le formulaire actuel contient des modifications non enregistrées. Quitter quand même ?",
"prompt_delete_entry": "Supprimer l'entrée '{key}' ?",
"prompt_overwrite_entry": "L'entrée '{key}' existe déjà. L'écraser ?",
"prompt_replace_language": "La langue '{lang}' existe déjà dans le XML. Retraduire toutes les entrées pour cette langue ?",
"prompt_language_code": "Entrez un code de langue (par ex. : de, fr, es, it, pt-BR) :",
"prompt_language_code_all": "Entrez un code de langue pour la traduction par lot (par ex. : ja, ko, tr, pt-BR) :",
"msg_open_first": "Veuillez d'abord ouvrir ou créer un fichier XML.",
"msg_invalid_xml": "Impossible de lire le fichier XML sélectionné.\n\n{error}",
"msg_key_required": "Veuillez saisir une clé d'entrée (ID).",
"msg_entry_saved": "L'entrée '{key}' a été enregistrée.",
"msg_entry_deleted": "L'entrée '{key}' a été supprimée.",
"msg_nothing_selected": "Veuillez d'abord sélectionner une entrée.",
"msg_language_exists": "Cette langue XML existe déjà.",
"msg_language_added": "La langue '{lang}' a été ajoutée à l'éditeur.",
"msg_no_translator": "La traduction n'est pas disponible.\nInstallez la dépendance : pip install deep-translator",
"msg_missing_source": "Le texte source est vide.",
"msg_missing_source_lang": "Le champ de langue source n'est pas disponible.",
"msg_no_entries": "Le fichier XML ne contient aucune entrée.",
"msg_batch_finished": "Langue '{lang}' traitée.\nTraduites : {ok}\nErreurs/Ignorées : {failed}\nNombre total d'entrées : {total}",
"msg_file_created": "Un nouveau fichier XML a été créé.",
"msg_file_saved": "Le fichier XML a été enregistré.",
"msg_save_before_xml": "Enregistrer l'entrée actuelle avant d'enregistrer le XML ?",
"msg_search_empty": "Saisissez du texte pour filtrer la liste des entrées.",
"msg_same_ui_language": "Cette langue du programme est déjà sélectionnée.",
"progress_title": "Traduction en cours",
"progress_batch_label": "Traduction de {total} entrées vers '{lang}'...",
"progress_current_label": "Traduction de l'entrée actuelle...",
"xml_language_list": "Langues XML",
"current_file": "Fichier actuel",
"translator_status_on": "Traducteur : disponible",
"translator_status_off": "Traducteur : indisponible",
"field_text": "Texte ({lang})",
"footer_hint": "Astuce : double-cliquez sur une entrée dans la liste pour l'ouvrir rapidement en modification.",
"language_program": "Langue du programme",
},
}
EXTRA_UI_TEXTS = {
"en": {
"menu_tutorial": "Quick tutorial",
"tutorial_window_title": "Quick tutorial",
"tutorial_step_counter": "Step {current} of {total}",
"tutorial_prev": "Previous",
"tutorial_next": "Next",
"tutorial_close": "Close",
"tutorial_focus": "Show",
"tutorial_intro_body": "This short tour shows the main workflow: open or create XML, pick an entry, edit translations and save the result.",
"tooltip_open": "Open an existing XML localization file.",
"tooltip_create": "Create a new empty XML localization file.",
"tooltip_save_xml": "Write all current entries back to the XML file on disk.",
"tooltip_add_language": "Add a new language column to the current XML file.",
"tooltip_batch": "Generate a new language for the whole file using automatic translation.",
"tooltip_search": "Filter the entry list by key or translated text.",
"tooltip_entries": "This list contains all localization keys. Double-click an item to edit it.",
"tooltip_entry_id": "Each localization entry needs a unique key used by the game or app.",
"tooltip_source_language": "Choose which language will be used as the source for automatic translation.",
"tooltip_save_entry": "Save the current entry to the XML data.",
"tooltip_clear_form": "Clear the editor and start a new entry.",
"tooltip_auto_translate": "Fill the other language fields from the selected source language.",
"tooltip_translations": "Edit the texts for every XML language here.",
"tooltip_language_switch": "Switch the program interface language instantly.",
"tooltip_current_file": "Shows the XML file currently opened in the editor.",
},
"ru": {
"menu_tutorial": "Быстрое обучение",
"tutorial_window_title": "Быстрое обучение",
"tutorial_step_counter": "Шаг {current} из {total}",
"tutorial_prev": "Назад",
"tutorial_next": "Далее",
"tutorial_close": "Закрыть",
"tutorial_focus": "Показать",
"tutorial_intro_body": "Этот короткий тур показывает основной сценарий работы: открыть или создать XML, выбрать запись, отредактировать переводы и сохранить результат.",
"tooltip_open": "Открыть существующий XML-файл локализации.",
"tooltip_create": "Создать новый пустой XML-файл локализации.",
"tooltip_save_xml": "Записать все текущие записи обратно в XML-файл на диске.",
"tooltip_add_language": "Добавить новый языковой столбец в текущий XML-файл.",
"tooltip_batch": "Сгенерировать новый язык для всего файла с помощью автоперевода.",
"tooltip_search": "Фильтровать список записей по ключу или переведённому тексту.",
"tooltip_entries": "Здесь находятся все ключи локализации. Дважды кликните по записи, чтобы отредактировать её.",
"tooltip_entry_id": "Каждой записи локализации нужен уникальный ключ, который использует игра или приложение.",
"tooltip_source_language": "Выберите язык, который будет исходным для автоматического перевода.",
"tooltip_save_entry": "Сохранить текущую запись в XML-данные.",
"tooltip_clear_form": "Очистить редактор и начать новую запись.",
"tooltip_auto_translate": "Заполнить остальные языки на основе выбранного исходного языка.",
"tooltip_translations": "Редактируйте здесь тексты для всех языков XML.",
"tooltip_language_switch": "Мгновенно переключить язык интерфейса программы.",
"tooltip_current_file": "Показывает XML-файл, который сейчас открыт в редакторе.",
},
"de": {
"menu_tutorial": "Kurzanleitung",
"tutorial_window_title": "Kurzanleitung",
"tutorial_step_counter": "Schritt {current} von {total}",
"tutorial_prev": "Zurück",
"tutorial_next": "Weiter",
"tutorial_close": "Schließen",
"tutorial_focus": "Anzeigen",
"tutorial_intro_body": "Diese kurze Tour zeigt den Hauptablauf: XML öffnen oder erstellen, einen Eintrag wählen, Übersetzungen bearbeiten und das Ergebnis speichern.",
"tooltip_open": "Eine vorhandene XML-Lokalisierungsdatei öffnen.",
"tooltip_create": "Eine neue leere XML-Lokalisierungsdatei erstellen.",
"tooltip_save_xml": "Alle aktuellen Einträge zurück in die XML-Datei auf dem Datenträger schreiben.",
"tooltip_add_language": "Eine neue Sprachspalte zur aktuellen XML-Datei hinzufügen.",
"tooltip_batch": "Eine neue Sprache für die gesamte Datei per automatischer Übersetzung erzeugen.",
"tooltip_search": "Die Eintragsliste nach Schlüssel oder übersetztem Text filtern.",
"tooltip_entries": "Diese Liste enthält alle Lokalisierungsschlüssel. Doppelklicken Sie auf einen Eintrag, um ihn zu bearbeiten.",
"tooltip_entry_id": "Jeder Lokalisierungseintrag benötigt einen eindeutigen Schlüssel, der vom Spiel oder der App verwendet wird.",
"tooltip_source_language": "Wählen Sie die Sprache, die als Quelle für die automatische Übersetzung verwendet wird.",
"tooltip_save_entry": "Den aktuellen Eintrag in den XML-Daten speichern.",
"tooltip_clear_form": "Den Editor leeren und einen neuen Eintrag beginnen.",
"tooltip_auto_translate": "Die anderen Sprachfelder aus der gewählten Quellsprache füllen.",
"tooltip_translations": "Bearbeiten Sie hier die Texte für alle XML-Sprachen.",
"tooltip_language_switch": "Die Programmsprache sofort umschalten.",
"tooltip_current_file": "Zeigt die XML-Datei an, die aktuell im Editor geöffnet ist.",
},
"es": {
"menu_tutorial": "Tutorial rápido",
"tutorial_window_title": "Tutorial rápido",
"tutorial_step_counter": "Paso {current} de {total}",
"tutorial_prev": "Anterior",
"tutorial_next": "Siguiente",
"tutorial_close": "Cerrar",
"tutorial_focus": "Mostrar",
"tutorial_intro_body": "Este recorrido corto muestra el flujo principal: abrir o crear XML, elegir una entrada, editar traducciones y guardar el resultado.",
"tooltip_open": "Abrir un archivo XML de localización existente.",
"tooltip_create": "Crear un archivo XML de localización vacío.",
"tooltip_save_xml": "Escribir todas las entradas actuales de nuevo en el archivo XML del disco.",
"tooltip_add_language": "Agregar una nueva columna de idioma al archivo XML actual.",
"tooltip_batch": "Generar un nuevo idioma para todo el archivo usando traducción automática.",
"tooltip_search": "Filtrar la lista de entradas por clave o texto traducido.",
"tooltip_entries": "Esta lista contiene todas las claves de localización. Haz doble clic en un elemento para editarlo.",
"tooltip_entry_id": "Cada entrada de localización necesita una clave única usada por el juego o la aplicación.",
"tooltip_source_language": "Elige el idioma que se usará como fuente para la traducción automática.",
"tooltip_save_entry": "Guardar la entrada actual en los datos XML.",
"tooltip_clear_form": "Limpiar el editor y empezar una nueva entrada.",
"tooltip_auto_translate": "Rellenar los demás idiomas desde el idioma de origen seleccionado.",
"tooltip_translations": "Edita aquí los textos para todos los idiomas XML.",
"tooltip_language_switch": "Cambiar al instante el idioma de la interfaz del programa.",
"tooltip_current_file": "Muestra el archivo XML que está abierto actualmente en el editor.",
},
"fr": {
"menu_tutorial": "Tutoriel rapide",
"tutorial_window_title": "Tutoriel rapide",
"tutorial_step_counter": "Étape {current} sur {total}",
"tutorial_prev": "Précédent",
"tutorial_next": "Suivant",
"tutorial_close": "Fermer",
"tutorial_focus": "Afficher",
"tutorial_intro_body": "Cette courte visite montre le flux principal : ouvrir ou créer un XML, choisir une entrée, modifier les traductions et enregistrer le résultat.",
"tooltip_open": "Ouvrir un fichier XML de localisation existant.",
"tooltip_create": "Créer un nouveau fichier XML de localisation vide.",
"tooltip_save_xml": "Écrire toutes les entrées actuelles dans le fichier XML sur le disque.",
"tooltip_add_language": "Ajouter une nouvelle colonne de langue au fichier XML actuel.",
"tooltip_batch": "Générer une nouvelle langue pour tout le fichier à l'aide de la traduction automatique.",
"tooltip_search": "Filtrer la liste des entrées par clé ou par texte traduit.",
"tooltip_entries": "Cette liste contient toutes les clés de localisation. Double-cliquez sur un élément pour le modifier.",
"tooltip_entry_id": "Chaque entrée de localisation a besoin d'une clé unique utilisée par le jeu ou l'application.",
"tooltip_source_language": "Choisissez la langue qui servira de source pour la traduction automatique.",
"tooltip_save_entry": "Enregistrer l'entrée actuelle dans les données XML.",
"tooltip_clear_form": "Effacer l'éditeur et commencer une nouvelle entrée.",
"tooltip_auto_translate": "Remplir les autres langues à partir de la langue source sélectionnée.",
"tooltip_translations": "Modifiez ici les textes pour toutes les langues XML.",
"tooltip_language_switch": "Changer instantanément la langue de l'interface du programme.",
"tooltip_current_file": "Affiche le fichier XML actuellement ouvert dans l'éditeur.",
},
}
for lang_code, localized_values in EXTRA_UI_TEXTS.items():
UI_TEXTS.setdefault(lang_code, {}).update(localized_values)
def ensure_app_dir():
if os.name == "nt":
base = os.getenv("APPDATA") or os.path.expanduser("~")
else:
base = os.path.join(os.path.expanduser("~"), ".config")
app_dir = os.path.join(base, APP_ID)
os.makedirs(app_dir, exist_ok=True)
return app_dir
APP_DIR = ensure_app_dir()
SETTINGS_PATH = os.path.join(APP_DIR, "settings.json")
LOG_PATH = os.path.join(APP_DIR, "app.log")
def setup_logging():
logging.basicConfig(
filename=LOG_PATH,
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
)
class ToolTip:
def __init__(self, widget, text="", delay=350):
self.widget = widget
self.text = text
self.delay = delay
self.tip_window = None
self.after_id = None
self.widget.bind("<Enter>", self.schedule, add="+")
self.widget.bind("<Leave>", self.hide, add="+")
self.widget.bind("<ButtonPress>", self.hide, add="+")
def set_text(self, text):
self.text = text
def schedule(self, _event=None):
self.unschedule()
self.after_id = self.widget.after(self.delay, self.show)
def unschedule(self):
if self.after_id:
self.widget.after_cancel(self.after_id)
self.after_id = None
def show(self):
if self.tip_window or not self.text:
return
x = self.widget.winfo_rootx() + 16
y = self.widget.winfo_rooty() + self.widget.winfo_height() + 10
self.tip_window = tw = tk.Toplevel(self.widget)
tw.wm_overrideredirect(True)
try:
tw.attributes("-topmost", True)
except Exception:
pass
tw.configure(bg="#111827")
label = tk.Label(
tw,
text=self.text,
justify="left",
bg="#111827",
fg="#F9FAFB",
padx=10,
pady=8,
font=("Segoe UI", 9),
wraplength=320,
)
label.pack()
tw.wm_geometry(f"+{x}+{y}")
def hide(self, _event=None):
self.unschedule()
if self.tip_window is not None:
self.tip_window.destroy()
self.tip_window = None
class LocalizationEditorApp:
def __init__(self, root: tk.Tk):
self.root = root
self.root.minsize(1180, 760)
self.root.protocol("WM_DELETE_WINDOW", self.on_close)
self.settings = self.load_settings()
self.ui_language = self.settings.get("ui_language", "en")
if self.ui_language not in UI_LANGUAGES:
self.ui_language = "en"
self.xml_path = ""
self.entries = []
self.languages = []
self.filtered_entry_ids = []
self.current_entry_id = None
self.form_dirty = False
self.widgets_ready = False
self.text_inputs = {}
self.translation_frames = {}
self.tooltip_objects = {}
self.tutorial_window = None
self.tutorial_step_index = 0
self.tutorial_steps_cache = []
self.style = ttk.Style()
self.configure_styles()
self.translator_available = GoogleTranslator is not None
if self.translator_available:
try:
GoogleTranslator(source="en", target="de").translate("test")
except Exception as exc:
logging.warning("Translator self-test failed: %s", exc)
self.translator_available = False
self.search_var = tk.StringVar()
self.entry_id_var = tk.StringVar()
self.source_lang_var = tk.StringVar()
self.ui_lang_var = tk.StringVar(value=self.ui_language)
self.status_var = tk.StringVar()
self.current_file_var = tk.StringVar()
self.languages_var = tk.StringVar()
self.translator_var = tk.StringVar()
self.editor_title_var = tk.StringVar()
self.build_ui()
self.restore_window_geometry()
self.apply_ui_language(initial=True)
self.mark_clean()
self.root.after(700, self.maybe_show_startup_tutorial)
def tr(self, key: str, **kwargs) -> str:
text = UI_TEXTS.get(self.ui_language, UI_TEXTS["en"]).get(key)
if text is None:
text = UI_TEXTS["en"].get(key, key)
if kwargs:
try:
return text.format(**kwargs)
except Exception:
return text
return text
def load_settings(self):
if not os.path.exists(SETTINGS_PATH):
return {}
try:
with open(SETTINGS_PATH, "r", encoding="utf-8") as f:
return json.load(f)
except Exception as exc:
logging.warning("Failed to load settings: %s", exc)
return {}
def save_settings(self):
data = {
"ui_language": self.ui_language,
"geometry": self.root.geometry(),
"last_xml_path": self.xml_path,
"tutorial_seen": self.settings.get("tutorial_seen", False),
}
try:
with open(SETTINGS_PATH, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
except Exception as exc:
logging.warning("Failed to save settings: %s", exc)
def restore_window_geometry(self):
geometry = self.settings.get("geometry")
if geometry:
try:
self.root.geometry(geometry)
except Exception:
self.root.geometry("1280x820")
else:
self.root.geometry("1280x820")
def configure_styles(self):
self.style.theme_use("clam")
colors = {
"bg": "#F4F7FB",
"surface": "#FFFFFF",
"surface_alt": "#EEF3F9",
"border": "#D7E0EA",
"text": "#1F2937",
"muted": "#6B7280",
"accent": "#2563EB",
"accent_hover": "#1D4ED8",
"success": "#16A34A",
"success_hover": "#15803D",
"danger": "#DC2626",
"danger_hover": "#B91C1C",
}
self.colors = colors
self.root.configure(bg=colors["bg"])
self.style.configure("TFrame", background=colors["bg"])
self.style.configure("Surface.TFrame", background=colors["surface"])
self.style.configure("Toolbar.TFrame", background=colors["bg"])
self.style.configure("Header.TFrame", background=colors["bg"])
self.style.configure(
"Card.TLabelframe",
background=colors["surface"],
foreground=colors["text"],
borderwidth=1,
relief="solid",
)
self.style.configure(
"Card.TLabelframe.Label",
background=colors["surface"],
foreground=colors["text"],
font=("Segoe UI", 11, "bold"),
)
self.style.configure("TLabel", background=colors["bg"], foreground=colors["text"], font=("Segoe UI", 10))
self.style.configure("Title.TLabel", background=colors["bg"], foreground=colors["text"], font=("Segoe UI", 20, "bold"))
self.style.configure("Subtitle.TLabel", background=colors["bg"], foreground=colors["muted"], font=("Segoe UI", 10))
self.style.configure("Section.TLabel", background=colors["surface"], foreground=colors["text"], font=("Segoe UI", 10, "bold"))
self.style.configure("Hint.TLabel", background=colors["bg"], foreground=colors["muted"], font=("Segoe UI", 9))
self.style.configure("Info.TLabel", background=colors["surface"], foreground=colors["muted"], font=("Segoe UI", 9))
self.style.configure(
"TButton",
font=("Segoe UI", 10),
padding=(14, 8),
borderwidth=0,
)
self.style.map("TButton", background=[("active", colors["surface_alt"])])
self.style.configure(
"Accent.TButton",
background=colors["accent"],
foreground="#FFFFFF",
padding=(16, 9),
borderwidth=0,
)
self.style.map(
"Accent.TButton",
background=[("active", colors["accent_hover"]), ("pressed", colors["accent_hover"])],
foreground=[("disabled", "#D1D5DB")],
)
self.style.configure(
"Success.TButton",
background=colors["success"],
foreground="#FFFFFF",
padding=(16, 9),
borderwidth=0,
)
self.style.map(
"Success.TButton",
background=[("active", colors["success_hover"]), ("pressed", colors["success_hover"])],
)
self.style.configure(
"Danger.TButton",
background=colors["danger"],
foreground="#FFFFFF",
padding=(16, 9),
borderwidth=0,
)
self.style.map(
"Danger.TButton",
background=[("active", colors["danger_hover"]), ("pressed", colors["danger_hover"])],
)
self.style.configure(
"TEntry",
fieldbackground=colors["surface"],
bordercolor=colors["border"],
lightcolor=colors["border"],
darkcolor=colors["border"],
padding=8,
)
self.style.configure(
"TCombobox",
fieldbackground=colors["surface"],
background=colors["surface"],
bordercolor=colors["border"],
arrowsize=14,
padding=6,
)
self.style.configure(
"Treeview",
background=colors["surface"],
fieldbackground=colors["surface"],
foreground=colors["text"],
bordercolor=colors["border"],
rowheight=30,
font=("Segoe UI", 10),
)
self.style.configure(
"Treeview.Heading",
background=colors["surface_alt"],
foreground=colors["text"],
font=("Segoe UI", 10, "bold"),
relief="flat",
)
self.style.map("Treeview", background=[("selected", colors["accent"])], foreground=[("selected", "#FFFFFF")])
self.style.configure("TSeparator", background=colors["border"])
self.style.configure("TProgressbar", troughcolor=colors["surface_alt"], bordercolor=colors["border"], background=colors["accent"], lightcolor=colors["accent"], darkcolor=colors["accent"])
def build_ui(self):
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(2, weight=1)
self.menu_bar = tk.Menu(self.root)
self.root.config(menu=self.menu_bar)
self.file_menu = tk.Menu(self.menu_bar, tearoff=0)
self.edit_menu = tk.Menu(self.menu_bar, tearoff=0)
self.tools_menu = tk.Menu(self.menu_bar, tearoff=0)
self.language_menu = tk.Menu(self.menu_bar, tearoff=0)
self.help_menu = tk.Menu(self.menu_bar, tearoff=0)
self.build_menus()
header = ttk.Frame(self.root, style="Header.TFrame")
header.grid(row=0, column=0, sticky="ew", padx=18, pady=(18, 8))
header.columnconfigure(0, weight=1)
header.columnconfigure(1, weight=0)
title_box = ttk.Frame(header, style="Header.TFrame")
title_box.grid(row=0, column=0, sticky="w")
self.header_title_label = ttk.Label(title_box, style="Title.TLabel")
self.header_title_label.pack(anchor="w")
self.header_subtitle_label = ttk.Label(title_box, style="Subtitle.TLabel")
self.header_subtitle_label.pack(anchor="w", pady=(2, 0))
lang_box = ttk.Frame(header, style="Header.TFrame")
lang_box.grid(row=0, column=1, sticky="e")
self.program_language_label = ttk.Label(lang_box)
self.program_language_label.pack(anchor="e")
self.ui_lang_combo = ttk.Combobox(
lang_box,
state="readonly",
width=18,
values=[UI_LANGUAGE_NAMES[code] for code in UI_LANGUAGES],
)
self.ui_lang_combo.pack(anchor="e", pady=(6, 0))
self.ui_lang_combo.current(UI_LANGUAGES.index(self.ui_language))
self.ui_lang_combo.bind("<<ComboboxSelected>>", self.on_ui_language_changed)
toolbar = ttk.Frame(self.root, style="Toolbar.TFrame")
toolbar.grid(row=1, column=0, sticky="ew", padx=18, pady=(0, 10))
for col in range(6):
toolbar.columnconfigure(col, weight=0)
toolbar.columnconfigure(6, weight=1)
self.open_button = ttk.Button(toolbar, command=self.open_xml_file)
self.open_button.grid(row=0, column=0, padx=(0, 8), pady=4)
self.create_button = ttk.Button(toolbar, command=self.create_xml_file)
self.create_button.grid(row=0, column=1, padx=(0, 8), pady=4)
self.save_xml_button = ttk.Button(toolbar, style="Success.TButton", command=self.save_xml_file)
self.save_xml_button.grid(row=0, column=2, padx=(0, 8), pady=4)
self.add_language_button = ttk.Button(toolbar, command=self.add_new_language)
self.add_language_button.grid(row=0, column=3, padx=(0, 8), pady=4)
self.batch_button = ttk.Button(toolbar, style="Accent.TButton", command=self.add_language_and_translate_all)
self.batch_button.grid(row=0, column=4, padx=(0, 8), pady=4)