Translations, suggestion box, UX
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Gitea/kapitanbooru-uploader/pipeline/head This commit looks good
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Gitea/kapitanbooru-uploader/pipeline/head This commit looks good
				
			This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -54,6 +54,7 @@ cover/ | |||||||
| # Translations | # Translations | ||||||
| *.mo | *.mo | ||||||
| *.pot | *.pot | ||||||
|  | *.po~ | ||||||
|  |  | ||||||
| # Django stuff: | # Django stuff: | ||||||
| *.log | *.log | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | { | ||||||
|  |     "i18n-ally.localesPaths": [ | ||||||
|  |         "locales" | ||||||
|  |     ] | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								Jenkinsfile
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,5 @@ | |||||||
| pipeline { | pipeline { | ||||||
|     agent { label 'Pi4' }  // Use Raspberry Pi 4 agent |     agent { label 'Pi4' }  // Use Raspberry Pi 4 agent running on Ubuntu Server LTS 24.04 | ||||||
|     environment { |     environment { | ||||||
|         PIP_EXTRA_INDEX_URL = 'http://localhost:8090/simple/'  // Local PyPI repo |         PIP_EXTRA_INDEX_URL = 'http://localhost:8090/simple/'  // Local PyPI repo | ||||||
|         PACKAGE_NAME = 'kapitanbooru_uploader'  // Your package name |         PACKAGE_NAME = 'kapitanbooru_uploader'  // Your package name | ||||||
| @@ -17,6 +17,18 @@ pipeline { | |||||||
|                 sh '. venv/bin/activate && pip install --upgrade pip build twine' |                 sh '. venv/bin/activate && pip install --upgrade pip build twine' | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |          | ||||||
|  |         stage('Compile Translations') { | ||||||
|  |             steps { | ||||||
|  |                 // Find all .po files under kapitanbooru_uploader/locales and compile them to .mo | ||||||
|  |                 sh ''' | ||||||
|  |                 find kapitanbooru_uploader/locales -name "*.po" -print0 | while IFS= read -r -d '' po; do | ||||||
|  |                   mo=$(echo "$po" | sed 's/\\.po$/.mo/'); | ||||||
|  |                   msgfmt "$po" -o "$mo"; | ||||||
|  |                 done | ||||||
|  |                 ''' | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         stage('Build Package') { |         stage('Build Package') { | ||||||
|             steps { |             steps { | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | recursive-include kapitanbooru_uploader/locales *.mo | ||||||
							
								
								
									
										50
									
								
								kapitanbooru_uploader/I18N.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								kapitanbooru_uploader/I18N.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | import gettext | ||||||
|  | import locale | ||||||
|  | import os | ||||||
|  | from typing import Dict | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class I18N: | ||||||
|  |     def __init__(self, locale_dir=None): | ||||||
|  |         # If no locale_dir is provided, use the locales folder relative to this file. | ||||||
|  |         if locale_dir is None: | ||||||
|  |             current_dir = os.path.dirname(os.path.abspath(__file__)) | ||||||
|  |             locale_dir = os.path.join(current_dir, "locales") | ||||||
|  |         self.locale_dir = locale_dir | ||||||
|  |         self.languages = {"en": "English", "pl": "Polski"} | ||||||
|  |         self.current_lang = "en" | ||||||
|  |         self.translations: Dict[str, str] = {} | ||||||
|  |         self.load_translations() | ||||||
|  |  | ||||||
|  |     def load_translations(self): | ||||||
|  |         """Load all available translations""" | ||||||
|  |         for lang in self.languages: | ||||||
|  |             try: | ||||||
|  |                 trans = gettext.translation( | ||||||
|  |                     "messages", localedir=self.locale_dir, languages=[lang] | ||||||
|  |                 ) | ||||||
|  |                 self.translations[lang] = trans.gettext | ||||||
|  |             except FileNotFoundError: | ||||||
|  |                 self.translations[lang] = lambda x: x | ||||||
|  |  | ||||||
|  |     def set_language(self, lang: str): | ||||||
|  |         """Set application language""" | ||||||
|  |         if lang in self.languages: | ||||||
|  |             self.current_lang = lang | ||||||
|  |             # For Windows language detection | ||||||
|  |             try: | ||||||
|  |                 locale.setlocale(locale.LC_ALL, self.get_locale_code(lang)) | ||||||
|  |             except locale.Error: | ||||||
|  |                 pass | ||||||
|  |  | ||||||
|  |     def get_locale_code(self, lang: str) -> str: | ||||||
|  |         """Map languages to locale codes""" | ||||||
|  |         return {"en": "en_US.UTF-8", "pl": "pl_PL.UTF-8"}.get(lang, "en_US.UTF-8") | ||||||
|  |  | ||||||
|  |     def __call__(self, text: str) -> str: | ||||||
|  |         """Translate text using current language""" | ||||||
|  |         return self.translations[self.current_lang](text) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Shortcut for translation function | ||||||
|  | _ = I18N() | ||||||
| @@ -11,8 +11,9 @@ from typing import Dict, Tuple, Optional | |||||||
| import networkx as nx | import networkx as nx | ||||||
| import requests | import requests | ||||||
| from PIL import Image, ImageTk, PngImagePlugin | from PIL import Image, ImageTk, PngImagePlugin | ||||||
|  |  | ||||||
| import wdtagger as wdt | import wdtagger as wdt | ||||||
|  |  | ||||||
|  | from .I18N import _ | ||||||
| from .ProgressFile import ProgressFile | from .ProgressFile import ProgressFile | ||||||
| from .TagsRepo import TagsRepo | from .TagsRepo import TagsRepo | ||||||
| from .autocomplete import TagManager | from .autocomplete import TagManager | ||||||
| @@ -26,11 +27,11 @@ class ProcessingDialog: | |||||||
|     def __init__(self, root, target_function, *args): |     def __init__(self, root, target_function, *args): | ||||||
|         self.root = root |         self.root = root | ||||||
|         self.top = tk.Toplevel(root) |         self.top = tk.Toplevel(root) | ||||||
|         self.top.title("Processing...") |         self.top.title(_("Processing...")) | ||||||
|         self.top.geometry("300x150") |         self.top.geometry("300x150") | ||||||
|         self.top.protocol("WM_DELETE_WINDOW", self.on_close) |         self.top.protocol("WM_DELETE_WINDOW", self.on_close) | ||||||
|  |  | ||||||
|         self.label = tk.Label(self.top, text="Processing, please wait...") |         self.label = tk.Label(self.top, text=_("Processing, please wait...")) | ||||||
|         self.label.pack(pady=10) |         self.label.pack(pady=10) | ||||||
|  |  | ||||||
|         # Start with indeterminate progress bar |         # Start with indeterminate progress bar | ||||||
| @@ -38,8 +39,15 @@ class ProcessingDialog: | |||||||
|         self.progress.pack(pady=10, fill="x") |         self.progress.pack(pady=10, fill="x") | ||||||
|         self.progress.start(10) |         self.progress.start(10) | ||||||
|  |  | ||||||
|  |         sig = inspect.signature(target_function) | ||||||
|  |         if "secondary_progress_queue" in sig.parameters: | ||||||
|  |             self.sub_progress = ttk.Progressbar(self.top, mode="indeterminate") | ||||||
|  |             self.sub_progress.pack(pady=10, fill="x") | ||||||
|  |             self.sub_progress.start(10) | ||||||
|  |  | ||||||
|         # Setup communication queue and periodic checker |         # Setup communication queue and periodic checker | ||||||
|         self.queue = queue.Queue() |         self.queue = queue.Queue() | ||||||
|  |         self.sub_queue = queue.Queue() | ||||||
|         self.running = True |         self.running = True | ||||||
|         self.cancel_event = threading.Event()  # Cancellation flag |         self.cancel_event = threading.Event()  # Cancellation flag | ||||||
|         self.thread = threading.Thread( |         self.thread = threading.Thread( | ||||||
| @@ -58,6 +66,10 @@ class ProcessingDialog: | |||||||
|                     self.progress.config(mode=msg[1]) |                     self.progress.config(mode=msg[1]) | ||||||
|                     if msg[1] == "determinate": |                     if msg[1] == "determinate": | ||||||
|                         self.progress["value"] = 0 |                         self.progress["value"] = 0 | ||||||
|  |                         self.progress.stop() | ||||||
|  |                     elif msg[1] == "indeterminate": | ||||||
|  |                         self.progress["value"] = 0 | ||||||
|  |                         self.progress.start() | ||||||
|                 elif msg[0] == "max": |                 elif msg[0] == "max": | ||||||
|                     self.progress["maximum"] = msg[1] |                     self.progress["maximum"] = msg[1] | ||||||
|                 elif msg[0] == "progress": |                 elif msg[0] == "progress": | ||||||
| @@ -69,6 +81,26 @@ class ProcessingDialog: | |||||||
|             except queue.Empty: |             except queue.Empty: | ||||||
|                 break |                 break | ||||||
|  |  | ||||||
|  |             try: | ||||||
|  |                 msg = self.sub_queue.get_nowait() | ||||||
|  |  | ||||||
|  |                 if msg[0] == "mode": | ||||||
|  |                     self.sub_progress.config(mode=msg[1]) | ||||||
|  |                     if msg[1] == "determinate": | ||||||
|  |                         self.sub_progress["value"] = 0 | ||||||
|  |                         self.sub_progress.stop() | ||||||
|  |                     elif msg[1] == "indeterminate": | ||||||
|  |                         self.sub_progress["value"] = 0 | ||||||
|  |                         self.sub_progress.start() | ||||||
|  |                 elif msg[0] == "max": | ||||||
|  |                     self.sub_progress["maximum"] = msg[1] | ||||||
|  |                 elif msg[0] == "progress": | ||||||
|  |                     self.sub_progress["value"] = msg[1] | ||||||
|  |  | ||||||
|  |                 self.top.update_idletasks() | ||||||
|  |             except queue.Empty: | ||||||
|  |                 break | ||||||
|  |  | ||||||
|         if self.running: |         if self.running: | ||||||
|             self.top.after(100, self.process_queue) |             self.top.after(100, self.process_queue) | ||||||
|  |  | ||||||
| @@ -79,6 +111,8 @@ class ProcessingDialog: | |||||||
|             kwargs = {} |             kwargs = {} | ||||||
|             if "progress_queue" in sig.parameters: |             if "progress_queue" in sig.parameters: | ||||||
|                 kwargs["progress_queue"] = self.queue |                 kwargs["progress_queue"] = self.queue | ||||||
|  |             if "secondary_progress_queue" in sig.parameters: | ||||||
|  |                 kwargs["secondary_progress_queue"] = self.sub_queue | ||||||
|             if "cancel_event" in sig.parameters: |             if "cancel_event" in sig.parameters: | ||||||
|                 kwargs["cancel_event"] = self.cancel_event |                 kwargs["cancel_event"] = self.cancel_event | ||||||
|             target_function(*args, **kwargs) |             target_function(*args, **kwargs) | ||||||
| @@ -91,7 +125,7 @@ class ProcessingDialog: | |||||||
|             self.running = False |             self.running = False | ||||||
|             self.progress.stop() |             self.progress.stop() | ||||||
|             self.top.after(0, self.top.destroy) |             self.top.after(0, self.top.destroy) | ||||||
|         self.thread.join() |         self.root.after(100, self.thread.join) | ||||||
|  |  | ||||||
|     def on_close(self): |     def on_close(self): | ||||||
|         """Handle manual window closure""" |         """Handle manual window closure""" | ||||||
| @@ -105,6 +139,7 @@ class ImageBrowser(tk.Tk): | |||||||
|         super().__init__() |         super().__init__() | ||||||
|         self.title("Kapitanbooru Uploader") |         self.title("Kapitanbooru Uploader") | ||||||
|         self.geometry("900x600") |         self.geometry("900x600") | ||||||
|  |         self.version = "0.4.0" | ||||||
|  |  | ||||||
|         self.settings = Settings() |         self.settings = Settings() | ||||||
|         self.tags_repo = TagsRepo(self.settings) |         self.tags_repo = TagsRepo(self.settings) | ||||||
| @@ -169,6 +204,15 @@ class ImageBrowser(tk.Tk): | |||||||
|         self.create_widgets() |         self.create_widgets() | ||||||
|         self.bind_events() |         self.bind_events() | ||||||
|  |  | ||||||
|  |     def reload_ui(self): | ||||||
|  |         """Reload UI components with new language""" | ||||||
|  |         # Destroy current widgets | ||||||
|  |         for widget in self.winfo_children(): | ||||||
|  |             widget.destroy() | ||||||
|  |         # Rebuild UI | ||||||
|  |         self.create_menu() | ||||||
|  |         self.create_widgets() | ||||||
|  |  | ||||||
|     def load_implication_graph(self) -> nx.DiGraph: |     def load_implication_graph(self) -> nx.DiGraph: | ||||||
|         G = nx.DiGraph() |         G = nx.DiGraph() | ||||||
|         conn = self.tags_repo.get_conn() |         conn = self.tags_repo.get_conn() | ||||||
| @@ -249,7 +293,7 @@ class ImageBrowser(tk.Tk): | |||||||
|             png_tags = set(parse_parameters(parameters, self.tags_repo).split()) |             png_tags = set(parse_parameters(parameters, self.tags_repo).split()) | ||||||
|             img.close() |             img.close() | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print("Błąd przy otwieraniu pliku", file_path, ":", e) |             print(_("Błąd przy otwieraniu pliku"), file_path, ":", e) | ||||||
|             png_tags = set() |             png_tags = set() | ||||||
|  |  | ||||||
|         # Pobierz tagi z Taggera – sprawdzając cache |         # Pobierz tagi z Taggera – sprawdzając cache | ||||||
| @@ -291,10 +335,10 @@ class ImageBrowser(tk.Tk): | |||||||
|             self.tagger_cache[md5] = result |             self.tagger_cache[md5] = result | ||||||
|             self.tagger_processed.add(md5) |             self.tagger_processed.add(md5) | ||||||
|             self.after(0, self.update_status_bar) |             self.after(0, self.update_status_bar) | ||||||
|             print(f"Tagger przetworzył: {file_path}") |             print(_("Tagger przetworzył:"), f"{file_path}") | ||||||
|             return result |             return result | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print("Błąd Taggera dla", file_path, ":", e) |             print(_("Błąd Taggera dla"), file_path, ":", e) | ||||||
|  |  | ||||||
|     def map_tagger_rating(self, result: wdt.Result) -> str: |     def map_tagger_rating(self, result: wdt.Result) -> str: | ||||||
|         """ |         """ | ||||||
| @@ -320,37 +364,98 @@ class ImageBrowser(tk.Tk): | |||||||
|         self.file_menu = tk.Menu(menubar, tearoff=0) |         self.file_menu = tk.Menu(menubar, tearoff=0) | ||||||
|  |  | ||||||
|         # File menu items - create references for items we need to control |         # File menu items - create references for items we need to control | ||||||
|         self.file_menu.add_command(label="Otwórz folder", command=self.select_folder) |         self.file_menu.add_command(label=_("Otwórz folder"), command=self.select_folder) | ||||||
|         self.file_menu.add_separator() |         self.file_menu.add_separator() | ||||||
|         self.file_menu.add_command( |         self.file_menu.add_command( | ||||||
|             label="Wyślij", command=self.upload_current_image, state=tk.DISABLED |             label=_("Wyślij"), command=self.upload_current_image, state=tk.DISABLED | ||||||
|         ) |         ) | ||||||
|         self.file_menu.add_command( |         self.file_menu.add_command( | ||||||
|             label="Wyślij wszystko", command=self.upload_all_files, state=tk.DISABLED |             label=_("Wyślij wszystko"), command=self.upload_all_files, state=tk.DISABLED | ||||||
|         ) |         ) | ||||||
|         self.file_menu.add_separator() |         self.file_menu.add_separator() | ||||||
|         self.file_menu.add_command( |         self.file_menu.add_command( | ||||||
|             label="Podmień tagi", command=self.edit_current_image, state=tk.DISABLED |             label=_("Podmień tagi"), command=self.edit_current_image, state=tk.DISABLED | ||||||
|         ) |         ) | ||||||
|         self.file_menu.add_command( |         self.file_menu.add_command( | ||||||
|             label="Otwórz post", command=self.view_current_post, state=tk.DISABLED |             label=_("Otwórz post"), command=self.view_current_post, state=tk.DISABLED | ||||||
|         ) |         ) | ||||||
|         self.file_menu.add_separator() |         self.file_menu.add_separator() | ||||||
|         self.file_menu.add_command(label="Zakończ", command=self.quit) |         self.file_menu.add_command(label=_("Zakończ"), command=self.quit) | ||||||
|  |  | ||||||
|         menubar.add_cascade(label="Plik", menu=self.file_menu) |         menubar.add_cascade(label=_("Plik"), menu=self.file_menu) | ||||||
|  |  | ||||||
|         # Options menu |         # Options menu | ||||||
|         options_menu = tk.Menu(menubar, tearoff=0) |         options_menu = tk.Menu(menubar, tearoff=0) | ||||||
|         options_menu.add_command(label="Ustawienia", command=self.open_settings) |         options_menu.add_command(label=_("Ustawienia"), command=self.open_settings) | ||||||
|         options_menu.add_separator() |         options_menu.add_separator() | ||||||
|         options_menu.add_command( |         options_menu.add_command( | ||||||
|             label="Wyczyść cache Taggera", command=self.clear_cache |             label=_("Wyczyść cache Taggera"), command=self.clear_cache | ||||||
|         ) |         ) | ||||||
|         options_menu.add_command( |         options_menu.add_command( | ||||||
|             label="Zregeneruj bazę tagów", command=self.regenerate_tags_db |             label=_("Zregeneruj bazę tagów"), command=self.regenerate_tags_db | ||||||
|         ) |         ) | ||||||
|         menubar.add_cascade(label="Opcje", menu=options_menu) |         menubar.add_cascade(label=_("Opcje"), menu=options_menu) | ||||||
|  |  | ||||||
|  |         help_menu = tk.Menu(menubar, tearoff=0) | ||||||
|  |         help_menu.add_command(label=_("About"), command=self.show_about) | ||||||
|  |         menubar.add_cascade(label=_("Help"), menu=help_menu) | ||||||
|  |  | ||||||
|  |     def show_about(self): | ||||||
|  |         """Multilingual About window (updated version)""" | ||||||
|  |         about = tk.Toplevel(self) | ||||||
|  |         about.title(_("About Kapitanbooru Uploader")) | ||||||
|  |         about.resizable(False, False) | ||||||
|  |  | ||||||
|  |         # Window content | ||||||
|  |         frame = ttk.Frame(about, padding=10) | ||||||
|  |         frame.pack(fill="both", expand=True) | ||||||
|  |  | ||||||
|  |         ttk.Label( | ||||||
|  |             frame, text="Kapitanbooru Uploader", font=("TkDefaultFont", 16, "bold") | ||||||
|  |         ).grid(row=0, column=0, sticky=tk.W) | ||||||
|  |  | ||||||
|  |         content = [ | ||||||
|  |             (f"Version {self.version}", 1), | ||||||
|  |             ("", 2), | ||||||
|  |             (_("A GUI application for uploading images to KapitanBooru."), 3), | ||||||
|  |             (_("Features include image upload, tag management, automatic"), 4), | ||||||
|  |             (_("tagging with wdtagger, and cache management."), 5), | ||||||
|  |             ("", 6), | ||||||
|  |             (_("Authors:"), 7), | ||||||
|  |             ("Michał Leśniak", 8), | ||||||
|  |             ("", 9), | ||||||
|  |             (_("License: MIT License"), 10), | ||||||
|  |             (f"Copyright © 2025 Michał Leśniak", 11), | ||||||
|  |             ("", 12), | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |         for text, row in content: | ||||||
|  |             ttk.Label(frame, text=text).grid(row=row, column=0, sticky=tk.W) | ||||||
|  |  | ||||||
|  |         # Repository link | ||||||
|  |         repo_frame = ttk.Frame(frame) | ||||||
|  |         repo_frame.grid(row=13, sticky=tk.W) | ||||||
|  |         ttk.Label(repo_frame, text=_("Repository:")).pack(side=tk.LEFT) | ||||||
|  |         repo_url = "https://git.mlesniak.pl/kapitan/kapitanbooru-uploader" | ||||||
|  |         repo = ttk.Label(repo_frame, text=repo_url, cursor="hand2", foreground="blue") | ||||||
|  |         repo.pack(side=tk.LEFT, padx=(5, 0)) | ||||||
|  |         repo.bind("<Button-1>", lambda e: open_webbrowser(repo_url, self.settings)) | ||||||
|  |  | ||||||
|  |         # Website link | ||||||
|  |         website_frame = ttk.Frame(frame) | ||||||
|  |         website_frame.grid(row=14, sticky=tk.W) | ||||||
|  |         ttk.Label(website_frame, text=_("Website:")).pack(side=tk.LEFT) | ||||||
|  |         website_url = "https://booru.mlesniak.pl" | ||||||
|  |         website = ttk.Label( | ||||||
|  |             website_frame, text=website_url, cursor="hand2", foreground="blue" | ||||||
|  |         ) | ||||||
|  |         website.pack(side=tk.LEFT, padx=(5, 0)) | ||||||
|  |         website.bind( | ||||||
|  |             "<Button-1>", lambda e: open_webbrowser(website_url, self.settings) | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         # Close button | ||||||
|  |         ttk.Button(about, text=_("Close"), command=about.destroy).pack(pady=10) | ||||||
|  |  | ||||||
|     def regenerate_tags_db(self): |     def regenerate_tags_db(self): | ||||||
|         self.processing_dialog = ProcessingDialog(self, self.tags_repo.regenerate_db) |         self.processing_dialog = ProcessingDialog(self, self.tags_repo.regenerate_db) | ||||||
| @@ -358,41 +463,44 @@ class ImageBrowser(tk.Tk): | |||||||
|     def clear_cache(self): |     def clear_cache(self): | ||||||
|         res, err = self.tagger_cache.clear_cache() |         res, err = self.tagger_cache.clear_cache() | ||||||
|         if res: |         if res: | ||||||
|             messagebox.showinfo("Cache", "Cache Taggera zostało wyczyszczone.") |             messagebox.showinfo(_("Cache"), _("Cache Taggera zostało wyczyszczone.")) | ||||||
|         else: |         else: | ||||||
|             messagebox.showerror("Cache", f"Błąd przy czyszczeniu cache: {err}") |             messagebox.showerror( | ||||||
|  |                 _("Cache"), f"{_('Błąd przy czyszczeniu cache:')} {err}" | ||||||
|  |             ) | ||||||
|  |  | ||||||
|     def open_settings(self): |     def open_settings(self): | ||||||
|         settings_window = tk.Toplevel(self) |         settings_window = tk.Toplevel(self) | ||||||
|         settings_window.title("Ustawienia") |         settings_window.title(_("Ustawienia")) | ||||||
|         settings_window.geometry("300x350") |         settings_window.geometry("300x430")  # Enlarged vertically | ||||||
|  |         settings_window.resizable(False, False)  # Disable resizing | ||||||
|         settings_window.grab_set() |         settings_window.grab_set() | ||||||
|  |  | ||||||
|         lbl_login = tk.Label(settings_window, text="Login:") |         lbl_login = tk.Label(settings_window, text=_("Login:")) | ||||||
|         lbl_login.pack(pady=(10, 0)) |         lbl_login.pack(pady=(10, 0)) | ||||||
|         entry_login = tk.Entry(settings_window) |         entry_login = tk.Entry(settings_window) | ||||||
|         entry_login.pack(pady=(0, 10), padx=10, fill="x") |         entry_login.pack(pady=(0, 10), padx=10, fill="x") | ||||||
|         entry_login.insert(0, self.settings.username) |         entry_login.insert(0, self.settings.username) | ||||||
|  |  | ||||||
|         lbl_password = tk.Label(settings_window, text="Hasło:") |         lbl_password = tk.Label(settings_window, text=_("Hasło:")) | ||||||
|         lbl_password.pack(pady=(10, 0)) |         lbl_password.pack(pady=(10, 0)) | ||||||
|         entry_password = tk.Entry(settings_window, show="*") |         entry_password = tk.Entry(settings_window, show="*") | ||||||
|         entry_password.pack(pady=(0, 10), padx=10, fill="x") |         entry_password.pack(pady=(0, 10), padx=10, fill="x") | ||||||
|         entry_password.insert(0, self.settings.password) |         entry_password.insert(0, self.settings.password) | ||||||
|  |  | ||||||
|         lbl_base_url = tk.Label(settings_window, text="Base URL:") |         lbl_base_url = tk.Label(settings_window, text=_("Base URL:")) | ||||||
|         lbl_base_url.pack(pady=(10, 0)) |         lbl_base_url.pack(pady=(10, 0)) | ||||||
|         entry_base_url = tk.Entry(settings_window) |         entry_base_url = tk.Entry(settings_window) | ||||||
|         entry_base_url.pack(pady=(0, 10), padx=10, fill="x") |         entry_base_url.pack(pady=(0, 10), padx=10, fill="x") | ||||||
|         entry_base_url.insert(0, self.settings.base_url) |         entry_base_url.insert(0, self.settings.base_url) | ||||||
|  |  | ||||||
|         lbl_default_tags = tk.Label(settings_window, text="Default Tags:") |         lbl_default_tags = tk.Label(settings_window, text=_("Default Tags:")) | ||||||
|         lbl_default_tags.pack(pady=(10, 0)) |         lbl_default_tags.pack(pady=(10, 0)) | ||||||
|         entry_default_tags = tk.Entry(settings_window) |         entry_default_tags = tk.Entry(settings_window) | ||||||
|         entry_default_tags.pack(pady=(0, 10), padx=10, fill="x") |         entry_default_tags.pack(pady=(0, 10), padx=10, fill="x") | ||||||
|         entry_default_tags.insert(0, self.settings.default_tags) |         entry_default_tags.insert(0, self.settings.default_tags) | ||||||
|  |  | ||||||
|         lbl_browser = tk.Label(settings_window, text="Browser:") |         lbl_browser = tk.Label(settings_window, text=_("Browser:")) | ||||||
|         lbl_browser.pack(pady=(10, 0)) |         lbl_browser.pack(pady=(10, 0)) | ||||||
|         cb_browser = ttk.Combobox( |         cb_browser = ttk.Combobox( | ||||||
|             settings_window, |             settings_window, | ||||||
| @@ -406,16 +514,39 @@ class ImageBrowser(tk.Tk): | |||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |         lbl_lang = tk.Label(settings_window, text=_("Language:")) | ||||||
|  |         lbl_lang.pack(pady=(10, 0)) | ||||||
|  |         cb_lang = ttk.Combobox( | ||||||
|  |             settings_window, | ||||||
|  |             values=list(self.settings.i18n.languages.values()), | ||||||
|  |             state="readonly", | ||||||
|  |         ) | ||||||
|  |         cb_lang.pack(pady=(0, 10), padx=10, fill="x") | ||||||
|  |         cb_lang.set( | ||||||
|  |             self.settings.i18n.languages.get(self.settings.i18n.current_lang, "en") | ||||||
|  |         ) | ||||||
|  |  | ||||||
|         def save_and_close(): |         def save_and_close(): | ||||||
|             self.settings.username = entry_login.get() |             self.settings.username = entry_login.get() | ||||||
|             self.settings.password = entry_password.get() |             self.settings.password = entry_password.get() | ||||||
|             self.settings.base_url = entry_base_url.get() |             self.settings.base_url = entry_base_url.get() | ||||||
|             self.settings.default_tags = entry_default_tags.get() |             self.settings.default_tags = entry_default_tags.get() | ||||||
|             self.settings.browser = self.settings.installed_browsers[cb_browser.get()] |             self.settings.browser = self.settings.installed_browsers[cb_browser.get()] | ||||||
|  |             self.settings.i18n.set_language( | ||||||
|  |                 next( | ||||||
|  |                     ( | ||||||
|  |                         lang | ||||||
|  |                         for lang, name in self.settings.i18n.languages.items() | ||||||
|  |                         if name == cb_lang.get() | ||||||
|  |                     ), | ||||||
|  |                     "en", | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|             self.settings.save_settings() |             self.settings.save_settings() | ||||||
|             settings_window.destroy() |             settings_window.destroy() | ||||||
|  |             self.reload_ui() | ||||||
|  |  | ||||||
|         btn_save = tk.Button(settings_window, text="Zapisz", command=save_and_close) |         btn_save = tk.Button(settings_window, text=_("Zapisz"), command=save_and_close) | ||||||
|         btn_save.pack(pady=10) |         btn_save.pack(pady=10) | ||||||
|  |  | ||||||
|     def create_widgets(self): |     def create_widgets(self): | ||||||
| @@ -457,7 +588,7 @@ class ImageBrowser(tk.Tk): | |||||||
|         right_frame.grid_rowconfigure(4, weight=0)  # Upload Panel |         right_frame.grid_rowconfigure(4, weight=0)  # Upload Panel | ||||||
|  |  | ||||||
|         # PNG Tags – widget Text z scrollbar |         # PNG Tags – widget Text z scrollbar | ||||||
|         png_frame = tk.LabelFrame(right_frame, text="PNG Tags") |         png_frame = tk.LabelFrame(right_frame, text=_("PNG Tags")) | ||||||
|         png_frame.grid(row=0, column=0, sticky=tk.EW, padx=5, pady=5) |         png_frame.grid(row=0, column=0, sticky=tk.EW, padx=5, pady=5) | ||||||
|         png_frame.grid_columnconfigure(0, weight=1) |         png_frame.grid_columnconfigure(0, weight=1) | ||||||
|         self.png_tags_text = tk.Text(png_frame, wrap=tk.WORD) |         self.png_tags_text = tk.Text(png_frame, wrap=tk.WORD) | ||||||
| @@ -469,7 +600,7 @@ class ImageBrowser(tk.Tk): | |||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         # Tagger Tags – widget Text z scrollbar |         # Tagger Tags – widget Text z scrollbar | ||||||
|         tagger_frame = tk.LabelFrame(right_frame, text="Tagger Tags") |         tagger_frame = tk.LabelFrame(right_frame, text=_("Tagger Tags")) | ||||||
|         tagger_frame.grid(row=1, column=0, sticky=tk.EW, padx=5, pady=5) |         tagger_frame.grid(row=1, column=0, sticky=tk.EW, padx=5, pady=5) | ||||||
|         tagger_frame.grid_columnconfigure(0, weight=1) |         tagger_frame.grid_columnconfigure(0, weight=1) | ||||||
|         self.tagger_tags_text = tk.Text(tagger_frame, wrap=tk.WORD) |         self.tagger_tags_text = tk.Text(tagger_frame, wrap=tk.WORD) | ||||||
| @@ -483,7 +614,7 @@ class ImageBrowser(tk.Tk): | |||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         # Manual Tags – Entry (stała wysokość) |         # Manual Tags – Entry (stała wysokość) | ||||||
|         manual_frame = tk.LabelFrame(right_frame, text="Manual Tags") |         manual_frame = tk.LabelFrame(right_frame, text=_("Manual Tags")) | ||||||
|         manual_frame.grid(row=2, column=0, sticky=tk.NSEW, padx=5, pady=5) |         manual_frame.grid(row=2, column=0, sticky=tk.NSEW, padx=5, pady=5) | ||||||
|         self.manual_tags_manager = TagManager( |         self.manual_tags_manager = TagManager( | ||||||
|             manual_frame, self.settings, self.tags_repo, self.update_final_tags |             manual_frame, self.settings, self.tags_repo, self.update_final_tags | ||||||
| @@ -491,7 +622,7 @@ class ImageBrowser(tk.Tk): | |||||||
|         self.manual_tags_manager.pack(fill=tk.BOTH, expand=True) |         self.manual_tags_manager.pack(fill=tk.BOTH, expand=True) | ||||||
|  |  | ||||||
|         # Final Tags – widget Text z scrollbar, który rozszerza się |         # Final Tags – widget Text z scrollbar, który rozszerza się | ||||||
|         final_frame = tk.LabelFrame(right_frame, text="Final Tags") |         final_frame = tk.LabelFrame(right_frame, text=_("Final Tags")) | ||||||
|         final_frame.grid(row=3, column=0, sticky=tk.NSEW, padx=5, pady=5) |         final_frame.grid(row=3, column=0, sticky=tk.NSEW, padx=5, pady=5) | ||||||
|         final_frame.grid_rowconfigure(0, weight=1) |         final_frame.grid_rowconfigure(0, weight=1) | ||||||
|         final_frame.grid_columnconfigure(0, weight=1) |         final_frame.grid_columnconfigure(0, weight=1) | ||||||
| @@ -511,12 +642,12 @@ class ImageBrowser(tk.Tk): | |||||||
|         ) |         ) | ||||||
|         self.rating_dropdown.pack(side=tk.LEFT, padx=5) |         self.rating_dropdown.pack(side=tk.LEFT, padx=5) | ||||||
|         self.upload_button = tk.Button( |         self.upload_button = tk.Button( | ||||||
|             upload_frame, text="Upload", command=self.upload_current_image |             upload_frame, text=_("Wyślij"), command=self.upload_current_image | ||||||
|         ) |         ) | ||||||
|         self.upload_button.pack(side=tk.LEFT, padx=5) |         self.upload_button.pack(side=tk.LEFT, padx=5) | ||||||
|         self.upload_button.config(state=tk.DISABLED) |         self.upload_button.config(state=tk.DISABLED) | ||||||
|         self.view_post_button = tk.Button( |         self.view_post_button = tk.Button( | ||||||
|             upload_frame, text="Wyświetl", command=self.view_current_post |             upload_frame, text=_("Wyświetl"), command=self.view_current_post | ||||||
|         ) |         ) | ||||||
|         self.view_post_button.pack(side=tk.LEFT, padx=5) |         self.view_post_button.pack(side=tk.LEFT, padx=5) | ||||||
|         self.view_post_button.config(state=tk.DISABLED) |         self.view_post_button.config(state=tk.DISABLED) | ||||||
| @@ -533,9 +664,9 @@ class ImageBrowser(tk.Tk): | |||||||
|  |  | ||||||
|     def update_status_bar(self): |     def update_status_bar(self): | ||||||
|         status_text = ( |         status_text = ( | ||||||
|             f"Przetworzono tagi: {len(self.tagger_processed)}/{self.total_files} plików | " |             f"{_('Przetworzono tagi:')} {len(self.tagger_processed)}/{self.total_files} {_('plików')} | " | ||||||
|             f"Zweryfikowano status uploadu: {self.upload_verified}/{self.total_files} plików | " |             f"{_('Zweryfikowano status uploadu:')} {self.upload_verified}/{self.total_files} {_('plików')} | " | ||||||
|             f"Zuploadowano: {self.uploaded_count}/{self.upload_verified} plików" |             f"{_('Zuploadowano:')} {self.uploaded_count}/{self.upload_verified} {_('plików')}" | ||||||
|         ) |         ) | ||||||
|         self.status_label.config(text=status_text) |         self.status_label.config(text=status_text) | ||||||
|  |  | ||||||
| @@ -563,7 +694,7 @@ class ImageBrowser(tk.Tk): | |||||||
|         Otwiera okno dialogowe wyboru folderu z obrazkami |         Otwiera okno dialogowe wyboru folderu z obrazkami | ||||||
|         i wczytuje pliki PNG z wybranego folderu. |         i wczytuje pliki PNG z wybranego folderu. | ||||||
|         """ |         """ | ||||||
|         folder = filedialog.askdirectory(title="Wybierz folder z obrazkami PNG") |         folder = filedialog.askdirectory(title=_("Wybierz folder z obrazkami PNG")) | ||||||
|         if folder: |         if folder: | ||||||
|             self.folder_path = folder |             self.folder_path = folder | ||||||
|             self.load_images() |             self.load_images() | ||||||
| @@ -593,7 +724,9 @@ class ImageBrowser(tk.Tk): | |||||||
|             self.show_image(0) |             self.show_image(0) | ||||||
|             self.post_load_processing() |             self.post_load_processing() | ||||||
|         else: |         else: | ||||||
|             messagebox.showinfo("Informacja", "Brak plików PNG w wybranym folderze.") |             messagebox.showinfo( | ||||||
|  |                 _("Informacja"), _("Brak plików PNG w wybranym folderze.") | ||||||
|  |             ) | ||||||
|  |  | ||||||
|     def post_load_processing(self): |     def post_load_processing(self): | ||||||
|         """ |         """ | ||||||
| @@ -675,7 +808,7 @@ class ImageBrowser(tk.Tk): | |||||||
|                         self.uploaded[file_path] = False |                         self.uploaded[file_path] = False | ||||||
|                     self.after(0, self.update_status_bar) |                     self.after(0, self.update_status_bar) | ||||||
|             except Exception as e: |             except Exception as e: | ||||||
|                 print("Błąd podczas sprawdzania paczki uploadu:", e) |                 print(_("Błąd podczas sprawdzania paczki uploadu:"), e) | ||||||
|         self.after(100, self.join_check_uploaded_files_thread) |         self.after(100, self.join_check_uploaded_files_thread) | ||||||
|  |  | ||||||
|     def update_button_states(self): |     def update_button_states(self): | ||||||
| @@ -716,14 +849,14 @@ class ImageBrowser(tk.Tk): | |||||||
|         post_ops_state = tk.NORMAL if post_id else tk.DISABLED |         post_ops_state = tk.NORMAL if post_id else tk.DISABLED | ||||||
|  |  | ||||||
|         # Update menu items |         # Update menu items | ||||||
|         self.file_menu.entryconfig("Wyślij wszystko", state=send_all_state) |         self.file_menu.entryconfig(_("Wyślij wszystko"), state=send_all_state) | ||||||
|         self.file_menu.entryconfig("Podmień tagi", state=post_ops_state) |         self.file_menu.entryconfig(_("Podmień tagi"), state=post_ops_state) | ||||||
|         self.file_menu.entryconfig("Otwórz post", state=post_ops_state) |         self.file_menu.entryconfig(_("Otwórz post"), state=post_ops_state) | ||||||
|  |  | ||||||
|         # Update buttons |         # Update buttons | ||||||
|         self.upload_button.config( |         self.upload_button.config( | ||||||
|             state=tk.NORMAL if has_current else tk.DISABLED, |             state=tk.NORMAL if has_current else tk.DISABLED, | ||||||
|             text="Podmień tagi" if post_id else "Wyślij", |             text=_("Podmień tagi") if post_id else _("Wyślij"), | ||||||
|             command=self.edit_current_image if post_id else self.upload_current_image, |             command=self.edit_current_image if post_id else self.upload_current_image, | ||||||
|         ) |         ) | ||||||
|         self.view_post_button.config(state=post_ops_state) |         self.view_post_button.config(state=post_ops_state) | ||||||
| @@ -731,7 +864,7 @@ class ImageBrowser(tk.Tk): | |||||||
|         # Special case for "Wyślij" menu item |         # Special case for "Wyślij" menu item | ||||||
|         wyślij_state = tk.DISABLED if post_id else tk.NORMAL |         wyślij_state = tk.DISABLED if post_id else tk.NORMAL | ||||||
|         self.file_menu.entryconfig( |         self.file_menu.entryconfig( | ||||||
|             "Wyślij", state=wyślij_state if has_current else tk.DISABLED |             _("Wyślij"), state=wyślij_state if has_current else tk.DISABLED | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def view_current_post(self): |     def view_current_post(self): | ||||||
| @@ -751,7 +884,7 @@ class ImageBrowser(tk.Tk): | |||||||
|                 for chunk in iter(lambda: f.read(chunk_size), b""): |                 for chunk in iter(lambda: f.read(chunk_size), b""): | ||||||
|                     hash_md5.update(chunk) |                     hash_md5.update(chunk) | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print("Błąd przy obliczaniu MD5:", e) |             print(_("Błąd przy obliczaniu MD5:"), e) | ||||||
|             return "" |             return "" | ||||||
|         return hash_md5.hexdigest() |         return hash_md5.hexdigest() | ||||||
|  |  | ||||||
| @@ -807,7 +940,7 @@ class ImageBrowser(tk.Tk): | |||||||
|             self.tagger_thread_idx += 1 |             self.tagger_thread_idx += 1 | ||||||
|             thread.start() |             thread.start() | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             messagebox.showerror("Błąd", f"Nie można załadować obrazka:\n{e}") |             messagebox.showerror(_("Błąd"), f"{_('Nie można załadować obrazka:')}\n{e}") | ||||||
|  |  | ||||||
|     def edit_current_image(self): |     def edit_current_image(self): | ||||||
|         """ |         """ | ||||||
| @@ -1016,13 +1149,21 @@ class ImageBrowser(tk.Tk): | |||||||
|             if tag in self.implication_graph: |             if tag in self.implication_graph: | ||||||
|                 implied_by_selected.update(nx.descendants(self.implication_graph, tag)) |                 implied_by_selected.update(nx.descendants(self.implication_graph, tag)) | ||||||
|             else: |             else: | ||||||
|                 print(f"Warning: Tag '{tag}' not found in implication graph") |                 print( | ||||||
|  |                     _("Warning: Tag '{tag}' not found in implication graph").format( | ||||||
|  |                         tag=tag | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|                 self.missing_tags.add(tag)  # Log missing tags |                 self.missing_tags.add(tag)  # Log missing tags | ||||||
|         for tag in selected_png_tags: |         for tag in selected_png_tags: | ||||||
|             if tag in self.implication_graph: |             if tag in self.implication_graph: | ||||||
|                 implied_by_selected.update(nx.descendants(self.implication_graph, tag)) |                 implied_by_selected.update(nx.descendants(self.implication_graph, tag)) | ||||||
|             else: |             else: | ||||||
|                 print(f"Warning: Tag '{tag}' not found in implication graph") |                 print( | ||||||
|  |                     _("Warning: Tag '{tag}' not found in implication graph").format( | ||||||
|  |                         tag=tag | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|                 self.missing_tags.add(tag)  # Log missing tags |                 self.missing_tags.add(tag)  # Log missing tags | ||||||
|  |  | ||||||
|         # Build visible list |         # Build visible list | ||||||
| @@ -1249,7 +1390,7 @@ class ImageBrowser(tk.Tk): | |||||||
|         # Ustaw komunikat, że Tagger pracuje |         # Ustaw komunikat, że Tagger pracuje | ||||||
|         self.tagger_tags_text.config(state=tk.NORMAL) |         self.tagger_tags_text.config(state=tk.NORMAL) | ||||||
|         self.tagger_tags_text.delete("1.0", tk.END) |         self.tagger_tags_text.delete("1.0", tk.END) | ||||||
|         self.tagger_tags_text.insert("1.0", "Tagger przetwarza...") |         self.tagger_tags_text.insert("1.0", _("Tagger przetwarza...")) | ||||||
|         self.tagger_tags_text.config(state=tk.DISABLED) |         self.tagger_tags_text.config(state=tk.DISABLED) | ||||||
|         file_path = self.image_files[self.current_index] |         file_path = self.image_files[self.current_index] | ||||||
|         result = self.get_tagger_results(file_path) |         result = self.get_tagger_results(file_path) | ||||||
| @@ -1264,14 +1405,21 @@ class ImageBrowser(tk.Tk): | |||||||
|         file_path, |         file_path, | ||||||
|         final_tags=None, |         final_tags=None, | ||||||
|         final_rating=None, |         final_rating=None, | ||||||
|         progress_queue=None, |         progress_queue: Optional[queue.Queue] = None, | ||||||
|         cancel_event=None, |         cancel_event: Optional[threading.Event] = None, | ||||||
|     ): |     ): | ||||||
|         base_file_name = os.path.basename(file_path) |         base_file_name = os.path.basename(file_path) | ||||||
|         if progress_queue: |         if progress_queue: | ||||||
|             progress_queue.put(("mode", "determinate")) |             progress_queue.put(("mode", "determinate")) | ||||||
|             progress_queue.put(("max", 100)) |             progress_queue.put(("max", 100)) | ||||||
|             progress_queue.put(("label", f"Wysyłam plik {base_file_name}...")) |             progress_queue.put( | ||||||
|  |                 ( | ||||||
|  |                     "label", | ||||||
|  |                     _("Wysyłam plik {base_file_name}...").format( | ||||||
|  |                         base_file_name=base_file_name | ||||||
|  |                     ), | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|         url = self.settings.base_url.rstrip("/") + "/api/danbooru/add_post" |         url = self.settings.base_url.rstrip("/") + "/api/danbooru/add_post" | ||||||
|         tags = ( |         tags = ( | ||||||
|             self.final_tags_text.get("1.0", tk.END).strip() |             self.final_tags_text.get("1.0", tk.END).strip() | ||||||
| @@ -1308,22 +1456,29 @@ class ImageBrowser(tk.Tk): | |||||||
|             show_warn = False |             show_warn = False | ||||||
|             post_url = None |             post_url = None | ||||||
|             if response.status_code in (200, 201): |             if response.status_code in (200, 201): | ||||||
|                 message = "Upload zakończony powodzeniem!" |                 message = _("Wysyłanie zakończone powodzeniem!") | ||||||
|                 post_url = response.headers.get("X-Danbooru-Location", None) |                 post_url = response.headers.get("X-Danbooru-Location", None) | ||||||
|             elif response.status_code == 409: |             elif response.status_code == 409: | ||||||
|                 message = f"Upload zakończony błędem.\nStatus: 409\nTreść: {response.headers.get('X-Danbooru-Errors', '')}" |                 message = _( | ||||||
|  |                     "Wysyłanie zakończone błędem.\nStatus: {status_code}\nTreść: {text}" | ||||||
|  |                 ).format( | ||||||
|  |                     status_code=response.status_code, | ||||||
|  |                     text=response.headers.get("X-Danbooru-Errors", ""), | ||||||
|  |                 ) | ||||||
|                 post_url = response.headers.get("X-Danbooru-Location", None) |                 post_url = response.headers.get("X-Danbooru-Location", None) | ||||||
|                 show_warn = True |                 show_warn = True | ||||||
|             else: |             else: | ||||||
|                 message = f"Upload zakończony błędem.\nStatus: {response.status_code}\nTreść: {response.text}" |                 message = _( | ||||||
|  |                     "Wysyłanie zakończone błędem.\nStatus: {status_code}\nTreść: {text}" | ||||||
|  |                 ).format(status_code=response.status_code, text=response.text) | ||||||
|                 show_warn = True |                 show_warn = True | ||||||
|             # Aktualizacja wyglądu listy – musimy użyć domyślnych argumentów w lambdzie, aby zachować bieżący indeks |             # Aktualizacja wyglądu listy – musimy użyć domyślnych argumentów w lambdzie, aby zachować bieżący indeks | ||||||
|             if show_warn: |             if show_warn: | ||||||
|                 if not final_tags: |                 if not final_tags: | ||||||
|                     messagebox.showwarning("Upload", message) |                     messagebox.showwarning(_("Wysyłanie"), message) | ||||||
|             else: |             else: | ||||||
|                 if not final_tags: |                 if not final_tags: | ||||||
|                     messagebox.showinfo("Upload", message) |                     messagebox.showinfo(_("Wysyłanie"), message) | ||||||
|                 self.after( |                 self.after( | ||||||
|                     0, |                     0, | ||||||
|                     lambda idx=self.image_files.index( |                     lambda idx=self.image_files.index( | ||||||
| @@ -1336,7 +1491,7 @@ class ImageBrowser(tk.Tk): | |||||||
|                 self.uploaded_count += 1 |                 self.uploaded_count += 1 | ||||||
|                 self.after(0, self.update_status_bar) |                 self.after(0, self.update_status_bar) | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             messagebox.showerror("Błąd uploadu", str(e)) |             messagebox.showerror(_("Błąd wysyłania"), str(e)) | ||||||
|         finally: |         finally: | ||||||
|             self.upload_button.after(0, self.update_button_states) |             self.upload_button.after(0, self.update_button_states) | ||||||
|  |  | ||||||
| @@ -1356,20 +1511,27 @@ class ImageBrowser(tk.Tk): | |||||||
|  |  | ||||||
|         if not post_id: |         if not post_id: | ||||||
|             messagebox.showerror( |             messagebox.showerror( | ||||||
|                 "Błąd edycji", "Post nie został znaleziony dla tego pliku" |                 _("Błąd edycji"), _("Post nie został znaleziony dla tego pliku") | ||||||
|             ) |             ) | ||||||
|             return |             return | ||||||
|  |  | ||||||
|         if progress_queue: |         if progress_queue: | ||||||
|             progress_queue.put(("mode", "determinate")) |             progress_queue.put(("mode", "determinate")) | ||||||
|             progress_queue.put(("max", 100)) |             progress_queue.put(("max", 100)) | ||||||
|             progress_queue.put(("label", f"Aktualizuję tagi dla {base_file_name}...")) |             progress_queue.put( | ||||||
|  |                 ( | ||||||
|  |                     "label", | ||||||
|  |                     _("Aktualizuję tagi dla {base_file_name}...").format( | ||||||
|  |                         base_file_name=base_file_name | ||||||
|  |                     ), | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
|             # Check for cancellation before starting the operation. |             # Check for cancellation before starting the operation. | ||||||
|             if cancel_event is not None and cancel_event.is_set(): |             if cancel_event is not None and cancel_event.is_set(): | ||||||
|                 if progress_queue: |                 if progress_queue: | ||||||
|                     progress_queue.put(("label", "Operacja anulowana")) |                     progress_queue.put(("label", _("Operacja anulowana"))) | ||||||
|                 return |                 return | ||||||
|  |  | ||||||
|             # Get authentication session and token |             # Get authentication session and token | ||||||
| @@ -1379,7 +1541,7 @@ class ImageBrowser(tk.Tk): | |||||||
|             # Check cancellation after login if needed. |             # Check cancellation after login if needed. | ||||||
|             if cancel_event is not None and cancel_event.is_set(): |             if cancel_event is not None and cancel_event.is_set(): | ||||||
|                 if progress_queue: |                 if progress_queue: | ||||||
|                     progress_queue.put(("label", "Operacja anulowana")) |                     progress_queue.put(("label", _("Operacja anulowana"))) | ||||||
|                 return |                 return | ||||||
|  |  | ||||||
|             # Prepare tags and rating |             # Prepare tags and rating | ||||||
| @@ -1410,7 +1572,7 @@ class ImageBrowser(tk.Tk): | |||||||
|             # Check for cancellation before sending the update request. |             # Check for cancellation before sending the update request. | ||||||
|             if cancel_event is not None and cancel_event.is_set(): |             if cancel_event is not None and cancel_event.is_set(): | ||||||
|                 if progress_queue: |                 if progress_queue: | ||||||
|                     progress_queue.put(("label", "Operacja anulowana")) |                     progress_queue.put(("label", _("Operacja anulowana"))) | ||||||
|                 return |                 return | ||||||
|  |  | ||||||
|             # Send update request |             # Send update request | ||||||
| @@ -1420,23 +1582,23 @@ class ImageBrowser(tk.Tk): | |||||||
|             if response.status_code == 302: |             if response.status_code == 302: | ||||||
|                 if progress_queue: |                 if progress_queue: | ||||||
|                     progress_queue.put(("progress", 100)) |                     progress_queue.put(("progress", 100)) | ||||||
|                 message = "Tagi zostały zaktualizowane!" |                 message = _("Tagi zostały zaktualizowane!") | ||||||
|                 if not final_tags:  # Only show success if not bulk operation |                 if not final_tags:  # Only show success if not bulk operation | ||||||
|                     messagebox.showinfo("Sukces edycji", message) |                     messagebox.showinfo(_("Sukces edycji"), message) | ||||||
|                 # Update UI state |                 # Update UI state | ||||||
|                 self.after(0, self.update_button_states) |                 self.after(0, self.update_button_states) | ||||||
|                 return |                 return | ||||||
|  |  | ||||||
|             # Handle other status codes |             # Handle other status codes | ||||||
|             error_msg = ( |             error_msg = _("Błąd podczas aktualizacji tagów\nStatus: {code}").format( | ||||||
|                 f"Błąd podczas aktualizacji tagów\nStatus: {response.status_code}" |                 code=response.status_code | ||||||
|             ) |             ) | ||||||
|             if response.text: |             if response.text: | ||||||
|                 error_msg += f"\nTreść: {response.text}" |                 error_msg += f"\n{_('Treść:')} {response.text}" | ||||||
|             messagebox.showerror("Błąd edycji", error_msg) |             messagebox.showerror("Błąd edycji", error_msg) | ||||||
|  |  | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             messagebox.showerror("Krytyczny błąd edycji", str(e)) |             messagebox.showerror(_("Krytyczny błąd edycji"), str(e)) | ||||||
|         finally: |         finally: | ||||||
|             if progress_queue: |             if progress_queue: | ||||||
|                 progress_queue.put(("progress", 100)) |                 progress_queue.put(("progress", 100)) | ||||||
| @@ -1448,12 +1610,18 @@ class ImageBrowser(tk.Tk): | |||||||
|         i wywołuje upload_file. |         i wywołuje upload_file. | ||||||
|         """ |         """ | ||||||
|         if not messagebox.askyesno( |         if not messagebox.askyesno( | ||||||
|             "Potwierdzenie", |             _("Potwierdzenie"), | ||||||
|             "Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\nKażdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\nUpewnij się, że tagi są poprawne!", |             _( | ||||||
|  |                 "Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\nKażdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\nUpewnij się, że tagi są poprawne!" | ||||||
|  |             ), | ||||||
|         ): |         ): | ||||||
|             return |             return | ||||||
|  |  | ||||||
|         def worker(progress_queue: queue = None, cancel_event: threading.Event = None): |         def worker( | ||||||
|  |             progress_queue: queue.Queue = None, | ||||||
|  |             cancel_event: threading.Event = None, | ||||||
|  |             secondary_progress_queue: queue.Queue = None, | ||||||
|  |         ): | ||||||
|             files_count = len(self.image_files) |             files_count = len(self.image_files) | ||||||
|             if progress_queue: |             if progress_queue: | ||||||
|                 progress_queue.put(("mode", "determinate")) |                 progress_queue.put(("mode", "determinate")) | ||||||
| @@ -1461,26 +1629,37 @@ class ImageBrowser(tk.Tk): | |||||||
|             file_idx = 0 |             file_idx = 0 | ||||||
|             for file_path in self.image_files: |             for file_path in self.image_files: | ||||||
|                 if progress_queue: |                 if progress_queue: | ||||||
|                     progress_queue.put(("progress", file_idx * 1.0 / files_count)) |                     progress_queue.put(("progress", file_idx * 100 / files_count)) | ||||||
|                     progress_queue.put( |                     progress_queue.put( | ||||||
|                         ("label", f"Wysyłam plik {file_idx+1}/{files_count}...") |                         ("label", f"Wysyłam plik {file_idx+1}/{files_count}...") | ||||||
|                     ) |                     ) | ||||||
|                 if cancel_event is not None and cancel_event.is_set(): |                 if cancel_event is not None and cancel_event.is_set(): | ||||||
|                     if progress_queue: |                     if progress_queue: | ||||||
|                         progress_queue.put(("label", f"Anulowano operację!")) |                         progress_queue.put(("label", _("Anulowano operację!"))) | ||||||
|                     return |                     return | ||||||
|                 if not self.uploaded.get(file_path, False): |                 if not self.uploaded.get(file_path, False): | ||||||
|                     final_tags, final_rating = ( |                     final_tags, final_rating = ( | ||||||
|                         self.compute_final_tags_and_rating_for_file(file_path) |                         self.compute_final_tags_and_rating_for_file(file_path) | ||||||
|                     ) |                     ) | ||||||
|                     print( |                     print( | ||||||
|                         f"Uploading {file_path} z tagami: {final_tags} i ratingiem: {final_rating}" |                         _( | ||||||
|  |                             "Wysyłanie {file_path} z tagami: {final_tags} i ratingiem: {final_rating}" | ||||||
|  |                         ).format( | ||||||
|  |                             file_path=file_path, | ||||||
|  |                             final_tags=final_tags, | ||||||
|  |                             final_rating=final_rating, | ||||||
|  |                         ) | ||||||
|                     ) |                     ) | ||||||
|                     self.upload_file( |                     self.upload_file( | ||||||
|                         file_path, final_tags=final_tags, final_rating=final_rating |                         file_path, | ||||||
|  |                         final_tags=final_tags, | ||||||
|  |                         final_rating=final_rating, | ||||||
|  |                         progress_queue=secondary_progress_queue, | ||||||
|  |                         cancel_event=cancel_event, | ||||||
|                     ) |                     ) | ||||||
|  |                 file_idx += 1 | ||||||
|             if progress_queue: |             if progress_queue: | ||||||
|                 progress_queue.put(("label", f"Przesłano pliki!")) |                 progress_queue.put(("label", _("Przesłano pliki!"))) | ||||||
|                 progress_queue.put(("progress", 100)) |                 progress_queue.put(("progress", 100)) | ||||||
|  |  | ||||||
|         self.processing_dialog = ProcessingDialog(self, worker) |         self.processing_dialog = ProcessingDialog(self, worker) | ||||||
|   | |||||||
| @@ -1,3 +1,6 @@ | |||||||
|  | from .I18N import _ | ||||||
|  |  | ||||||
|  |  | ||||||
| # Klasa pomocnicza do monitorowania postępu uploadu | # Klasa pomocnicza do monitorowania postępu uploadu | ||||||
| class ProgressFile: | class ProgressFile: | ||||||
|     def __init__(self, f, callback, total_size, cancel_event=None): |     def __init__(self, f, callback, total_size, cancel_event=None): | ||||||
| @@ -10,7 +13,7 @@ class ProgressFile: | |||||||
|     def read(self, size=-1): |     def read(self, size=-1): | ||||||
|         # Check for cancellation before reading more data |         # Check for cancellation before reading more data | ||||||
|         if self.cancel_event is not None and self.cancel_event.is_set(): |         if self.cancel_event is not None and self.cancel_event.is_set(): | ||||||
|             raise Exception("Upload cancelled by user.") |             raise Exception(_("Upload cancelled by user.")) | ||||||
|  |  | ||||||
|         data = self.f.read(size) |         data = self.f.read(size) | ||||||
|         self.read_bytes += len(data) |         self.read_bytes += len(data) | ||||||
|   | |||||||
| @@ -1,11 +1,15 @@ | |||||||
| from collections import deque | from collections import deque | ||||||
| import json | import json | ||||||
| import os | import os | ||||||
|  | import queue | ||||||
| import sqlite3 | import sqlite3 | ||||||
| import time |  | ||||||
| import requests |  | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
|  | import threading | ||||||
|  | import time | ||||||
|  |  | ||||||
|  | import requests | ||||||
|  |  | ||||||
|  | from .I18N import _ | ||||||
| from .settings import Settings | from .settings import Settings | ||||||
|  |  | ||||||
| # Stałe – auth_token (CSRF token) oraz ciasteczka | # Stałe – auth_token (CSRF token) oraz ciasteczka | ||||||
| @@ -51,7 +55,11 @@ class TagsRepo: | |||||||
|         regenerate = False |         regenerate = False | ||||||
|         if not Path(self.db_path).is_file(): |         if not Path(self.db_path).is_file(): | ||||||
|             regenerate = True |             regenerate = True | ||||||
|             print(f"Database file not found: {self.db_path}, will regenerate DB") |             print( | ||||||
|  |                 _("Database file not found: {path}, will regenerate DB").format( | ||||||
|  |                     path=self.db_path | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|         self.init_tags_db() |         self.init_tags_db() | ||||||
|         if regenerate: |         if regenerate: | ||||||
|             self.regenerate_db() |             self.regenerate_db() | ||||||
| @@ -132,9 +140,11 @@ class TagsRepo: | |||||||
|             conn.commit() |             conn.commit() | ||||||
|             conn.close() |             conn.close() | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print("Błąd przy inicjalizacji bazy tagów:", e) |             print(_("Błąd przy inicjalizacji bazy tagów:"), e) | ||||||
|  |  | ||||||
|     def regenerate_db(self, progress_queue=None, cancel_event=None): |     def regenerate_db( | ||||||
|  |         self, progress_queue: queue.Queue = None, cancel_event: threading.Event = None | ||||||
|  |     ): | ||||||
|         """ |         """ | ||||||
|         Regenerate the database of tags, aliases, and tag implications. |         Regenerate the database of tags, aliases, and tag implications. | ||||||
|  |  | ||||||
| @@ -146,7 +156,7 @@ class TagsRepo: | |||||||
|         cursor = conn.cursor() |         cursor = conn.cursor() | ||||||
|  |  | ||||||
|         if progress_queue: |         if progress_queue: | ||||||
|             progress_queue.put(("label", "Czyszczenie bazy danych...")) |             progress_queue.put(("label", _("Czyszczenie bazy danych..."))) | ||||||
|         cursor.execute("DELETE FROM tags") |         cursor.execute("DELETE FROM tags") | ||||||
|         cursor.execute("DELETE FROM tag_aliases") |         cursor.execute("DELETE FROM tag_aliases") | ||||||
|         conn.commit() |         conn.commit() | ||||||
| @@ -162,12 +172,19 @@ class TagsRepo: | |||||||
|         while True: |         while True: | ||||||
|             if cancel_event and cancel_event.is_set(): |             if cancel_event and cancel_event.is_set(): | ||||||
|                 if progress_queue: |                 if progress_queue: | ||||||
|                     progress_queue.put(("label", "Anulowano pobieranie tagów.")) |                     progress_queue.put(("label", _("Anulowano pobieranie tagów."))) | ||||||
|                 conn.close() |                 conn.close() | ||||||
|                 return |                 return | ||||||
|  |  | ||||||
|             if progress_queue: |             if progress_queue: | ||||||
|                 progress_queue.put(("label", f"Pobieranie tagów (od ID {last_id})...")) |                 progress_queue.put( | ||||||
|  |                     ( | ||||||
|  |                         "label", | ||||||
|  |                         _("Pobieranie tagów (od ID {last_id})...").format( | ||||||
|  |                             last_id=last_id | ||||||
|  |                         ), | ||||||
|  |                     ) | ||||||
|  |                 ) | ||||||
|             start_time = time.monotonic() |             start_time = time.monotonic() | ||||||
|             url = f"https://danbooru.donmai.us/tags.json?limit=1000&page=a{last_id}" |             url = f"https://danbooru.donmai.us/tags.json?limit=1000&page=a{last_id}" | ||||||
|             response = requests.get(url) |             response = requests.get(url) | ||||||
| @@ -176,7 +193,9 @@ class TagsRepo: | |||||||
|                     progress_queue.put( |                     progress_queue.put( | ||||||
|                         ( |                         ( | ||||||
|                             "label", |                             "label", | ||||||
|                             f"Błąd przy pobieraniu tagów od ID {last_id}: HTTP {response.status_code}", |                             _( | ||||||
|  |                                 "Błąd przy pobieraniu tagów od ID {last_id}: HTTP {code}" | ||||||
|  |                             ).format(last_id=last_id, code=response.status_code), | ||||||
|                         ) |                         ) | ||||||
|                     ) |                     ) | ||||||
|                 break |                 break | ||||||
| @@ -189,7 +208,9 @@ class TagsRepo: | |||||||
|             for item in data: |             for item in data: | ||||||
|                 if cancel_event and cancel_event.is_set(): |                 if cancel_event and cancel_event.is_set(): | ||||||
|                     if progress_queue: |                     if progress_queue: | ||||||
|                         progress_queue.put(("label", "Anulowano przetwarzanie tagów.")) |                         progress_queue.put( | ||||||
|  |                             ("label", _("Anulowano przetwarzanie tagów.")) | ||||||
|  |                         ) | ||||||
|                     conn.close() |                     conn.close() | ||||||
|                     return |                     return | ||||||
|                 tag_id = item.get("id") |                 tag_id = item.get("id") | ||||||
| @@ -223,7 +244,9 @@ class TagsRepo: | |||||||
|                 time.sleep(min_interval - elapsed) |                 time.sleep(min_interval - elapsed) | ||||||
|  |  | ||||||
|         if progress_queue: |         if progress_queue: | ||||||
|             progress_queue.put(("label", f"Pobrano {len(data_list)} tagów...")) |             progress_queue.put( | ||||||
|  |                 ("label", _("Pobrano {count} tagów...").format(count=len(data_list))) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|         data_list = sorted(data_list, key=lambda x: x[0]) |         data_list = sorted(data_list, key=lambda x: x[0]) | ||||||
|         data_list = [(idx,) + row for idx, row in enumerate(data_list)] |         data_list = [(idx,) + row for idx, row in enumerate(data_list)] | ||||||
| @@ -244,13 +267,20 @@ class TagsRepo: | |||||||
|         while True: |         while True: | ||||||
|             if cancel_event and cancel_event.is_set(): |             if cancel_event and cancel_event.is_set(): | ||||||
|                 if progress_queue: |                 if progress_queue: | ||||||
|                     progress_queue.put(("label", "Anulowano pobieranie aliasów tagów.")) |                     progress_queue.put( | ||||||
|  |                         ("label", _("Anulowano pobieranie aliasów tagów.")) | ||||||
|  |                     ) | ||||||
|                 conn.close() |                 conn.close() | ||||||
|                 return |                 return | ||||||
|  |  | ||||||
|             if progress_queue: |             if progress_queue: | ||||||
|                 progress_queue.put( |                 progress_queue.put( | ||||||
|                     ("label", f"Pobieranie aliasów tagów (od ID {last_id})...") |                     ( | ||||||
|  |                         "label", | ||||||
|  |                         _("Pobieranie aliasów tagów (od ID {last_id})...").format( | ||||||
|  |                             last_id=last_id | ||||||
|  |                         ), | ||||||
|  |                     ) | ||||||
|                 ) |                 ) | ||||||
|             start_time = time.monotonic() |             start_time = time.monotonic() | ||||||
|             url = ( |             url = ( | ||||||
| @@ -263,7 +293,9 @@ class TagsRepo: | |||||||
|                     progress_queue.put( |                     progress_queue.put( | ||||||
|                         ( |                         ( | ||||||
|                             "label", |                             "label", | ||||||
|                             f"Błąd przy pobieraniu aliasów tagów od ID {last_id}: HTTP {response.status_code}", |                             _( | ||||||
|  |                                 "Błąd przy pobieraniu aliasów tagów od ID {last_id}: HTTP {code}" | ||||||
|  |                             ).format(last_id=last_id, code=response.status_code), | ||||||
|                         ) |                         ) | ||||||
|                     ) |                     ) | ||||||
|                 break |                 break | ||||||
| @@ -277,7 +309,7 @@ class TagsRepo: | |||||||
|                 if cancel_event and cancel_event.is_set(): |                 if cancel_event and cancel_event.is_set(): | ||||||
|                     if progress_queue: |                     if progress_queue: | ||||||
|                         progress_queue.put( |                         progress_queue.put( | ||||||
|                             ("label", "Anulowano przetwarzanie aliasów tagów.") |                             ("label", _("Anulowano przetwarzanie aliasów tagów.")) | ||||||
|                         ) |                         ) | ||||||
|                     conn.close() |                     conn.close() | ||||||
|                     return |                     return | ||||||
| @@ -296,7 +328,12 @@ class TagsRepo: | |||||||
|                 time.sleep(min_interval - elapsed) |                 time.sleep(min_interval - elapsed) | ||||||
|  |  | ||||||
|         if progress_queue: |         if progress_queue: | ||||||
|             progress_queue.put(("label", f"Pobrano {len(data_list)} aliasów tagów...")) |             progress_queue.put( | ||||||
|  |                 ( | ||||||
|  |                     "label", | ||||||
|  |                     _("Pobrano {count} aliasów tagów...").format(count=len(data_list)), | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|         data_list = sorted(data_list, key=lambda x: x[0]) |         data_list = sorted(data_list, key=lambda x: x[0]) | ||||||
|         data_list = [(idx,) + row for idx, row in enumerate(data_list)] |         data_list = [(idx,) + row for idx, row in enumerate(data_list)] | ||||||
| @@ -335,14 +372,19 @@ class TagsRepo: | |||||||
|             if cancel_event and cancel_event.is_set(): |             if cancel_event and cancel_event.is_set(): | ||||||
|                 if progress_queue: |                 if progress_queue: | ||||||
|                     progress_queue.put( |                     progress_queue.put( | ||||||
|                         ("label", "Anulowano pobieranie implikacji tagów.") |                         ("label", _("Anulowano pobieranie implikacji tagów.")) | ||||||
|                     ) |                     ) | ||||||
|                 conn.close() |                 conn.close() | ||||||
|                 return |                 return | ||||||
|  |  | ||||||
|             if progress_queue: |             if progress_queue: | ||||||
|                 progress_queue.put( |                 progress_queue.put( | ||||||
|                     ("label", f"Pobieranie implikacji tagów (od ID {last_id})...") |                     ( | ||||||
|  |                         "label", | ||||||
|  |                         _("Pobieranie implikacji tagów (od ID {last_id})...").format( | ||||||
|  |                             last_id=last_id | ||||||
|  |                         ), | ||||||
|  |                     ) | ||||||
|                 ) |                 ) | ||||||
|             start_time = time.monotonic() |             start_time = time.monotonic() | ||||||
|             url = f"https://danbooru.donmai.us/tag_implications.json?limit=1000&page=a{last_id}" |             url = f"https://danbooru.donmai.us/tag_implications.json?limit=1000&page=a{last_id}" | ||||||
| @@ -352,7 +394,9 @@ class TagsRepo: | |||||||
|                     progress_queue.put( |                     progress_queue.put( | ||||||
|                         ( |                         ( | ||||||
|                             "label", |                             "label", | ||||||
|                             f"Błąd przy pobieraniu implikacji tagów od ID {last_id}: HTTP {response.status_code}", |                             _( | ||||||
|  |                                 "Błąd przy pobieraniu implikacji tagów od ID {last_id}: HTTP {code}" | ||||||
|  |                             ).format(last_id=last_id, code=response.status_code), | ||||||
|                         ) |                         ) | ||||||
|                     ) |                     ) | ||||||
|                 break |                 break | ||||||
| @@ -366,7 +410,7 @@ class TagsRepo: | |||||||
|                 if cancel_event and cancel_event.is_set(): |                 if cancel_event and cancel_event.is_set(): | ||||||
|                     if progress_queue: |                     if progress_queue: | ||||||
|                         progress_queue.put( |                         progress_queue.put( | ||||||
|                             ("label", "Anulowano przetwarzanie implikacji tagów.") |                             ("label", _("Anulowano przetwarzanie implikacji tagów.")) | ||||||
|                         ) |                         ) | ||||||
|                     conn.close() |                     conn.close() | ||||||
|                     return |                     return | ||||||
| @@ -411,7 +455,12 @@ class TagsRepo: | |||||||
|  |  | ||||||
|         if progress_queue: |         if progress_queue: | ||||||
|             progress_queue.put( |             progress_queue.put( | ||||||
|                 ("label", f"Pobrano implikacje dla {len(tag_dict)} tagów...") |                 ( | ||||||
|  |                     "label", | ||||||
|  |                     _("Pobrano implikacje dla {count} tagów...").format( | ||||||
|  |                         count=len(tag_dict) | ||||||
|  |                     ), | ||||||
|  |                 ) | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|         # ----------------------- |         # ----------------------- | ||||||
| @@ -435,7 +484,7 @@ class TagsRepo: | |||||||
|         conn.close() |         conn.close() | ||||||
|  |  | ||||||
|         if progress_queue: |         if progress_queue: | ||||||
|             progress_queue.put(("label", "Regeneracja bazy zakończona.")) |             progress_queue.put(("label", _("Regeneracja bazy zakończona."))) | ||||||
|  |  | ||||||
|     def build_transitive_closure(self, tag_dict): |     def build_transitive_closure(self, tag_dict): | ||||||
|         closure = set() |         closure = set() | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import tkinter as tk | import tkinter as tk | ||||||
| from tkinter import font | from tkinter import font | ||||||
|  |  | ||||||
|  | from .I18N import _ | ||||||
| from .TagsRepo import TagsRepo | from .TagsRepo import TagsRepo | ||||||
| from .common import open_tag_wiki_url | from .common import open_tag_wiki_url | ||||||
| from .tag_processing import process_tag | from .tag_processing import process_tag | ||||||
| @@ -24,7 +25,37 @@ class AutocompleteEntry(tk.Entry): | |||||||
|         self.bind("<Up>", self.on_up) |         self.bind("<Up>", self.on_up) | ||||||
|         self.bind("<Return>", self.on_return) |         self.bind("<Return>", self.on_return) | ||||||
|         self.bind("<Tab>", self.on_return) |         self.bind("<Tab>", self.on_return) | ||||||
|         self.bind("<FocusOut>", lambda e: self.hide_listbox()) |         self.bind("<FocusOut>", self.on_focus_out) | ||||||
|  |  | ||||||
|  |     def on_focus_out(self, event): | ||||||
|  |         self.after(10, self.delayed_focus_check) | ||||||
|  |  | ||||||
|  |     def delayed_focus_check(self): | ||||||
|  |         try: | ||||||
|  |             # Get current focus using Tk's internal command | ||||||
|  |             focused_name = self.tk.call("focus") | ||||||
|  |             if not focused_name: | ||||||
|  |                 self.hide_listbox() | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |             # Convert to widget object | ||||||
|  |             focused_widget = self.nametowidget(focused_name) | ||||||
|  |  | ||||||
|  |             # Check if focus is in our listbox hierarchy | ||||||
|  |             if self.listbox_window and self.is_child_of_listbox_window(focused_widget): | ||||||
|  |                 return | ||||||
|  |         except (KeyError, tk.TclError): | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         self.hide_listbox() | ||||||
|  |  | ||||||
|  |     def is_child_of_listbox_window(self, widget): | ||||||
|  |         current = widget | ||||||
|  |         while current: | ||||||
|  |             if current == self.listbox_window: | ||||||
|  |                 return True | ||||||
|  |             current = current.master | ||||||
|  |         return False | ||||||
|  |  | ||||||
|     def on_keyrelease(self, event): |     def on_keyrelease(self, event): | ||||||
|         if event.keysym in ("Down", "Up", "Return", "Tab"): |         if event.keysym in ("Down", "Up", "Return", "Tab"): | ||||||
| @@ -99,7 +130,7 @@ class AutocompleteEntry(tk.Entry): | |||||||
|                 self.suggestion_map[display_text] = tag_insert |                 self.suggestion_map[display_text] = tag_insert | ||||||
|             return suggestions |             return suggestions | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print("Błąd przy pobieraniu sugestii:", e) |             print(_("Błąd przy pobieraniu sugestii:"), e) | ||||||
|             return [] |             return [] | ||||||
|  |  | ||||||
|     def show_listbox(self): |     def show_listbox(self): | ||||||
| @@ -139,12 +170,22 @@ class AutocompleteEntry(tk.Entry): | |||||||
|  |  | ||||||
|     def on_listbox_click(self, event): |     def on_listbox_click(self, event): | ||||||
|         if self.listbox: |         if self.listbox: | ||||||
|             index = self.listbox.curselection() |             # Process click first before changing focus | ||||||
|             if index: |             index = self.listbox.nearest(event.y) | ||||||
|                 value = self.listbox.get(index) |             if index >= 0: | ||||||
|  |                 # Get the display text from listbox | ||||||
|  |                 selected_display = self.listbox.get(index) | ||||||
|  |                 # Get the actual tag from the suggestion map | ||||||
|  |                 tag = self.suggestion_map.get(selected_display, selected_display) | ||||||
|  |  | ||||||
|  |                 if self.callback: | ||||||
|  |                     self.callback(tag) | ||||||
|  |  | ||||||
|  |                 # Clear the entry and hide listbox | ||||||
|                 self.delete(0, tk.END) |                 self.delete(0, tk.END) | ||||||
|                 self.insert(tk.END, value) |                 # Then explicitly manage focus | ||||||
|             self.hide_listbox() |                 self.focus_set()  # Use focus_set() instead of focus_force() | ||||||
|  |                 self.hide_listbox() | ||||||
|             return "break" |             return "break" | ||||||
|  |  | ||||||
|     def on_listbox_motion(self, event): |     def on_listbox_motion(self, event): | ||||||
|   | |||||||
| @@ -3,10 +3,11 @@ import subprocess | |||||||
| from bs4 import BeautifulSoup | from bs4 import BeautifulSoup | ||||||
| import requests | import requests | ||||||
|  |  | ||||||
|  | from .I18N import _ | ||||||
| from .settings import Settings | from .settings import Settings | ||||||
|  |  | ||||||
|  |  | ||||||
| def open_tag_wiki_url(tag, settings: Settings): | def open_tag_wiki_url(tag: str, settings: Settings): | ||||||
|     """Otwiera w przeglądarce URL strony wiki dla podanego tagu.""" |     """Otwiera w przeglądarce URL strony wiki dla podanego tagu.""" | ||||||
|     # Usuń prefiksy |     # Usuń prefiksy | ||||||
|     for prefix in [ |     for prefix in [ | ||||||
| @@ -31,7 +32,7 @@ def open_webbrowser(url, settings: Settings): | |||||||
|             subprocess.run([settings.browser, url], check=True) |             subprocess.run([settings.browser, url], check=True) | ||||||
|             return |             return | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print("Błąd przy otwieraniu przeglądarki:", e) |             print(_("Błąd przy otwieraniu przeglądarki:"), e) | ||||||
|     import webbrowser |     import webbrowser | ||||||
|  |  | ||||||
|     webbrowser.open(url) |     webbrowser.open(url) | ||||||
| @@ -68,18 +69,18 @@ def login(settings: Settings): | |||||||
|         shm_session = session.cookies.get("shm_session") |         shm_session = session.cookies.get("shm_session") | ||||||
|  |  | ||||||
|         if not (shm_user and shm_session): |         if not (shm_user and shm_session): | ||||||
|             raise Exception("Login succeeded, but expected cookies were not set.") |             raise Exception(_("Login succeeded, but expected cookies were not set.")) | ||||||
|  |  | ||||||
|         print("Login successful. Cookies stored in session:") |         print(_("Login successful. Cookies stored in session.")) | ||||||
|         print(f"shm_user: {shm_user}") |  | ||||||
|         print(f"shm_session: {shm_session}") |  | ||||||
|  |  | ||||||
|         return session |         return session | ||||||
|     else: |     else: | ||||||
|         raise Exception(f"Login failed: {response.status_code} - {response.text}") |         raise Exception( | ||||||
|  |             f"{_('Login failed:')} {response.status_code} - {response.text}" | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_auth_token(session, settings): | def get_auth_token(session: requests.Session, settings: Settings) -> str: | ||||||
|     """ |     """ | ||||||
|     Given a logged-in session and settings, fetch the user page |     Given a logged-in session and settings, fetch the user page | ||||||
|     and extract the auth_token from the hidden input field. |     and extract the auth_token from the hidden input field. | ||||||
| @@ -91,7 +92,7 @@ def get_auth_token(session, settings): | |||||||
|     # Retrieve the user identifier from cookies |     # Retrieve the user identifier from cookies | ||||||
|     shm_user = session.cookies.get("shm_user") |     shm_user = session.cookies.get("shm_user") | ||||||
|     if not shm_user: |     if not shm_user: | ||||||
|         raise Exception("shm_user cookie not found; login might have failed.") |         raise Exception(_("shm_user cookie not found; login might have failed.")) | ||||||
|  |  | ||||||
|     # Build the URL to fetch, e.g., /user/<shm_user> |     # Build the URL to fetch, e.g., /user/<shm_user> | ||||||
|     user_url = f"{settings.base_url.rstrip('/')}/user/{shm_user}" |     user_url = f"{settings.base_url.rstrip('/')}/user/{shm_user}" | ||||||
| @@ -108,7 +109,9 @@ def get_auth_token(session, settings): | |||||||
|  |  | ||||||
|     if response.status_code != 200: |     if response.status_code != 200: | ||||||
|         raise Exception( |         raise Exception( | ||||||
|             f"Failed to load {user_url}, status code: {response.status_code}" |             _("Failed to load {user_url}, status code: {code}").format( | ||||||
|  |                 user_url=user_url, code=response.status_code | ||||||
|  |             ) | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     # Parse the returned HTML with BeautifulSoup |     # Parse the returned HTML with BeautifulSoup | ||||||
| @@ -118,7 +121,7 @@ def get_auth_token(session, settings): | |||||||
|     auth_input = soup.find("input", {"name": "auth_token"}) |     auth_input = soup.find("input", {"name": "auth_token"}) | ||||||
|     if auth_input and auth_input.has_attr("value"): |     if auth_input and auth_input.has_attr("value"): | ||||||
|         auth_token = auth_input["value"] |         auth_token = auth_input["value"] | ||||||
|         print(f"Found auth_token: {auth_token}") |         print(_("Found auth_token:"), auth_token) | ||||||
|         return auth_token |         return auth_token | ||||||
|     else: |     else: | ||||||
|         raise Exception("auth_token not found in the HTML page.") |         raise Exception(_("auth_token not found in the HTML page.")) | ||||||
|   | |||||||
							
								
								
									
										491
									
								
								kapitanbooru_uploader/locales/en/LC_MESSAGES/messages.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										491
									
								
								kapitanbooru_uploader/locales/en/LC_MESSAGES/messages.po
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,491 @@ | |||||||
|  | msgid "" | ||||||
|  | msgstr "" | ||||||
|  | "Project-Id-Version: Kapitanbooru Uploader 0.4.0\n" | ||||||
|  | "Report-Msgid-Bugs-To: \n" | ||||||
|  | "POT-Creation-Date: 2025-03-02 00:39+0100\n" | ||||||
|  | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
|  | "Content-Transfer-Encoding: 8bit\n" | ||||||
|  |  | ||||||
|  | #: autocomplete.py:133 | ||||||
|  | msgid "Błąd przy pobieraniu sugestii:" | ||||||
|  | msgstr "Error fetching suggestions:" | ||||||
|  |  | ||||||
|  | #: common.py:35 | ||||||
|  | msgid "Błąd przy otwieraniu przeglądarki:" | ||||||
|  | msgstr "Error opening browser:" | ||||||
|  |  | ||||||
|  | #: common.py:72 | ||||||
|  | msgid "Login succeeded, but expected cookies were not set." | ||||||
|  | msgstr "Login succeeded, but expected cookies were not set." | ||||||
|  |  | ||||||
|  | #: common.py:74 | ||||||
|  | msgid "Login successful. Cookies stored in session." | ||||||
|  | msgstr "Login successful. Cookies stored in session." | ||||||
|  |  | ||||||
|  | #: common.py:79 | ||||||
|  | msgid "Login failed:" | ||||||
|  | msgstr "Login failed:" | ||||||
|  |  | ||||||
|  | #: common.py:95 | ||||||
|  | msgid "shm_user cookie not found; login might have failed." | ||||||
|  | msgstr "shm_user cookie not found; login might have failed." | ||||||
|  |  | ||||||
|  | #: common.py:112 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Failed to load {user_url}, status code: {code}" | ||||||
|  | msgstr "Failed to load {user_url}, status code: {code}" | ||||||
|  |  | ||||||
|  | #: common.py:124 | ||||||
|  | msgid "Found auth_token:" | ||||||
|  | msgstr "Found auth_token:" | ||||||
|  |  | ||||||
|  | #: common.py:127 | ||||||
|  | msgid "auth_token not found in the HTML page." | ||||||
|  | msgstr "auth_token not found in the HTML page." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:30 | ||||||
|  | msgid "Processing..." | ||||||
|  | msgstr "Processing..." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:34 | ||||||
|  | msgid "Processing, please wait..." | ||||||
|  | msgstr "Processing, please wait..." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:263 | ||||||
|  | msgid "Błąd przy otwieraniu pliku" | ||||||
|  | msgstr "Error opening file" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:305 | ||||||
|  | msgid "Tagger przetworzył:" | ||||||
|  | msgstr "Tagger processed:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:308 | ||||||
|  | msgid "Błąd Taggera dla" | ||||||
|  | msgstr "Tagger error for" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:334 | ||||||
|  | msgid "Otwórz folder" | ||||||
|  | msgstr "Open folder" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:337 ImageBrowser.py:600 ImageBrowser.py:814 | ||||||
|  | #: ImageBrowser.py:822 | ||||||
|  | msgid "Wyślij" | ||||||
|  | msgstr "Send" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:340 ImageBrowser.py:807 | ||||||
|  | msgid "Wyślij wszystko" | ||||||
|  | msgstr "Send all" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:344 ImageBrowser.py:808 ImageBrowser.py:814 | ||||||
|  | msgid "Podmień tagi" | ||||||
|  | msgstr "Replace tags" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:347 ImageBrowser.py:809 | ||||||
|  | msgid "Otwórz post" | ||||||
|  | msgstr "Open post" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:350 | ||||||
|  | msgid "Zakończ" | ||||||
|  | msgstr "Finish" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:352 | ||||||
|  | msgid "Plik" | ||||||
|  | msgstr "File" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:356 ImageBrowser.py:430 | ||||||
|  | msgid "Ustawienia" | ||||||
|  | msgstr "Settings" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:359 | ||||||
|  | msgid "Wyczyść cache Taggera" | ||||||
|  | msgstr "Clear Tagger cache" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:362 | ||||||
|  | msgid "Zregeneruj bazę tagów" | ||||||
|  | msgstr "Regenerate tag database" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:364 | ||||||
|  | msgid "Opcje" | ||||||
|  | msgstr "Options" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:367 | ||||||
|  | msgid "About" | ||||||
|  | msgstr "About" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:368 | ||||||
|  | msgid "Help" | ||||||
|  | msgstr "Help" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:373 | ||||||
|  | msgid "About Kapitanbooru Uploader" | ||||||
|  | msgstr "About Kapitanbooru Uploader" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:386 | ||||||
|  | msgid "A GUI application for uploading images to KapitanBooru." | ||||||
|  | msgstr "A GUI application for uploading images to KapitanBooru." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:387 | ||||||
|  | msgid "Features include image upload, tag management, automatic" | ||||||
|  | msgstr "Features include image upload, tag management, automatic" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:388 | ||||||
|  | msgid "tagging with wdtagger, and cache management." | ||||||
|  | msgstr "tagging with wdtagger, and cache management." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:390 | ||||||
|  | msgid "Authors:" | ||||||
|  | msgstr "Authors:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:393 | ||||||
|  | msgid "License: MIT License" | ||||||
|  | msgstr "License: MIT License" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:396 | ||||||
|  | msgid "Repository:" | ||||||
|  | msgstr "Repository:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:397 | ||||||
|  | msgid "Website:" | ||||||
|  | msgstr "Website:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:414 | ||||||
|  | msgid "Close" | ||||||
|  | msgstr "Close" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:422 ImageBrowser.py:425 | ||||||
|  | msgid "Cache" | ||||||
|  | msgstr "Cache" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:422 | ||||||
|  | msgid "Cache Taggera zostało wyczyszczone." | ||||||
|  | msgstr "Tagger cache has been cleared." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:425 | ||||||
|  | msgid "Błąd przy czyszczeniu cache:" | ||||||
|  | msgstr "Error clearing cache:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:434 | ||||||
|  | msgid "Login:" | ||||||
|  | msgstr "Login:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:440 | ||||||
|  | msgid "Hasło:" | ||||||
|  | msgstr "Password:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:446 | ||||||
|  | msgid "Base URL:" | ||||||
|  | msgstr "Base URL:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:452 | ||||||
|  | msgid "Default Tags:" | ||||||
|  | msgstr "Default Tags:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:458 | ||||||
|  | msgid "Browser:" | ||||||
|  | msgstr "Browser:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:472 | ||||||
|  | msgid "Language:" | ||||||
|  | msgstr "Language:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:504 | ||||||
|  | msgid "Zapisz" | ||||||
|  | msgstr "Save" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:546 | ||||||
|  | msgid "PNG Tags" | ||||||
|  | msgstr "PNG Tags" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:558 | ||||||
|  | msgid "Tagger Tags" | ||||||
|  | msgstr "Tagger Tags" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:572 | ||||||
|  | msgid "Manual Tags" | ||||||
|  | msgstr "Manual Tags" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:580 | ||||||
|  | msgid "Final Tags" | ||||||
|  | msgstr "Final Tags" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:605 | ||||||
|  | msgid "Wyświetl" | ||||||
|  | msgstr "Display" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:622 | ||||||
|  | msgid "Przetworzono tagi:" | ||||||
|  | msgstr "Processed tags:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:622 ImageBrowser.py:623 ImageBrowser.py:624 | ||||||
|  | msgid "plików" | ||||||
|  | msgstr "files" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:623 | ||||||
|  | msgid "Zweryfikowano status uploadu:" | ||||||
|  | msgstr "Upload status verified:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:624 | ||||||
|  | msgid "Zuploadowano:" | ||||||
|  | msgstr "Uploaded:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:652 | ||||||
|  | msgid "Wybierz folder z obrazkami PNG" | ||||||
|  | msgstr "Select folder with PNG images" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:683 | ||||||
|  | msgid "Informacja" | ||||||
|  | msgstr "Information" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:683 | ||||||
|  | msgid "Brak plików PNG w wybranym folderze." | ||||||
|  | msgstr "No PNG files in the selected folder." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:766 | ||||||
|  | msgid "Błąd podczas sprawdzania paczki uploadu:" | ||||||
|  | msgstr "Error while checking upload package:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:842 | ||||||
|  | msgid "Błąd przy obliczaniu MD5:" | ||||||
|  | msgstr "Error calculating MD5:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:898 | ||||||
|  | msgid "Błąd" | ||||||
|  | msgstr "Error" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:898 | ||||||
|  | msgid "Nie można załadować obrazka:" | ||||||
|  | msgstr "Unable to load image:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1108 ImageBrowser.py:1118 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Warning: Tag '{tag}' not found in implication graph" | ||||||
|  | msgstr "Warning: Tag '{tag}' not found in implication graph" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1348 | ||||||
|  | msgid "Tagger przetwarza..." | ||||||
|  | msgstr "Tagger processing..." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1373 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Wysyłam plik {base_file_name}..." | ||||||
|  | msgstr "Sending file {base_file_name}..." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1414 | ||||||
|  | msgid "Wysyłanie zakończone powodzeniem!" | ||||||
|  | msgstr "Upload completed successfully!" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1418 ImageBrowser.py:1427 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "" | ||||||
|  | "Wysyłanie zakończone błędem.\n" | ||||||
|  | "Status: {status_code}\n" | ||||||
|  | "Treść: {text}" | ||||||
|  | msgstr "" | ||||||
|  | "Upload failed.\nStatus: {status_code}\nContent: {text}" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1433 ImageBrowser.py:1436 | ||||||
|  | msgid "Wysyłanie" | ||||||
|  | msgstr "Uploading" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1449 | ||||||
|  | msgid "Błąd wysyłania" | ||||||
|  | msgstr "Upload error" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1469 | ||||||
|  | msgid "Błąd edycji" | ||||||
|  | msgstr "Edit error" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1469 | ||||||
|  | msgid "Post nie został znaleziony dla tego pliku" | ||||||
|  | msgstr "Post not found for this file" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1479 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Aktualizuję tagi dla {base_file_name}..." | ||||||
|  | msgstr "Updating tags for {base_file_name}..." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1489 ImageBrowser.py:1499 ImageBrowser.py:1530 | ||||||
|  | msgid "Operacja anulowana" | ||||||
|  | msgstr "Operation cancelled" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1540 | ||||||
|  | msgid "Tagi zostały zaktualizowane!" | ||||||
|  | msgstr "Tags have been updated!" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1542 | ||||||
|  | msgid "Sukces edycji" | ||||||
|  | msgstr "Edit successful" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1548 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "" | ||||||
|  | "Błąd podczas aktualizacji tagów\n" | ||||||
|  | "Status: {code}" | ||||||
|  | msgstr "" | ||||||
|  | "Error updating tags\nStatus: {code}" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1552 | ||||||
|  | msgid "Treść:" | ||||||
|  | msgstr "Content:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1556 | ||||||
|  | msgid "Krytyczny błąd edycji" | ||||||
|  | msgstr "Critical edit error" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1568 | ||||||
|  | msgid "Potwierdzenie" | ||||||
|  | msgstr "Confirmation" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1570 | ||||||
|  | msgid "" | ||||||
|  | "Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\n" | ||||||
|  | "Każdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\n" | ||||||
|  | "Upewnij się, że tagi są poprawne!" | ||||||
|  | msgstr "" | ||||||
|  | "Are you sure you want to upload all unsubmitted files?\n" | ||||||
|  | "Each will be tagged with 'meta:auto_upload'.\n" | ||||||
|  | "Make sure the tags are correct!" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1589 | ||||||
|  | msgid "Anulowano operację!" | ||||||
|  | msgstr "Operation cancelled!" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1597 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "" | ||||||
|  | "Wysyłanie {file_path} z tagami: {final_tags} i ratingiem: {final_rating}" | ||||||
|  | msgstr "Uploading {file_path} with tags: {final_tags} and rating: {final_rating}" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1608 | ||||||
|  | msgid "Przesłano pliki!" | ||||||
|  | msgstr "Files have been uploaded!" | ||||||
|  |  | ||||||
|  | #: ProgressFile.py:16 | ||||||
|  | msgid "Upload cancelled by user." | ||||||
|  | msgstr "Upload cancelled by user." | ||||||
|  |  | ||||||
|  | #: settings.py:163 | ||||||
|  | msgid "Błąd podczas ładowania ustawień:" | ||||||
|  | msgstr "Error loading settings:" | ||||||
|  |  | ||||||
|  | #: settings.py:184 | ||||||
|  | msgid "Błąd podczas zapisywania ustawień:" | ||||||
|  | msgstr "Error saving settings:" | ||||||
|  |  | ||||||
|  | #: tagger_cache.py:63 | ||||||
|  | msgid "Błąd przy odczycie cache dla" | ||||||
|  | msgstr "Error reading cache for" | ||||||
|  |  | ||||||
|  | #: tagger_cache.py:88 | ||||||
|  | msgid "Błąd przy zapisie cache dla" | ||||||
|  | msgstr "Error writing cache for" | ||||||
|  |  | ||||||
|  | #: tagger_cache.py:98 | ||||||
|  | msgid "Błąd przy usuwaniu cache dla" | ||||||
|  | msgstr "Error deleting cache for" | ||||||
|  |  | ||||||
|  | #: tagger_cache.py:111 | ||||||
|  | msgid "Błąd przy czyszczeniu przeterminowanego cache:" | ||||||
|  | msgstr "Error clearing expired cache:" | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:59 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Database file not found: {path}, will regenerate DB" | ||||||
|  | msgstr "Database file not found: {path}, will regenerate DB" | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:143 | ||||||
|  | msgid "Błąd przy inicjalizacji bazy tagów:" | ||||||
|  | msgstr "Error initializing tag database:" | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:159 | ||||||
|  | msgid "Czyszczenie bazy danych..." | ||||||
|  | msgstr "Cleaning database..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:175 | ||||||
|  | msgid "Anulowano pobieranie tagów." | ||||||
|  | msgstr "Tag fetching cancelled." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:183 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Pobieranie tagów (od ID {last_id})..." | ||||||
|  | msgstr "Fetching tags (from ID {last_id})..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:197 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Błąd przy pobieraniu tagów od ID {last_id}: HTTP {code}" | ||||||
|  | msgstr "Error fetching tags from ID {last_id}: HTTP {code}" | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:212 | ||||||
|  | msgid "Anulowano przetwarzanie tagów." | ||||||
|  | msgstr "Tag processing cancelled." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:248 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Pobrano {count} tagów..." | ||||||
|  | msgstr "Fetched {count} tags..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:271 | ||||||
|  | msgid "Anulowano pobieranie aliasów tagów." | ||||||
|  | msgstr "Tag alias fetching cancelled." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:280 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Pobieranie aliasów tagów (od ID {last_id})..." | ||||||
|  | msgstr "Fetching tag aliases (from ID {last_id})..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:297 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Błąd przy pobieraniu aliasów tagów od ID {last_id}: HTTP {code}" | ||||||
|  | msgstr "Error fetching tag aliases from ID {last_id}: HTTP {code}" | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:312 | ||||||
|  | msgid "Anulowano przetwarzanie aliasów tagów." | ||||||
|  | msgstr "Tag alias processing cancelled." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:334 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Pobrano {count} aliasów tagów..." | ||||||
|  | msgstr "Fetched {count} tag aliases..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:375 | ||||||
|  | msgid "Anulowano pobieranie implikacji tagów." | ||||||
|  | msgstr "Tag implication fetching cancelled." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:384 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Pobieranie implikacji tagów (od ID {last_id})..." | ||||||
|  | msgstr "Fetching tag implications (from ID {last_id})..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:398 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Błąd przy pobieraniu implikacji tagów od ID {last_id}: HTTP {code}" | ||||||
|  | msgstr "Error fetching tag implications from ID {last_id}: HTTP {code}" | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:413 | ||||||
|  | msgid "Anulowano przetwarzanie implikacji tagów." | ||||||
|  | msgstr "Tag implication processing cancelled." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:460 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Pobrano implikacje dla {count} tagów..." | ||||||
|  | msgstr "Fetched implications for {count} tags..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:487 | ||||||
|  | msgid "Regeneracja bazy zakończona." | ||||||
|  | msgstr "Database regeneration complete." | ||||||
|  |  | ||||||
|  | #: tag_processing.py:19 | ||||||
|  | msgid "Błąd przy pobieraniu tagów postaci:" | ||||||
|  | msgstr "Error fetching character tags:" | ||||||
|  |  | ||||||
|  | #: tag_processing.py:34 | ||||||
|  | msgid "Błąd przy pobieraniu tagów copyright:" | ||||||
|  | msgstr "Error fetching copyright tags:" | ||||||
|  |  | ||||||
|  | #: tag_processing.py:165 | ||||||
|  | msgid "Błąd podczas odczytu tag_aliases:" | ||||||
|  | msgstr "Error reading tag aliases:" | ||||||
|  |  | ||||||
|  | #: tag_processing.py:181 | ||||||
|  | msgid "Błąd podczas odczytu tags:" | ||||||
|  | msgstr "Error reading tags:" | ||||||
							
								
								
									
										515
									
								
								kapitanbooru_uploader/locales/pl/LC_MESSAGES/messages.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										515
									
								
								kapitanbooru_uploader/locales/pl/LC_MESSAGES/messages.po
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,515 @@ | |||||||
|  | msgid "" | ||||||
|  | msgstr "" | ||||||
|  | "Project-Id-Version: Kapitanbooru Uploader 0.4.0\n" | ||||||
|  | "Report-Msgid-Bugs-To: \n" | ||||||
|  | "POT-Creation-Date: 2025-03-02 00:39+0100\n" | ||||||
|  | "Language: pl\n" | ||||||
|  | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
|  | "Content-Transfer-Encoding: 8bit\n" | ||||||
|  |  | ||||||
|  | #: autocomplete.py:133 | ||||||
|  | msgid "Błąd przy pobieraniu sugestii:" | ||||||
|  | msgstr "Błąd przy pobieraniu sugestii:" | ||||||
|  |  | ||||||
|  | #: common.py:35 | ||||||
|  | msgid "Błąd przy otwieraniu przeglądarki:" | ||||||
|  | msgstr "Błąd przy otwieraniu przeglądarki:" | ||||||
|  |  | ||||||
|  | #: common.py:72 | ||||||
|  | msgid "Login succeeded, but expected cookies were not set." | ||||||
|  | msgstr "" | ||||||
|  | "Logowanie powiodło się, ale oczekiwane ciasteczka nie zostały ustawione." | ||||||
|  |  | ||||||
|  | #: common.py:74 | ||||||
|  | msgid "Login successful. Cookies stored in session." | ||||||
|  | msgstr "Logowanie powiodło się. Ciasteczka zapisane w sesji." | ||||||
|  |  | ||||||
|  | #: common.py:79 | ||||||
|  | msgid "Login failed:" | ||||||
|  | msgstr "Logowanie nie powiodło się:" | ||||||
|  |  | ||||||
|  | #: common.py:95 | ||||||
|  | msgid "shm_user cookie not found; login might have failed." | ||||||
|  | msgstr "Nie znaleziono ciasteczka shm_user; logowanie mogło nie powieść się." | ||||||
|  |  | ||||||
|  | #: common.py:112 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Failed to load {user_url}, status code: {code}" | ||||||
|  | msgstr "Nie udało się załadować {user_url}, kod statusu: {code}" | ||||||
|  |  | ||||||
|  | #: common.py:124 | ||||||
|  | msgid "Found auth_token:" | ||||||
|  | msgstr "Znaleziono auth_token:" | ||||||
|  |  | ||||||
|  | #: common.py:127 | ||||||
|  | msgid "auth_token not found in the HTML page." | ||||||
|  | msgstr "Nie znaleziono auth_token w stronie HTML." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:30 | ||||||
|  | msgid "Processing..." | ||||||
|  | msgstr "Przetwarzanie..." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:34 | ||||||
|  | msgid "Processing, please wait..." | ||||||
|  | msgstr "Przetwarzanie, proszę czekać..." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:263 | ||||||
|  | msgid "Błąd przy otwieraniu pliku" | ||||||
|  | msgstr "Błąd przy otwieraniu pliku" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:305 | ||||||
|  | msgid "Tagger przetworzył:" | ||||||
|  | msgstr "Tagger przetworzył:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:308 | ||||||
|  | msgid "Błąd Taggera dla" | ||||||
|  | msgstr "Błąd Taggera dla" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:334 | ||||||
|  | msgid "Otwórz folder" | ||||||
|  | msgstr "Otwórz folder" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:337 ImageBrowser.py:600 ImageBrowser.py:814 | ||||||
|  | #: ImageBrowser.py:822 | ||||||
|  | msgid "Wyślij" | ||||||
|  | msgstr "Wyślij" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:340 ImageBrowser.py:807 | ||||||
|  | msgid "Wyślij wszystko" | ||||||
|  | msgstr "Wyślij wszystko" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:344 ImageBrowser.py:808 ImageBrowser.py:814 | ||||||
|  | msgid "Podmień tagi" | ||||||
|  | msgstr "Podmień tagi" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:347 ImageBrowser.py:809 | ||||||
|  | msgid "Otwórz post" | ||||||
|  | msgstr "Otwórz post" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:350 | ||||||
|  | msgid "Zakończ" | ||||||
|  | msgstr "Zakończ" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:352 | ||||||
|  | msgid "Plik" | ||||||
|  | msgstr "Plik" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:356 ImageBrowser.py:430 | ||||||
|  | msgid "Ustawienia" | ||||||
|  | msgstr "Ustawienia" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:359 | ||||||
|  | msgid "Wyczyść cache Taggera" | ||||||
|  | msgstr "Wyczyść cache Taggera" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:362 | ||||||
|  | msgid "Zregeneruj bazę tagów" | ||||||
|  | msgstr "Zregeneruj bazę tagów" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:364 | ||||||
|  | msgid "Opcje" | ||||||
|  | msgstr "Opcje" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:367 | ||||||
|  | msgid "About" | ||||||
|  | msgstr "O programie" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:368 | ||||||
|  | msgid "Help" | ||||||
|  | msgstr "Pomoc" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:373 | ||||||
|  | msgid "About Kapitanbooru Uploader" | ||||||
|  | msgstr "O programie Kapitanbooru Uploader" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:386 | ||||||
|  | msgid "A GUI application for uploading images to KapitanBooru." | ||||||
|  | msgstr "Aplikacja GUI do przesyłania obrazów do KapitanBooru." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:387 | ||||||
|  | msgid "Features include image upload, tag management, automatic" | ||||||
|  | msgstr "Funkcje obejmują przesyłanie obrazów, zarządzanie tagami, automatyczne" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:388 | ||||||
|  | msgid "tagging with wdtagger, and cache management." | ||||||
|  | msgstr "tagowanie za pomocą wdtagger oraz zarządzanie cache." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:390 | ||||||
|  | msgid "Authors:" | ||||||
|  | msgstr "Autorzy:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:393 | ||||||
|  | msgid "License: MIT License" | ||||||
|  | msgstr "Licencja: MIT License" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:396 | ||||||
|  | msgid "Repository:" | ||||||
|  | msgstr "Repozytorium:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:397 | ||||||
|  | msgid "Website:" | ||||||
|  | msgstr "Strona internetowa:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:414 | ||||||
|  | msgid "Close" | ||||||
|  | msgstr "Zamknij" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:422 ImageBrowser.py:425 | ||||||
|  | msgid "Cache" | ||||||
|  | msgstr "Cache" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:422 | ||||||
|  | msgid "Cache Taggera zostało wyczyszczone." | ||||||
|  | msgstr "Cache Taggera zostało wyczyszczone." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:425 | ||||||
|  | msgid "Błąd przy czyszczeniu cache:" | ||||||
|  | msgstr "Błąd przy czyszczeniu cache:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:434 | ||||||
|  | msgid "Login:" | ||||||
|  | msgstr "Login:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:440 | ||||||
|  | msgid "Hasło:" | ||||||
|  | msgstr "Hasło:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:446 | ||||||
|  | msgid "Base URL:" | ||||||
|  | msgstr "Base URL:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:452 | ||||||
|  | msgid "Default Tags:" | ||||||
|  | msgstr "Domyślne tagi:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:458 | ||||||
|  | msgid "Browser:" | ||||||
|  | msgstr "Przeglądarka:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:472 | ||||||
|  | msgid "Language:" | ||||||
|  | msgstr "Język:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:504 | ||||||
|  | msgid "Zapisz" | ||||||
|  | msgstr "Zapisz" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:546 | ||||||
|  | msgid "PNG Tags" | ||||||
|  | msgstr "Tagi PNG" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:558 | ||||||
|  | msgid "Tagger Tags" | ||||||
|  | msgstr "Tagi Taggera" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:572 | ||||||
|  | msgid "Manual Tags" | ||||||
|  | msgstr "Tagi ręczne" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:580 | ||||||
|  | msgid "Final Tags" | ||||||
|  | msgstr "Ostateczne tagi" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:605 | ||||||
|  | msgid "Wyświetl" | ||||||
|  | msgstr "Wyświetl" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:622 | ||||||
|  | msgid "Przetworzono tagi:" | ||||||
|  | msgstr "Przetworzono tagi:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:622 ImageBrowser.py:623 ImageBrowser.py:624 | ||||||
|  | msgid "plików" | ||||||
|  | msgstr "plików" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:623 | ||||||
|  | msgid "Zweryfikowano status uploadu:" | ||||||
|  | msgstr "Zweryfikowano status uploadu:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:624 | ||||||
|  | msgid "Zuploadowano:" | ||||||
|  | msgstr "Zuploadowano:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:652 | ||||||
|  | msgid "Wybierz folder z obrazkami PNG" | ||||||
|  | msgstr "Wybierz folder z obrazami PNG" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:683 | ||||||
|  | msgid "Informacja" | ||||||
|  | msgstr "Informacja" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:683 | ||||||
|  | msgid "Brak plików PNG w wybranym folderze." | ||||||
|  | msgstr "Brak plików PNG w wybranym folderze." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:766 | ||||||
|  | msgid "Błąd podczas sprawdzania paczki uploadu:" | ||||||
|  | msgstr "Błąd podczas sprawdzania paczki uploadu:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:842 | ||||||
|  | msgid "Błąd przy obliczaniu MD5:" | ||||||
|  | msgstr "Błąd przy obliczaniu MD5:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:898 | ||||||
|  | msgid "Błąd" | ||||||
|  | msgstr "Błąd" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:898 | ||||||
|  | msgid "Nie można załadować obrazka:" | ||||||
|  | msgstr "Nie można załadować obrazka:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1108 ImageBrowser.py:1118 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Warning: Tag '{tag}' not found in implication graph" | ||||||
|  | msgstr "Ostrzeżenie: Tag '{tag}' nie został znaleziony w grafie implikacji" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1348 | ||||||
|  | msgid "Tagger przetwarza..." | ||||||
|  | msgstr "Tagger przetwarza..." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1373 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Wysyłam plik {base_file_name}..." | ||||||
|  | msgstr "Wysyłam plik {base_file_name}..." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1414 | ||||||
|  | #, fuzzy | ||||||
|  | msgid "Wysyłanie zakończone powodzeniem!" | ||||||
|  | msgstr "Upload zakończony powodzeniem!" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1418 ImageBrowser.py:1427 | ||||||
|  | #, fuzzy, python-brace-format | ||||||
|  | msgid "" | ||||||
|  | "Wysyłanie zakończone błędem.\n" | ||||||
|  | "Status: {status_code}\n" | ||||||
|  | "Treść: {text}" | ||||||
|  | msgstr "" | ||||||
|  | "Upload zakończony błędem.\n" | ||||||
|  | "Status: {status_code}\n" | ||||||
|  | "Treść: {text}" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1433 ImageBrowser.py:1436 | ||||||
|  | msgid "Wysyłanie" | ||||||
|  | msgstr "" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1449 | ||||||
|  | #, fuzzy | ||||||
|  | msgid "Błąd wysyłania" | ||||||
|  | msgstr "Błąd edycji" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1469 | ||||||
|  | msgid "Błąd edycji" | ||||||
|  | msgstr "Błąd edycji" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1469 | ||||||
|  | msgid "Post nie został znaleziony dla tego pliku" | ||||||
|  | msgstr "Post nie został znaleziony dla tego pliku" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1479 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Aktualizuję tagi dla {base_file_name}..." | ||||||
|  | msgstr "Aktualizuję tagi dla {base_file_name}..." | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1489 ImageBrowser.py:1499 ImageBrowser.py:1530 | ||||||
|  | msgid "Operacja anulowana" | ||||||
|  | msgstr "Operacja anulowana" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1540 | ||||||
|  | msgid "Tagi zostały zaktualizowane!" | ||||||
|  | msgstr "Tagi zostały zaktualizowane!" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1542 | ||||||
|  | msgid "Sukces edycji" | ||||||
|  | msgstr "Sukces edycji" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1548 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "" | ||||||
|  | "Błąd podczas aktualizacji tagów\n" | ||||||
|  | "Status: {code}" | ||||||
|  | msgstr "" | ||||||
|  | "Błąd podczas aktualizacji tagów\n" | ||||||
|  | "Status: {code}" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1552 | ||||||
|  | msgid "Treść:" | ||||||
|  | msgstr "Treść:" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1556 | ||||||
|  | msgid "Krytyczny błąd edycji" | ||||||
|  | msgstr "Krytyczny błąd edycji" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1568 | ||||||
|  | msgid "Potwierdzenie" | ||||||
|  | msgstr "Potwierdzenie" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1570 | ||||||
|  | msgid "" | ||||||
|  | "Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\n" | ||||||
|  | "Każdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\n" | ||||||
|  | "Upewnij się, że tagi są poprawne!" | ||||||
|  | msgstr "" | ||||||
|  | "Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\n" | ||||||
|  | "Każdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\n" | ||||||
|  | "Upewnij się, że tagi są poprawne!" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1589 | ||||||
|  | msgid "Anulowano operację!" | ||||||
|  | msgstr "Operacja anulowana!" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1597 | ||||||
|  | #, fuzzy, python-brace-format | ||||||
|  | msgid "" | ||||||
|  | "Wysyłanie {file_path} z tagami: {final_tags} i ratingiem: {final_rating}" | ||||||
|  | msgstr "" | ||||||
|  | "Wysyłanie {file_path} z tagami: {final_tags} i ratingiem: {final_rating}" | ||||||
|  |  | ||||||
|  | #: ImageBrowser.py:1608 | ||||||
|  | msgid "Przesłano pliki!" | ||||||
|  | msgstr "Pliki zostały przesłane!" | ||||||
|  |  | ||||||
|  | #: ProgressFile.py:16 | ||||||
|  | msgid "Upload cancelled by user." | ||||||
|  | msgstr "Przesyłanie anulowane przez użytkownika." | ||||||
|  |  | ||||||
|  | #: settings.py:163 | ||||||
|  | msgid "Błąd podczas ładowania ustawień:" | ||||||
|  | msgstr "Błąd podczas ładowania ustawień:" | ||||||
|  |  | ||||||
|  | #: settings.py:184 | ||||||
|  | msgid "Błąd podczas zapisywania ustawień:" | ||||||
|  | msgstr "Błąd podczas zapisywania ustawień:" | ||||||
|  |  | ||||||
|  | #: tagger_cache.py:63 | ||||||
|  | msgid "Błąd przy odczycie cache dla" | ||||||
|  | msgstr "Błąd przy odczycie cache dla" | ||||||
|  |  | ||||||
|  | #: tagger_cache.py:88 | ||||||
|  | msgid "Błąd przy zapisie cache dla" | ||||||
|  | msgstr "Błąd przy zapisie cache dla" | ||||||
|  |  | ||||||
|  | #: tagger_cache.py:98 | ||||||
|  | msgid "Błąd przy usuwaniu cache dla" | ||||||
|  | msgstr "Błąd przy usuwaniu cache dla" | ||||||
|  |  | ||||||
|  | #: tagger_cache.py:111 | ||||||
|  | msgid "Błąd przy czyszczeniu przeterminowanego cache:" | ||||||
|  | msgstr "Błąd przy czyszczeniu przeterminowanego cache:" | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:59 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Database file not found: {path}, will regenerate DB" | ||||||
|  | msgstr "Nie znaleziono pliku bazy danych: {path}, baza zostanie zregenerowana" | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:143 | ||||||
|  | msgid "Błąd przy inicjalizacji bazy tagów:" | ||||||
|  | msgstr "Błąd przy inicjalizacji bazy tagów:" | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:159 | ||||||
|  | msgid "Czyszczenie bazy danych..." | ||||||
|  | msgstr "Czyszczenie bazy danych..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:175 | ||||||
|  | msgid "Anulowano pobieranie tagów." | ||||||
|  | msgstr "Pobieranie tagów anulowane." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:183 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Pobieranie tagów (od ID {last_id})..." | ||||||
|  | msgstr "Pobieranie tagów (od ID {last_id})..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:197 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Błąd przy pobieraniu tagów od ID {last_id}: HTTP {code}" | ||||||
|  | msgstr "Błąd przy pobieraniu tagów od ID {last_id}: HTTP {code}" | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:212 | ||||||
|  | msgid "Anulowano przetwarzanie tagów." | ||||||
|  | msgstr "Przetwarzanie tagów anulowane." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:248 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Pobrano {count} tagów..." | ||||||
|  | msgstr "Pobrano {count} tagów..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:271 | ||||||
|  | msgid "Anulowano pobieranie aliasów tagów." | ||||||
|  | msgstr "Pobieranie aliasów tagów anulowane." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:280 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Pobieranie aliasów tagów (od ID {last_id})..." | ||||||
|  | msgstr "Pobieranie aliasów tagów (od ID {last_id})..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:297 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Błąd przy pobieraniu aliasów tagów od ID {last_id}: HTTP {code}" | ||||||
|  | msgstr "Błąd przy pobieraniu aliasów tagów od ID {last_id}: HTTP {code}" | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:312 | ||||||
|  | msgid "Anulowano przetwarzanie aliasów tagów." | ||||||
|  | msgstr "Przetwarzanie aliasów tagów anulowane." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:334 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Pobrano {count} aliasów tagów..." | ||||||
|  | msgstr "Pobrano {count} aliasów tagów..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:375 | ||||||
|  | msgid "Anulowano pobieranie implikacji tagów." | ||||||
|  | msgstr "Pobieranie implikacji tagów anulowane." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:384 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Pobieranie implikacji tagów (od ID {last_id})..." | ||||||
|  | msgstr "Pobieranie implikacji tagów (od ID {last_id})..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:398 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Błąd przy pobieraniu implikacji tagów od ID {last_id}: HTTP {code}" | ||||||
|  | msgstr "Błąd przy pobieraniu implikacji tagów od ID {last_id}: HTTP {code}" | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:413 | ||||||
|  | msgid "Anulowano przetwarzanie implikacji tagów." | ||||||
|  | msgstr "Przetwarzanie implikacji tagów anulowane." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:460 | ||||||
|  | #, python-brace-format | ||||||
|  | msgid "Pobrano implikacje dla {count} tagów..." | ||||||
|  | msgstr "Pobrano implikacje dla {count} tagów..." | ||||||
|  |  | ||||||
|  | #: TagsRepo.py:487 | ||||||
|  | msgid "Regeneracja bazy zakończona." | ||||||
|  | msgstr "Regeneracja bazy zakończona." | ||||||
|  |  | ||||||
|  | #: tag_processing.py:19 | ||||||
|  | msgid "Błąd przy pobieraniu tagów postaci:" | ||||||
|  | msgstr "Błąd przy pobieraniu tagów postaci:" | ||||||
|  |  | ||||||
|  | #: tag_processing.py:34 | ||||||
|  | msgid "Błąd przy pobieraniu tagów copyright:" | ||||||
|  | msgstr "Błąd przy pobieraniu tagów copyright:" | ||||||
|  |  | ||||||
|  | #: tag_processing.py:165 | ||||||
|  | msgid "Błąd podczas odczytu tag_aliases:" | ||||||
|  | msgstr "Błąd podczas odczytu tag_aliases:" | ||||||
|  |  | ||||||
|  | #: tag_processing.py:181 | ||||||
|  | msgid "Błąd podczas odczytu tags:" | ||||||
|  | msgstr "Błąd podczas odczytu tags:" | ||||||
|  |  | ||||||
|  | #, python-brace-format | ||||||
|  | #~ msgid "" | ||||||
|  | #~ "Upload zakończony błędem.\n" | ||||||
|  | #~ "Status: 409\n" | ||||||
|  | #~ "Treść: {error}" | ||||||
|  | #~ msgstr "" | ||||||
|  | #~ "Upload zakończony błędem.\n" | ||||||
|  | #~ "Status: 409\n" | ||||||
|  | #~ "Treść: {error}" | ||||||
|  |  | ||||||
|  | #~ msgid "Upload" | ||||||
|  | #~ msgstr "Upload" | ||||||
|  |  | ||||||
|  | #~ msgid "Błąd uploadu" | ||||||
|  | #~ msgstr "Błąd uploadu" | ||||||
| @@ -1,11 +1,11 @@ | |||||||
| import base64 | import base64 | ||||||
| import importlib |  | ||||||
| import json | import json | ||||||
| import os | import os | ||||||
| import sqlite3 |  | ||||||
| import subprocess | import subprocess | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
|  | from .I18N import _ | ||||||
|  |  | ||||||
| # Na Windowsie używamy DPAPI | # Na Windowsie używamy DPAPI | ||||||
| if sys.platform.startswith("win"): | if sys.platform.startswith("win"): | ||||||
|     try: |     try: | ||||||
| @@ -49,6 +49,7 @@ def get_browser_paths_windows(): | |||||||
|  |  | ||||||
|     return browsers |     return browsers | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_browsers_linux(): | def get_browsers_linux(): | ||||||
|     """Detects installed browsers on Linux by checking available executables.""" |     """Detects installed browsers on Linux by checking available executables.""" | ||||||
|     browsers = {"Default": None} |     browsers = {"Default": None} | ||||||
| @@ -114,6 +115,7 @@ class Settings: | |||||||
|         self.default_tags = "artist:kapitan meta:ai-generated" |         self.default_tags = "artist:kapitan meta:ai-generated" | ||||||
|         self.cache_expiry = 604800  # 7 dni w sekundach |         self.cache_expiry = 604800  # 7 dni w sekundach | ||||||
|         self.browser = "" |         self.browser = "" | ||||||
|  |         self.i18n = _ | ||||||
|         self.installed_browsers = detect_installed_browsers() |         self.installed_browsers = detect_installed_browsers() | ||||||
|         self.load_settings() |         self.load_settings() | ||||||
|         self.installed_browsers_reverse = { |         self.installed_browsers_reverse = { | ||||||
| @@ -139,6 +141,7 @@ class Settings: | |||||||
|         self.default_tags = "artist:kapitan meta:ai-generated" |         self.default_tags = "artist:kapitan meta:ai-generated" | ||||||
|         self.cache_expiry = 604800  # 7 dni w sekundach |         self.cache_expiry = 604800  # 7 dni w sekundach | ||||||
|         self.browser = "" |         self.browser = "" | ||||||
|  |         self.i18n.set_language("en") | ||||||
|         try: |         try: | ||||||
|             if os.path.exists(self.get_settings_path()): |             if os.path.exists(self.get_settings_path()): | ||||||
|                 with open(self.get_settings_path(), "r", encoding="utf-8") as f: |                 with open(self.get_settings_path(), "r", encoding="utf-8") as f: | ||||||
| @@ -155,8 +158,9 @@ class Settings: | |||||||
|                 self.browser = data.get("browser", self.browser) |                 self.browser = data.get("browser", self.browser) | ||||||
|                 if self.browser not in self.installed_browsers: |                 if self.browser not in self.installed_browsers: | ||||||
|                     self.browser = "" |                     self.browser = "" | ||||||
|  |                 self.i18n.set_language(data.get("language", "en")) | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print("Błąd podczas ładowania ustawień:", e) |             print(_("Błąd podczas ładowania ustawień:"), e) | ||||||
|  |  | ||||||
|     def save_settings(self): |     def save_settings(self): | ||||||
|         """Zapisuje ustawienia do pliku.""" |         """Zapisuje ustawienia do pliku.""" | ||||||
| @@ -166,6 +170,7 @@ class Settings: | |||||||
|             "default_tags": self.default_tags, |             "default_tags": self.default_tags, | ||||||
|             "cache_expiry": self.cache_expiry, |             "cache_expiry": self.cache_expiry, | ||||||
|             "browser": self.browser, |             "browser": self.browser, | ||||||
|  |             "language": self.i18n.current_lang, | ||||||
|         } |         } | ||||||
|         # Na Windowsie szyfrujemy hasło |         # Na Windowsie szyfrujemy hasło | ||||||
|         if sys.platform.startswith("win"): |         if sys.platform.startswith("win"): | ||||||
| @@ -176,4 +181,4 @@ class Settings: | |||||||
|             with open(self.get_settings_path(), "w", encoding="utf-8") as f: |             with open(self.get_settings_path(), "w", encoding="utf-8") as f: | ||||||
|                 json.dump(data, f, indent=4) |                 json.dump(data, f, indent=4) | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print("Błąd podczas zapisywania ustawień:", e) |             print(_("Błąd podczas zapisywania ustawień:"), e) | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| from functools import lru_cache | from functools import lru_cache | ||||||
| import re | import re | ||||||
|  |  | ||||||
|  | from .I18N import _ | ||||||
| from .TagsRepo import TagsRepo | from .TagsRepo import TagsRepo | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -15,7 +16,7 @@ def get_character_tags(tags_repo: TagsRepo): | |||||||
|         conn.close() |         conn.close() | ||||||
|         return {row[0] for row in rows} |         return {row[0] for row in rows} | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
|         print("Błąd przy pobieraniu tagów postaci:", e) |         print(_("Błąd przy pobieraniu tagów postaci:"), e) | ||||||
|         return set() |         return set() | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -30,7 +31,7 @@ def get_copyright_tags(tags_repo: TagsRepo): | |||||||
|         conn.close() |         conn.close() | ||||||
|         return {row[0] for row in rows} |         return {row[0] for row in rows} | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
|         print("Błąd przy pobieraniu tagów copyright:", e) |         print(_("Błąd przy pobieraniu tagów copyright:"), e) | ||||||
|         return set() |         return set() | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -161,7 +162,7 @@ def process_tag(tag, tags_repo: TagsRepo): | |||||||
|             tag_lookup = row[0] |             tag_lookup = row[0] | ||||||
|         conn.close() |         conn.close() | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
|         print("Błąd podczas odczytu tag_aliases:", e) |         print(_("Błąd podczas odczytu tag_aliases:"), e) | ||||||
|  |  | ||||||
|     # Sprawdź w tabeli tags – kolumna name nie zawiera prefiksów |     # Sprawdź w tabeli tags – kolumna name nie zawiera prefiksów | ||||||
|     try: |     try: | ||||||
| @@ -177,5 +178,5 @@ def process_tag(tag, tags_repo: TagsRepo): | |||||||
|             # Tag nie istnieje |             # Tag nie istnieje | ||||||
|             return tag_lookup, None |             return tag_lookup, None | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
|         print("Błąd podczas odczytu tags:", e) |         print(_("Błąd podczas odczytu tags:"), e) | ||||||
|         return tag_lookup, None |         return tag_lookup, None | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ import os | |||||||
| import pickle | import pickle | ||||||
| import sqlite3 | import sqlite3 | ||||||
| import time | import time | ||||||
|  | from .I18N import _ | ||||||
| from .settings import Settings | from .settings import Settings | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -59,7 +60,7 @@ class TaggerCache: | |||||||
|                 else: |                 else: | ||||||
|                     self.delete_cache_entry(file_md5) |                     self.delete_cache_entry(file_md5) | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print("Błąd przy odczycie cache dla", file_md5, ":", e) |             print(_("Błąd przy odczycie cache dla"), file_md5, ":", e) | ||||||
|         return None |         return None | ||||||
|  |  | ||||||
|     def __setitem__(self, file_md5, result): |     def __setitem__(self, file_md5, result): | ||||||
| @@ -84,7 +85,7 @@ class TaggerCache: | |||||||
|             conn.commit() |             conn.commit() | ||||||
|             conn.close() |             conn.close() | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print("Błąd przy zapisie cache dla", file_md5, ":", e) |             print(_("Błąd przy zapisie cache dla"), file_md5, ":", e) | ||||||
|  |  | ||||||
|     def delete_cache_entry(self, file_md5): |     def delete_cache_entry(self, file_md5): | ||||||
|         try: |         try: | ||||||
| @@ -94,7 +95,7 @@ class TaggerCache: | |||||||
|             conn.commit() |             conn.commit() | ||||||
|             conn.close() |             conn.close() | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print("Błąd przy usuwaniu cache dla", file_md5, ":", e) |             print(_("Błąd przy usuwaniu cache dla"), file_md5, ":", e) | ||||||
|  |  | ||||||
|     def clear_expired_cache(self): |     def clear_expired_cache(self): | ||||||
|         try: |         try: | ||||||
| @@ -107,7 +108,7 @@ class TaggerCache: | |||||||
|             conn.commit() |             conn.commit() | ||||||
|             conn.close() |             conn.close() | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             print("Błąd przy czyszczeniu przeterminowanego cache:", e) |             print(_("Błąd przy czyszczeniu przeterminowanego cache:"), e) | ||||||
|  |  | ||||||
|     def clear_cache(self): |     def clear_cache(self): | ||||||
|         try: |         try: | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" | |||||||
|  |  | ||||||
| [project] | [project] | ||||||
| name = "kapitanbooru-uploader" | name = "kapitanbooru-uploader" | ||||||
| version = "0.3.0" | version = "0.4.0" | ||||||
| description = "A GUI application for uploading images to KapitanBooru" | description = "A GUI application for uploading images to KapitanBooru" | ||||||
| authors = [ | authors = [ | ||||||
|     {name = "Michał Leśniak", email = "kapitan@mlesniak.pl"} |     {name = "Michał Leśniak", email = "kapitan@mlesniak.pl"} | ||||||
| @@ -25,4 +25,8 @@ license = {file = "LICENSE"} | |||||||
| kapitanbooru-uploader = "kapitanbooru_uploader.__main__:main" | kapitanbooru-uploader = "kapitanbooru_uploader.__main__:main" | ||||||
|  |  | ||||||
| [tool.setuptools] | [tool.setuptools] | ||||||
| packages = ["kapitanbooru_uploader"] | packages = ["kapitanbooru_uploader"] | ||||||
|  | include-package-data = true | ||||||
|  |  | ||||||
|  | [tool.setuptools.package-data] | ||||||
|  | "kapitanbooru_uploader" = ["locales/*/LC_MESSAGES/*.mo"] | ||||||
							
								
								
									
										19
									
								
								update_translations.bat
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								update_translations.bat
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | @echo off | ||||||
|  | setlocal enabledelayedexpansion | ||||||
|  |  | ||||||
|  | :: Create POT file | ||||||
|  | del locales\messages.pot 2>nul | ||||||
|  | for %%f in (*.py) do ( | ||||||
|  |     if not exist locales\messages.pot ( | ||||||
|  |         xgettext -d messages -o locales\messages.pot "%%f" | ||||||
|  |     ) else ( | ||||||
|  |         xgettext -d messages -o locales\messages.pot --join-existing "%%f" | ||||||
|  |     ) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | :: Update PO files | ||||||
|  | for /D %%d in (locales\*) do ( | ||||||
|  |     if exist "%%d\LC_MESSAGES\messages.po" ( | ||||||
|  |         msgmerge --update "%%d\LC_MESSAGES\messages.po" locales\messages.pot | ||||||
|  |     ) | ||||||
|  | ) | ||||||
		Reference in New Issue
	
	Block a user