def __init__(self, master, config, *args, **kwargs): super().__init__(master, *args, **kwargs) self.config = init_section(config, section_name='translate_external_files') config = self.config tk.Label(self, text='Dwarf Fortress root path:').grid() self.fileentry_df_root_path = FileEntry( self, dialogtype='askdirectory', default_path=config.get('df_root_path', ''), on_change=lambda text: check_and_save_path(self.config, 'df_root_path', text), ) self.fileentry_df_root_path.grid(row=0, column=1, sticky='WE') tk.Label(self, text="Translation files' directory:").grid() self.fileentry_translation_files = FileEntry( self, dialogtype='askdirectory', default_path=config.get('translation_files_path', ''), on_change=lambda text: self.on_change_translation_files_path(self.config, 'translation_files_path', text), ) self.fileentry_translation_files.grid(row=1, column=1, sticky='WE') tk.Label(self, text="Language:").grid() self.combo_language = ComboboxCustom(self) self.combo_language.grid(row=2, column=1, sticky='WE') directory = self.fileentry_translation_files.text if path.exists(directory): languages = self.get_languages(directory) self.combo_language.values = languages if languages: self.combo_language.current(0) self.combo_language.bind('<<ComboboxSelected>>', self.update_listbox_translation_files) tk.Label(self, text="Encoding:").grid() self.combo_encoding = ComboboxCustom(self) self.combo_encoding.grid(row=3, column=1, sticky='WE') self.update_combo_encoding() self.listbox_translation_files = ListboxCustom(self) self.listbox_translation_files.grid(columnspan=2, sticky='NSWE') self.update_listbox_translation_files(language=self.combo_language.text) ttk.Button(self, text='Search', command=self.bt_search).grid() ttk.Button(self, text='Translate', command=lambda: self.bt_search(translate=True)).grid(row=5, column=1) self.listbox_found_directories = ListboxCustom(self) self.listbox_found_directories.grid(columnspan=2, sticky='NSWE') self.grid_columnconfigure(1, weight=1)
class PatchExecutableFrame(tk.Frame): def update_log(self, message_queue): try: while message_queue.poll(): self.log_field.write(message_queue.recv()) if not self.dfrus_process.is_alive(): self.log_field.write('\n[PROCESS FINISHED]') self.button_patch.reset_state() else: self.after(100, self.update_log, message_queue) except (EOFError, BrokenPipeError): self.log_field.write('\n[MESSAGE QUEUE/PIPE BROKEN]') self.button_patch.reset_state() def load_dictionary(self, translation_file): with open(translation_file, 'r', encoding='utf-8') as fn: pofile = po.PoReader(fn) meta = pofile.meta dictionary = OrderedDict( cleanup_spaces(((entry['msgid'], cleanup_special_symbols(entry['msgstr'])) for entry in pofile), self.exclusions.get(meta['Language'], self.exclusions)) ) return dictionary @property def dictionary(self): if not self._dictionary: translation_file = self.fileentry_translation_file.text if self.fileentry_translation_file.path_is_valid(): self._dictionary = self.load_dictionary(translation_file) return self._dictionary def bt_patch(self): if self.dfrus_process is not None and self.dfrus_process.is_alive(): return False executable_file = self.fileentry_executable_file.text if not executable_file or not path.exists(executable_file): messagebox.showerror('Error', 'Valid path to an executable file must be specified') elif not self.dictionary: messagebox.showerror('Error', "Dictionary wasn't loaded") else: if not self.debug_frame: dictionary = self.dictionary else: dictionary = OrderedDict(self.debug_frame.bisect.filtered_strings) self.config['last_encoding'] = self.combo_encoding.text parent_conn, child_conn = mp.Pipe() self.after(100, self.update_log, parent_conn) self.log_field.clear() self.dfrus_process = mp.Process( target=dfrus.run, kwargs=dict( path=executable_file, dest='', trans_table=dictionary, codepage=self.combo_encoding.text, debug=self.chk_debug_output.is_checked, stdout=ProcessMessageWrapper(child_conn) ) ) self.dfrus_process.start() return True return False def bt_stop(self): r = messagebox.showwarning('Are you sure?', 'Stop the patching process?', type=messagebox.OKCANCEL) if r == 'cancel': return False else: self.dfrus_process.terminate() return True def kill_processes(self, _): if self.dfrus_process and self.dfrus_process.is_alive(): self.dfrus_process.terminate() def bt_exclusions(self): translation_file = self.fileentry_translation_file.text language = None dictionary = None if translation_file and path.exists(translation_file): with open(translation_file, 'r', encoding='utf-8') as fn: pofile = po.PoReader(fn) meta = pofile.meta language = meta['Language'] dictionary = {entry['msgid']: entry['msgstr'] for entry in pofile} dialog = DialogDontFixSpaces(self, self.config['fix_space_exclusions'], language, dictionary) self.config['fix_space_exclusions'] = dialog.exclusions or self.config['fix_space_exclusions'] self.exclusions = self.config['fix_space_exclusions'] def setup_checkbutton(self, text, config_key, default_state): config = self.config def save_checkbox_state(event, option_name): config[option_name] = not event.widget.is_checked # Event occurs before the widget changes state check = CheckbuttonVar(self, text=text) check.bind('<1>', lambda event: save_checkbox_state(event, config_key)) check.is_checked = config[config_key] = config.get(config_key, default_state) return check def update_combo_encoding(self, text): check_and_save_path(self.config, 'df_exe_translation_file', text) # Update codepage combobox # TODO: Cache supported codepages' list codepages = get_codepages().keys() if self.fileentry_translation_file.path_is_valid(): translation_file = self.fileentry_translation_file.text with open(translation_file, 'r', encoding='utf-8') as fn: pofile = po.PoReader(fn) self.translation_file_language = pofile.meta['Language'] strings = [cleanup_special_symbols(entry['msgstr']) for entry in pofile] codepages = filter_codepages(codepages, strings) self.combo_encoding.values = sorted(codepages, key=lambda x: int(x.strip(string.ascii_letters))) if self.translation_file_language not in self.config['language_codepages']: if self.combo_encoding.values: self.combo_encoding.current(0) else: self.combo_encoding.text = 'cp437' else: self.combo_encoding.text = self.config['language_codepages'][self.translation_file_language] def __init__(self, master, config, debug=False): super().__init__(master) self.config = init_section( config, section_name='patch_executable', defaults=dict( fix_space_exclusions=dict(ru=['Histories of ']), language_codepages=dict(), ) ) config = self.config self.exclusions = config['fix_space_exclusions'] self.dfrus_process = None self._dictionary = None tk.Label(self, text='DF executable file:').grid() self.fileentry_executable_file = FileEntry( self, dialogtype='askopenfilename', filetypes=[('Executable files', '*.exe')], default_path=config.get('df_executable', ''), on_change=lambda text: check_and_save_path(self.config, 'df_executable', text), ) self.fileentry_executable_file.grid(column=1, row=0, columnspan=2, sticky='EW') tk.Label(self, text='DF executable translation file:').grid() self.fileentry_translation_file = FileEntry( self, dialogtype='askopenfilename', filetypes=[ ("Hardcoded strings' translation", '*hardcoded*.po'), ('Translation files', '*.po'), # ('csv file', '*.csv'), # @TODO: Currently not supported ], default_path=config.get('df_exe_translation_file', ''), on_change=self.update_combo_encoding, change_color=True ) self.fileentry_translation_file.grid(column=1, row=1, columnspan=2, sticky='EW') tk.Label(self, text='Encoding:').grid() self.combo_encoding = ComboboxCustom(self) self.combo_encoding.grid(column=1, row=2, sticky=tk.E + tk.W) codepages = get_codepages().keys() if not self.fileentry_translation_file.path_is_valid(): self.translation_file_language = None else: translation_file = self.fileentry_translation_file.text with open(translation_file, 'r', encoding='utf-8') as fn: pofile = po.PoReader(fn) self.translation_file_language = pofile.meta['Language'] strings = [cleanup_special_symbols(entry['msgstr']) for entry in pofile] codepages = filter_codepages(codepages, strings) self.combo_encoding.values = sorted(codepages, key=lambda x: int(x.strip(string.ascii_letters))) if 'last_encoding' in config: self.combo_encoding.text = config['last_encoding'] else: self.combo_encoding.current(0) def save_encoding_into_config(event): config['last_encoding'] = event.widget.text if self.translation_file_language: config['language_codepages'][self.translation_file_language] = event.widget.text self.combo_encoding.bind('<<ComboboxSelected>>', func=save_encoding_into_config) # FIXME: chk_dont_patch_charmap does nothing self.chk_dont_patch_charmap = self.setup_checkbutton( text="Don't patch charmap table", config_key='dont_patch_charmap', default_state=False) self.chk_dont_patch_charmap.grid(column=1, sticky=tk.W) self.chk_add_leading_trailing_spaces = self.setup_checkbutton( text='Add necessary leading/trailing spaces', config_key='add_leading_trailing_spaces', default_state=True) self.chk_add_leading_trailing_spaces.grid(columnspan=2, sticky=tk.W) button_exclusions = ttk.Button(self, text='Exclusions...', command=self.bt_exclusions) button_exclusions.grid(row=4, column=2) self.debug_frame = None if not debug else DebugFrame(self, dictionary=self.dictionary) if self.debug_frame: self.debug_frame.grid(columnspan=3, sticky='NSWE') self.grid_rowconfigure(5, weight=1) self.chk_debug_output = self.setup_checkbutton( text='Enable debugging output', config_key='debug_output', default_state=False) self.chk_debug_output.grid(columnspan=2, sticky=tk.W) self.button_patch = TwoStateButton(self, text='Patch!', command=self.bt_patch, text2='Stop!', command2=self.bt_stop) self.button_patch.grid(row=6, column=2) self.log_field = CustomText(self, width=48, height=8, enabled=False) self.log_field.grid(columnspan=3, sticky='NSWE') self.grid_rowconfigure(self.log_field.grid_info()['row'], weight=1) self.grid_columnconfigure(1, weight=1) self.bind('<Destroy>', self.kill_processes)
def __init__(self, master, config, debug=False): super().__init__(master) self.config = init_section( config, section_name='patch_executable', defaults=dict( fix_space_exclusions=dict(ru=['Histories of ']), language_codepages=dict(), ) ) config = self.config self.exclusions = config['fix_space_exclusions'] self.dfrus_process = None self._dictionary = None tk.Label(self, text='DF executable file:').grid() self.fileentry_executable_file = FileEntry( self, dialogtype='askopenfilename', filetypes=[('Executable files', '*.exe')], default_path=config.get('df_executable', ''), on_change=lambda text: check_and_save_path(self.config, 'df_executable', text), ) self.fileentry_executable_file.grid(column=1, row=0, columnspan=2, sticky='EW') tk.Label(self, text='DF executable translation file:').grid() self.fileentry_translation_file = FileEntry( self, dialogtype='askopenfilename', filetypes=[ ("Hardcoded strings' translation", '*hardcoded*.po'), ('Translation files', '*.po'), # ('csv file', '*.csv'), # @TODO: Currently not supported ], default_path=config.get('df_exe_translation_file', ''), on_change=self.update_combo_encoding, change_color=True ) self.fileentry_translation_file.grid(column=1, row=1, columnspan=2, sticky='EW') tk.Label(self, text='Encoding:').grid() self.combo_encoding = ComboboxCustom(self) self.combo_encoding.grid(column=1, row=2, sticky=tk.E + tk.W) codepages = get_codepages().keys() if not self.fileentry_translation_file.path_is_valid(): self.translation_file_language = None else: translation_file = self.fileentry_translation_file.text with open(translation_file, 'r', encoding='utf-8') as fn: pofile = po.PoReader(fn) self.translation_file_language = pofile.meta['Language'] strings = [cleanup_special_symbols(entry['msgstr']) for entry in pofile] codepages = filter_codepages(codepages, strings) self.combo_encoding.values = sorted(codepages, key=lambda x: int(x.strip(string.ascii_letters))) if 'last_encoding' in config: self.combo_encoding.text = config['last_encoding'] else: self.combo_encoding.current(0) def save_encoding_into_config(event): config['last_encoding'] = event.widget.text if self.translation_file_language: config['language_codepages'][self.translation_file_language] = event.widget.text self.combo_encoding.bind('<<ComboboxSelected>>', func=save_encoding_into_config) # FIXME: chk_dont_patch_charmap does nothing self.chk_dont_patch_charmap = self.setup_checkbutton( text="Don't patch charmap table", config_key='dont_patch_charmap', default_state=False) self.chk_dont_patch_charmap.grid(column=1, sticky=tk.W) self.chk_add_leading_trailing_spaces = self.setup_checkbutton( text='Add necessary leading/trailing spaces', config_key='add_leading_trailing_spaces', default_state=True) self.chk_add_leading_trailing_spaces.grid(columnspan=2, sticky=tk.W) button_exclusions = ttk.Button(self, text='Exclusions...', command=self.bt_exclusions) button_exclusions.grid(row=4, column=2) self.debug_frame = None if not debug else DebugFrame(self, dictionary=self.dictionary) if self.debug_frame: self.debug_frame.grid(columnspan=3, sticky='NSWE') self.grid_rowconfigure(5, weight=1) self.chk_debug_output = self.setup_checkbutton( text='Enable debugging output', config_key='debug_output', default_state=False) self.chk_debug_output.grid(columnspan=2, sticky=tk.W) self.button_patch = TwoStateButton(self, text='Patch!', command=self.bt_patch, text2='Stop!', command2=self.bt_stop) self.button_patch.grid(row=6, column=2) self.log_field = CustomText(self, width=48, height=8, enabled=False) self.log_field.grid(columnspan=3, sticky='NSWE') self.grid_rowconfigure(self.log_field.grid_info()['row'], weight=1) self.grid_columnconfigure(1, weight=1) self.bind('<Destroy>', self.kill_processes)
def __init__(self, parent, exclusions: dict, language: str, dictionary: dict, *args, **kwargs): super().__init__(parent, *args, **kwargs) self.grab_set() self.exclusions = exclusions self.title("Choose exclusions") language_list = list(self.exclusions) if language: if language in language_list: language_list.remove(language) language_list.insert(0, language) self.language = language self.dictionary = dictionary or dict() self.strings = sorted((key for key in dictionary.keys() if key.startswith(' ') or key.endswith(' ')), key=lambda x: x.lower().strip()) self.restore_strings = {show_spaces(s): s for s in self.strings} parent_frame = tk.Frame(self) tk.Label(parent_frame, text='Language:').pack(side=tk.LEFT) self.combo_language = ComboboxCustom(parent_frame, values=language_list) self.combo_language.pack(fill='both', expand=1) parent_frame.grid(sticky=tk.W + tk.E) self.combo_language.current(0) self.combo_language.bind('<<ComboboxSelected>>', self.combo_language_change_selection) self.combo_language.bind('<Any-KeyRelease>', self.combo_language_change_selection) bt = ttk.Button(self, text='-- Remove selected --', command=self.bt_remove_selected) bt.grid(column=0, row=1, sticky=tk.W + tk.E) self.listbox_exclusions = ListboxCustom(self, width=40, height=20) self.listbox_exclusions.grid(sticky='NSWE') self.update_listbox_exclusions() parent_frame = tk.Frame(self) tk.Label(parent_frame, text='Filter:').pack(side=tk.LEFT) self.entry_search = EntryCustom(parent_frame) self.entry_search.bind('<Any-KeyRelease>', self.entry_search_key_up) self.entry_search.pack(fill='both', expand=1) parent_frame.grid(column=1, row=0, sticky=tk.W + tk.E) bt = ttk.Button(self, text='<< Add selected <<', command=self.bt_add_selected) bt.grid(column=1, row=1, sticky=tk.W + tk.E) self.listbox_exclusions_hints = ListboxCustom(self, width=40, height=20) self.listbox_exclusions_hints.grid(column=1, row=2, sticky='NSWE') self.update_listbox_exclusions_hints() button = ttk.Button(self, text="OK", command=self.destroy) button.grid(row=3, column=0) def cancel(): self.exclusions = None self.destroy() button = ttk.Button(self, text="Cancel", command=cancel) button.grid(row=3, column=1) self.grid_columnconfigure(0, weight=1) self.grid_columnconfigure(1, weight=1) self.grid_rowconfigure(2, weight=1)
class DownloadTranslationsFrame(tk.Frame): def bt_connect(self): username = self.entry_username.text password = self.entry_password.text # DO NOT remember password (not safe) project = self.combo_projects.text try: # Todo: make connection in separate thread self.tx = TransifexAPI(username, password, 'https://www.transifex.com') assert self.tx.ping(), 'No connection to the server' assert self.tx.project_exists(project), "Project %r does not exist" % project self.resources = self.tx.list_resources(project) languages = self.tx.list_languages(project, resource_slug=self.resources[0]['slug']) except (TransifexAPIException, requests.exceptions.ConnectionError, AssertionError) as err: messagebox.showerror('Error', err) except: messagebox.showerror('Unexpected error', traceback.format_exc()) else: self.combo_languages.values = sorted(languages) last_language = self.config.get('language', None) if last_language and last_language in languages: self.combo_languages.text = last_language else: self.combo_languages.current(0) self.listbox_resources.clear() self.listbox_resources.values = tuple(res['name'] for res in self.resources) self.config['username'] = username recent_projects = self.config['recent_projects'] if recent_projects or project != recent_projects[0]: if project in recent_projects: recent_projects.remove(project) recent_projects.insert(0, project) self.combo_projects.values = recent_projects def download_waiter(self, resources, language, project, download_dir, parent_conn=None, initial_names=None, resource_names=None, i=0): if initial_names is None: initial_names = [res['name'] for res in self.resources] resource_names = initial_names.copy() if self.download_process is None: parent_conn, child_conn = mp.Pipe() self.download_process = mp.Process( target=downloader, kwargs=dict( conn=child_conn, tx=self.tx, project=project, language=language, resources=resources, file_path_pattern=path.join(download_dir, '%s_' + language + '.po') ) ) self.download_process.start() while parent_conn.poll() or not self.download_process.is_alive(): if parent_conn.poll(): i, message = parent_conn.recv() else: i, message = i, 'stopped' if message == 'completed': # Everything is downloaded self.download_process.join() self.download_process = None self.button_download.reset_state() self.download_started = False self.config['language'] = language if sys.platform == 'win32': subprocess.Popen('explorer "%s"' % (download_dir.replace('/', '\\'))) else: pass # Todo: open the directory in a file manager on linux return resource_names[i] = '{} - {}'.format(initial_names[i], message) self.listbox_resources.values = resource_names self.update() if message == 'ok!': self.progressbar.step() break elif message == 'failed': error = parent_conn.recv() self.download_process.join() self.download_process = None self.button_download.reset_state() self.download_started = False messagebox.showerror('Downloading error', error) return elif message == 'stopped': self.download_process = None self.button_download.reset_state() self.download_started = False return self.after(100, self.download_waiter, resources, language, project, download_dir, parent_conn, initial_names, resource_names, i) def bt_download(self): if self.tx and self.resources and not self.download_started: self.progressbar['maximum'] = len(self.resources) * 1.001 self.progressbar['value'] = 0 download_dir = self.fileentry_download_to.text if not download_dir: messagebox.showwarning('Directory not specified', 'Specify download directory first') return else: self.config['download_to'] = download_dir if not path.exists(download_dir): messagebox.showerror('Directory does not exist', 'Specify existing directory first') return project = self.combo_projects.get() language = self.combo_languages.get() initial_names = [res['name'] for res in self.resources] resource_names = list(initial_names) self.listbox_resources.values = resource_names self.download_started = True self.download_waiter(self.resources, language, project, download_dir) return True def bt_stop_downloading(self): r = messagebox.showwarning('Are you sure?', 'Stop downloading?', type=messagebox.OKCANCEL) if r == 'cancel': return False else: self.download_process.terminate() return True def kill_processes(self, _): if self.download_process and self.download_process.is_alive(): self.download_process.terminate() def __init__(self, master, config): super().__init__(master) self.config = init_section( config, section_name='download_translations', defaults=dict(recent_projects=['dwarf-fortress']) ) tk.Label(self, text='Transifex project:').grid() self.combo_projects = ComboboxCustom(self, values=self.config['recent_projects']) self.combo_projects.current(0) self.combo_projects.grid(column=1, row=0, sticky=tk.W + tk.E) tk.Label(self, text='Username:'******'username', '') self.entry_username.grid(column=1, row=1, sticky=tk.W + tk.E) tk.Label(self, text='Password:'******'\u2022') # 'bullet' symbol self.entry_password.grid(column=1, row=2, sticky=tk.W + tk.E) button_connect = ttk.Button(self, text='Connect...', command=self.bt_connect) button_connect.grid(row=0, column=2, rowspan=3, sticky=tk.N + tk.S + tk.W + tk.E) ttk.Separator(self, orient=tk.HORIZONTAL).grid(columnspan=3, sticky=tk.W + tk.E, pady=5) tk.Label(self, text='Choose language:').grid(column=0) self.combo_languages = ComboboxCustom(self) self.combo_languages.grid(column=1, row=4, sticky=tk.W + tk.E) # self.chk_all_languages = CheckbuttonVar(self, text='All languages (backup)') # self.chk_all_languages.grid(column=1) ttk.Separator(self, orient=tk.HORIZONTAL).grid(columnspan=3, sticky=tk.W + tk.E, pady=5) tk.Label(self, text='Download to:').grid() self.fileentry_download_to = FileEntry( self, dialogtype='askdirectory', default_path=self.config.get('download_to', ''), on_change=lambda text: check_and_save_path(self.config, 'download_to', text), ) self.fileentry_download_to.grid(column=1, row=6, columnspan=2, sticky='WE') self.button_download = TwoStateButton(self, text='Download translations', command=self.bt_download, text2='Stop', command2=self.bt_stop_downloading) self.button_download.grid(sticky=tk.W + tk.E) self.progressbar = ttk.Progressbar(self) self.progressbar.grid(column=1, row=7, columnspan=2, sticky=tk.W + tk.E) tk.Label(self, text='Resources:').grid(columnspan=3) self.listbox_resources = ListboxCustom(self) self.listbox_resources.grid(column=0, columnspan=3, sticky=tk.E + tk.W + tk.N + tk.S) self.grid_columnconfigure(1, weight=1) self.grid_rowconfigure(9, weight=1) self.resources = None self.tx = None self.download_started = False self.download_process = None self.bind('<Destroy>', self.kill_processes)
def __init__(self, master, config): super().__init__(master) self.config = init_section( config, section_name='download_translations', defaults=dict(recent_projects=['dwarf-fortress']) ) tk.Label(self, text='Transifex project:').grid() self.combo_projects = ComboboxCustom(self, values=self.config['recent_projects']) self.combo_projects.current(0) self.combo_projects.grid(column=1, row=0, sticky=tk.W + tk.E) tk.Label(self, text='Username:'******'username', '') self.entry_username.grid(column=1, row=1, sticky=tk.W + tk.E) tk.Label(self, text='Password:'******'\u2022') # 'bullet' symbol self.entry_password.grid(column=1, row=2, sticky=tk.W + tk.E) button_connect = ttk.Button(self, text='Connect...', command=self.bt_connect) button_connect.grid(row=0, column=2, rowspan=3, sticky=tk.N + tk.S + tk.W + tk.E) ttk.Separator(self, orient=tk.HORIZONTAL).grid(columnspan=3, sticky=tk.W + tk.E, pady=5) tk.Label(self, text='Choose language:').grid(column=0) self.combo_languages = ComboboxCustom(self) self.combo_languages.grid(column=1, row=4, sticky=tk.W + tk.E) # self.chk_all_languages = CheckbuttonVar(self, text='All languages (backup)') # self.chk_all_languages.grid(column=1) ttk.Separator(self, orient=tk.HORIZONTAL).grid(columnspan=3, sticky=tk.W + tk.E, pady=5) tk.Label(self, text='Download to:').grid() self.fileentry_download_to = FileEntry( self, dialogtype='askdirectory', default_path=self.config.get('download_to', ''), on_change=lambda text: check_and_save_path(self.config, 'download_to', text), ) self.fileentry_download_to.grid(column=1, row=6, columnspan=2, sticky='WE') self.button_download = TwoStateButton(self, text='Download translations', command=self.bt_download, text2='Stop', command2=self.bt_stop_downloading) self.button_download.grid(sticky=tk.W + tk.E) self.progressbar = ttk.Progressbar(self) self.progressbar.grid(column=1, row=7, columnspan=2, sticky=tk.W + tk.E) tk.Label(self, text='Resources:').grid(columnspan=3) self.listbox_resources = ListboxCustom(self) self.listbox_resources.grid(column=0, columnspan=3, sticky=tk.E + tk.W + tk.N + tk.S) self.grid_columnconfigure(1, weight=1) self.grid_rowconfigure(9, weight=1) self.resources = None self.tx = None self.download_started = False self.download_process = None self.bind('<Destroy>', self.kill_processes)
class TranslateExternalFiles(tk.Frame): @staticmethod def get_languages(directory): languages = set() for filename in os.listdir(directory): if filename.endswith('.po'): with open(path.join(directory, filename), encoding='utf-8') as file: languages.add(po.PoReader(file).meta['Language']) return sorted(languages) def on_change_translation_files_path(self, config, key, directory): check_and_save_path(config, key, directory) if path.exists(directory): languages = self.get_languages(directory) self.combo_language.values = languages if languages: self.combo_language.current(0) else: self.combo_language.text = '' self.update_listbox_translation_files(language=self.combo_language.text) else: self.combo_language.values = tuple() self.combo_language.text = '' @staticmethod def filter_files_by_language(directory, language): for filename in os.listdir(directory): if filename.endswith('.po'): with open(path.join(directory, filename), encoding='utf-8') as file: if po.PoReader(file).meta['Language'] == language: yield filename def update_listbox_translation_files(self, _=None, language=None): language = self.combo_language.text if not language else language directory = self.fileentry_translation_files.text files = self.filter_files_by_language(directory, language) if path.exists(directory) else tuple() self.listbox_translation_files.values = files def update_combo_encoding(self, _=None): language = self.combo_language.text directory = self.fileentry_translation_files.text # TODO: Unify with PatchExecutableFrame.update_combo_encoding() if path.exists(directory): files = self.filter_files_by_language(directory, language) codepages = get_codepages().keys() for file in files: with open(path.join(directory, file), 'r', encoding='utf-8') as fn: pofile = po.PoReader(fn) strings = [cleanup_special_symbols(entry['msgstr']) for entry in pofile] codepages = filter_codepages(codepages, strings) self.combo_encoding.values = sorted(codepages, key=lambda x: int(x.strip(string.ascii_letters))) self.combo_encoding.current(0) def bt_search(self, translate=False): patterns = { r'raw\objects': dict( po_filename='raw-objects', func=translate_raws, ), # Swithched off for now # r'data_src': dict( # po_filename='uncompressed', # func=lambda *args: translate_plain_text(*args, join_paragraphs=True), # ), r'data\speech': dict( po_filename='speech', func=lambda *args: translate_plain_text(*args, join_paragraphs=False), ), r'raw\objects\text': dict( po_filename='text', func=lambda *args: translate_plain_text(*args, join_paragraphs=False), ), } # TODO: add progressbar self.listbox_found_directories.clear() for cur_dir, _, files in os.walk(self.fileentry_df_root_path.text): for pattern in patterns: if cur_dir.endswith(pattern): self.listbox_found_directories.append(cur_dir + ' (%s files)' % len(files)) postfix = '_{}.po'.format(self.combo_language.text) po_filename = os.path.join(self.fileentry_translation_files.text, patterns[pattern]['po_filename'] + postfix) if translate: func = patterns[pattern]['func'] for filename in func(po_filename, cur_dir, self.combo_encoding.get()): # print(filename, file=sys.stderr) self.listbox_found_directories.append(filename) if translate: self.listbox_found_directories.append("Completed.") def __init__(self, master, config, *args, **kwargs): super().__init__(master, *args, **kwargs) self.config = init_section(config, section_name='translate_external_files') config = self.config tk.Label(self, text='Dwarf Fortress root path:').grid() self.fileentry_df_root_path = FileEntry( self, dialogtype='askdirectory', default_path=config.get('df_root_path', ''), on_change=lambda text: check_and_save_path(self.config, 'df_root_path', text), ) self.fileentry_df_root_path.grid(row=0, column=1, sticky='WE') tk.Label(self, text="Translation files' directory:").grid() self.fileentry_translation_files = FileEntry( self, dialogtype='askdirectory', default_path=config.get('translation_files_path', ''), on_change=lambda text: self.on_change_translation_files_path(self.config, 'translation_files_path', text), ) self.fileentry_translation_files.grid(row=1, column=1, sticky='WE') tk.Label(self, text="Language:").grid() self.combo_language = ComboboxCustom(self) self.combo_language.grid(row=2, column=1, sticky='WE') directory = self.fileentry_translation_files.text if path.exists(directory): languages = self.get_languages(directory) self.combo_language.values = languages if languages: self.combo_language.current(0) self.combo_language.bind('<<ComboboxSelected>>', self.update_listbox_translation_files) tk.Label(self, text="Encoding:").grid() self.combo_encoding = ComboboxCustom(self) self.combo_encoding.grid(row=3, column=1, sticky='WE') self.update_combo_encoding() self.listbox_translation_files = ListboxCustom(self) self.listbox_translation_files.grid(columnspan=2, sticky='NSWE') self.update_listbox_translation_files(language=self.combo_language.text) ttk.Button(self, text='Search', command=self.bt_search).grid() ttk.Button(self, text='Translate', command=lambda: self.bt_search(translate=True)).grid(row=5, column=1) self.listbox_found_directories = ListboxCustom(self) self.listbox_found_directories.grid(columnspan=2, sticky='NSWE') self.grid_columnconfigure(1, weight=1)