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:
parent
9f187afc22
commit
9361bc0363
1
.gitignore
vendored
1
.gitignore
vendored
@ -54,6 +54,7 @@ cover/
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
*.po~
|
||||
|
||||
# Django stuff:
|
||||
*.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 {
|
||||
agent { label 'Pi4' } // Use Raspberry Pi 4 agent
|
||||
agent { label 'Pi4' } // Use Raspberry Pi 4 agent running on Ubuntu Server LTS 24.04
|
||||
environment {
|
||||
PIP_EXTRA_INDEX_URL = 'http://localhost:8090/simple/' // Local PyPI repo
|
||||
PACKAGE_NAME = 'kapitanbooru_uploader' // Your package name
|
||||
@ -17,6 +17,18 @@ pipeline {
|
||||
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') {
|
||||
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 requests
|
||||
from PIL import Image, ImageTk, PngImagePlugin
|
||||
|
||||
import wdtagger as wdt
|
||||
|
||||
from .I18N import _
|
||||
from .ProgressFile import ProgressFile
|
||||
from .TagsRepo import TagsRepo
|
||||
from .autocomplete import TagManager
|
||||
@ -26,11 +27,11 @@ class ProcessingDialog:
|
||||
def __init__(self, root, target_function, *args):
|
||||
self.root = root
|
||||
self.top = tk.Toplevel(root)
|
||||
self.top.title("Processing...")
|
||||
self.top.title(_("Processing..."))
|
||||
self.top.geometry("300x150")
|
||||
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)
|
||||
|
||||
# Start with indeterminate progress bar
|
||||
@ -38,8 +39,15 @@ class ProcessingDialog:
|
||||
self.progress.pack(pady=10, fill="x")
|
||||
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
|
||||
self.queue = queue.Queue()
|
||||
self.sub_queue = queue.Queue()
|
||||
self.running = True
|
||||
self.cancel_event = threading.Event() # Cancellation flag
|
||||
self.thread = threading.Thread(
|
||||
@ -58,6 +66,10 @@ class ProcessingDialog:
|
||||
self.progress.config(mode=msg[1])
|
||||
if msg[1] == "determinate":
|
||||
self.progress["value"] = 0
|
||||
self.progress.stop()
|
||||
elif msg[1] == "indeterminate":
|
||||
self.progress["value"] = 0
|
||||
self.progress.start()
|
||||
elif msg[0] == "max":
|
||||
self.progress["maximum"] = msg[1]
|
||||
elif msg[0] == "progress":
|
||||
@ -69,6 +81,26 @@ class ProcessingDialog:
|
||||
except queue.Empty:
|
||||
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:
|
||||
self.top.after(100, self.process_queue)
|
||||
|
||||
@ -79,6 +111,8 @@ class ProcessingDialog:
|
||||
kwargs = {}
|
||||
if "progress_queue" in sig.parameters:
|
||||
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:
|
||||
kwargs["cancel_event"] = self.cancel_event
|
||||
target_function(*args, **kwargs)
|
||||
@ -91,7 +125,7 @@ class ProcessingDialog:
|
||||
self.running = False
|
||||
self.progress.stop()
|
||||
self.top.after(0, self.top.destroy)
|
||||
self.thread.join()
|
||||
self.root.after(100, self.thread.join)
|
||||
|
||||
def on_close(self):
|
||||
"""Handle manual window closure"""
|
||||
@ -105,6 +139,7 @@ class ImageBrowser(tk.Tk):
|
||||
super().__init__()
|
||||
self.title("Kapitanbooru Uploader")
|
||||
self.geometry("900x600")
|
||||
self.version = "0.4.0"
|
||||
|
||||
self.settings = Settings()
|
||||
self.tags_repo = TagsRepo(self.settings)
|
||||
@ -169,6 +204,15 @@ class ImageBrowser(tk.Tk):
|
||||
self.create_widgets()
|
||||
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:
|
||||
G = nx.DiGraph()
|
||||
conn = self.tags_repo.get_conn()
|
||||
@ -249,7 +293,7 @@ class ImageBrowser(tk.Tk):
|
||||
png_tags = set(parse_parameters(parameters, self.tags_repo).split())
|
||||
img.close()
|
||||
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()
|
||||
|
||||
# Pobierz tagi z Taggera – sprawdzając cache
|
||||
@ -291,10 +335,10 @@ class ImageBrowser(tk.Tk):
|
||||
self.tagger_cache[md5] = result
|
||||
self.tagger_processed.add(md5)
|
||||
self.after(0, self.update_status_bar)
|
||||
print(f"Tagger przetworzył: {file_path}")
|
||||
print(_("Tagger przetworzył:"), f"{file_path}")
|
||||
return result
|
||||
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:
|
||||
"""
|
||||
@ -320,37 +364,98 @@ class ImageBrowser(tk.Tk):
|
||||
self.file_menu = tk.Menu(menubar, tearoff=0)
|
||||
|
||||
# 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_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(
|
||||
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_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(
|
||||
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_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 = 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_command(
|
||||
label="Wyczyść cache Taggera", command=self.clear_cache
|
||||
label=_("Wyczyść cache Taggera"), command=self.clear_cache
|
||||
)
|
||||
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):
|
||||
self.processing_dialog = ProcessingDialog(self, self.tags_repo.regenerate_db)
|
||||
@ -358,41 +463,44 @@ class ImageBrowser(tk.Tk):
|
||||
def clear_cache(self):
|
||||
res, err = self.tagger_cache.clear_cache()
|
||||
if res:
|
||||
messagebox.showinfo("Cache", "Cache Taggera zostało wyczyszczone.")
|
||||
messagebox.showinfo(_("Cache"), _("Cache Taggera zostało wyczyszczone."))
|
||||
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):
|
||||
settings_window = tk.Toplevel(self)
|
||||
settings_window.title("Ustawienia")
|
||||
settings_window.geometry("300x350")
|
||||
settings_window.title(_("Ustawienia"))
|
||||
settings_window.geometry("300x430") # Enlarged vertically
|
||||
settings_window.resizable(False, False) # Disable resizing
|
||||
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))
|
||||
entry_login = tk.Entry(settings_window)
|
||||
entry_login.pack(pady=(0, 10), padx=10, fill="x")
|
||||
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))
|
||||
entry_password = tk.Entry(settings_window, show="*")
|
||||
entry_password.pack(pady=(0, 10), padx=10, fill="x")
|
||||
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))
|
||||
entry_base_url = tk.Entry(settings_window)
|
||||
entry_base_url.pack(pady=(0, 10), padx=10, fill="x")
|
||||
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))
|
||||
entry_default_tags = tk.Entry(settings_window)
|
||||
entry_default_tags.pack(pady=(0, 10), padx=10, fill="x")
|
||||
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))
|
||||
cb_browser = ttk.Combobox(
|
||||
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():
|
||||
self.settings.username = entry_login.get()
|
||||
self.settings.password = entry_password.get()
|
||||
self.settings.base_url = entry_base_url.get()
|
||||
self.settings.default_tags = entry_default_tags.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()
|
||||
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)
|
||||
|
||||
def create_widgets(self):
|
||||
@ -457,7 +588,7 @@ class ImageBrowser(tk.Tk):
|
||||
right_frame.grid_rowconfigure(4, weight=0) # Upload Panel
|
||||
|
||||
# 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_columnconfigure(0, weight=1)
|
||||
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_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_columnconfigure(0, weight=1)
|
||||
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_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)
|
||||
self.manual_tags_manager = TagManager(
|
||||
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)
|
||||
|
||||
# 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_rowconfigure(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.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.config(state=tk.DISABLED)
|
||||
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.config(state=tk.DISABLED)
|
||||
@ -533,9 +664,9 @@ class ImageBrowser(tk.Tk):
|
||||
|
||||
def update_status_bar(self):
|
||||
status_text = (
|
||||
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"Zuploadowano: {self.uploaded_count}/{self.upload_verified} 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"{_('Zuploadowano:')} {self.uploaded_count}/{self.upload_verified} {_('plików')}"
|
||||
)
|
||||
self.status_label.config(text=status_text)
|
||||
|
||||
@ -563,7 +694,7 @@ class ImageBrowser(tk.Tk):
|
||||
Otwiera okno dialogowe wyboru folderu z obrazkami
|
||||
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:
|
||||
self.folder_path = folder
|
||||
self.load_images()
|
||||
@ -593,7 +724,9 @@ class ImageBrowser(tk.Tk):
|
||||
self.show_image(0)
|
||||
self.post_load_processing()
|
||||
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):
|
||||
"""
|
||||
@ -675,7 +808,7 @@ class ImageBrowser(tk.Tk):
|
||||
self.uploaded[file_path] = False
|
||||
self.after(0, self.update_status_bar)
|
||||
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)
|
||||
|
||||
def update_button_states(self):
|
||||
@ -716,14 +849,14 @@ class ImageBrowser(tk.Tk):
|
||||
post_ops_state = tk.NORMAL if post_id else tk.DISABLED
|
||||
|
||||
# Update menu items
|
||||
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("Otwórz post", state=post_ops_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(_("Otwórz post"), state=post_ops_state)
|
||||
|
||||
# Update buttons
|
||||
self.upload_button.config(
|
||||
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,
|
||||
)
|
||||
self.view_post_button.config(state=post_ops_state)
|
||||
@ -731,7 +864,7 @@ class ImageBrowser(tk.Tk):
|
||||
# Special case for "Wyślij" menu item
|
||||
wyślij_state = tk.DISABLED if post_id else tk.NORMAL
|
||||
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):
|
||||
@ -751,7 +884,7 @@ class ImageBrowser(tk.Tk):
|
||||
for chunk in iter(lambda: f.read(chunk_size), b""):
|
||||
hash_md5.update(chunk)
|
||||
except Exception as e:
|
||||
print("Błąd przy obliczaniu MD5:", e)
|
||||
print(_("Błąd przy obliczaniu MD5:"), e)
|
||||
return ""
|
||||
return hash_md5.hexdigest()
|
||||
|
||||
@ -807,7 +940,7 @@ class ImageBrowser(tk.Tk):
|
||||
self.tagger_thread_idx += 1
|
||||
thread.start()
|
||||
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):
|
||||
"""
|
||||
@ -1016,13 +1149,21 @@ class ImageBrowser(tk.Tk):
|
||||
if tag in self.implication_graph:
|
||||
implied_by_selected.update(nx.descendants(self.implication_graph, tag))
|
||||
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
|
||||
for tag in selected_png_tags:
|
||||
if tag in self.implication_graph:
|
||||
implied_by_selected.update(nx.descendants(self.implication_graph, tag))
|
||||
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
|
||||
|
||||
# Build visible list
|
||||
@ -1249,7 +1390,7 @@ class ImageBrowser(tk.Tk):
|
||||
# Ustaw komunikat, że Tagger pracuje
|
||||
self.tagger_tags_text.config(state=tk.NORMAL)
|
||||
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)
|
||||
file_path = self.image_files[self.current_index]
|
||||
result = self.get_tagger_results(file_path)
|
||||
@ -1264,14 +1405,21 @@ class ImageBrowser(tk.Tk):
|
||||
file_path,
|
||||
final_tags=None,
|
||||
final_rating=None,
|
||||
progress_queue=None,
|
||||
cancel_event=None,
|
||||
progress_queue: Optional[queue.Queue] = None,
|
||||
cancel_event: Optional[threading.Event] = None,
|
||||
):
|
||||
base_file_name = os.path.basename(file_path)
|
||||
if progress_queue:
|
||||
progress_queue.put(("mode", "determinate"))
|
||||
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"
|
||||
tags = (
|
||||
self.final_tags_text.get("1.0", tk.END).strip()
|
||||
@ -1308,22 +1456,29 @@ class ImageBrowser(tk.Tk):
|
||||
show_warn = False
|
||||
post_url = None
|
||||
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)
|
||||
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)
|
||||
show_warn = True
|
||||
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
|
||||
# Aktualizacja wyglądu listy – musimy użyć domyślnych argumentów w lambdzie, aby zachować bieżący indeks
|
||||
if show_warn:
|
||||
if not final_tags:
|
||||
messagebox.showwarning("Upload", message)
|
||||
messagebox.showwarning(_("Wysyłanie"), message)
|
||||
else:
|
||||
if not final_tags:
|
||||
messagebox.showinfo("Upload", message)
|
||||
messagebox.showinfo(_("Wysyłanie"), message)
|
||||
self.after(
|
||||
0,
|
||||
lambda idx=self.image_files.index(
|
||||
@ -1336,7 +1491,7 @@ class ImageBrowser(tk.Tk):
|
||||
self.uploaded_count += 1
|
||||
self.after(0, self.update_status_bar)
|
||||
except Exception as e:
|
||||
messagebox.showerror("Błąd uploadu", str(e))
|
||||
messagebox.showerror(_("Błąd wysyłania"), str(e))
|
||||
finally:
|
||||
self.upload_button.after(0, self.update_button_states)
|
||||
|
||||
@ -1356,20 +1511,27 @@ class ImageBrowser(tk.Tk):
|
||||
|
||||
if not post_id:
|
||||
messagebox.showerror(
|
||||
"Błąd edycji", "Post nie został znaleziony dla tego pliku"
|
||||
_("Błąd edycji"), _("Post nie został znaleziony dla tego pliku")
|
||||
)
|
||||
return
|
||||
|
||||
if progress_queue:
|
||||
progress_queue.put(("mode", "determinate"))
|
||||
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:
|
||||
# Check for cancellation before starting the operation.
|
||||
if cancel_event is not None and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(("label", "Operacja anulowana"))
|
||||
progress_queue.put(("label", _("Operacja anulowana")))
|
||||
return
|
||||
|
||||
# Get authentication session and token
|
||||
@ -1379,7 +1541,7 @@ class ImageBrowser(tk.Tk):
|
||||
# Check cancellation after login if needed.
|
||||
if cancel_event is not None and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(("label", "Operacja anulowana"))
|
||||
progress_queue.put(("label", _("Operacja anulowana")))
|
||||
return
|
||||
|
||||
# Prepare tags and rating
|
||||
@ -1410,7 +1572,7 @@ class ImageBrowser(tk.Tk):
|
||||
# Check for cancellation before sending the update request.
|
||||
if cancel_event is not None and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(("label", "Operacja anulowana"))
|
||||
progress_queue.put(("label", _("Operacja anulowana")))
|
||||
return
|
||||
|
||||
# Send update request
|
||||
@ -1420,23 +1582,23 @@ class ImageBrowser(tk.Tk):
|
||||
if response.status_code == 302:
|
||||
if progress_queue:
|
||||
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
|
||||
messagebox.showinfo("Sukces edycji", message)
|
||||
messagebox.showinfo(_("Sukces edycji"), message)
|
||||
# Update UI state
|
||||
self.after(0, self.update_button_states)
|
||||
return
|
||||
|
||||
# Handle other status codes
|
||||
error_msg = (
|
||||
f"Błąd podczas aktualizacji tagów\nStatus: {response.status_code}"
|
||||
error_msg = _("Błąd podczas aktualizacji tagów\nStatus: {code}").format(
|
||||
code=response.status_code
|
||||
)
|
||||
if response.text:
|
||||
error_msg += f"\nTreść: {response.text}"
|
||||
error_msg += f"\n{_('Treść:')} {response.text}"
|
||||
messagebox.showerror("Błąd edycji", error_msg)
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("Krytyczny błąd edycji", str(e))
|
||||
messagebox.showerror(_("Krytyczny błąd edycji"), str(e))
|
||||
finally:
|
||||
if progress_queue:
|
||||
progress_queue.put(("progress", 100))
|
||||
@ -1448,12 +1610,18 @@ class ImageBrowser(tk.Tk):
|
||||
i wywołuje upload_file.
|
||||
"""
|
||||
if not messagebox.askyesno(
|
||||
"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!",
|
||||
_("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!"
|
||||
),
|
||||
):
|
||||
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)
|
||||
if progress_queue:
|
||||
progress_queue.put(("mode", "determinate"))
|
||||
@ -1461,26 +1629,37 @@ class ImageBrowser(tk.Tk):
|
||||
file_idx = 0
|
||||
for file_path in self.image_files:
|
||||
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(
|
||||
("label", f"Wysyłam plik {file_idx+1}/{files_count}...")
|
||||
)
|
||||
if cancel_event is not None and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(("label", f"Anulowano operację!"))
|
||||
progress_queue.put(("label", _("Anulowano operację!")))
|
||||
return
|
||||
if not self.uploaded.get(file_path, False):
|
||||
final_tags, final_rating = (
|
||||
self.compute_final_tags_and_rating_for_file(file_path)
|
||||
)
|
||||
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(
|
||||
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:
|
||||
progress_queue.put(("label", f"Przesłano pliki!"))
|
||||
progress_queue.put(("label", _("Przesłano pliki!")))
|
||||
progress_queue.put(("progress", 100))
|
||||
|
||||
self.processing_dialog = ProcessingDialog(self, worker)
|
||||
|
@ -1,3 +1,6 @@
|
||||
from .I18N import _
|
||||
|
||||
|
||||
# Klasa pomocnicza do monitorowania postępu uploadu
|
||||
class ProgressFile:
|
||||
def __init__(self, f, callback, total_size, cancel_event=None):
|
||||
@ -10,7 +13,7 @@ class ProgressFile:
|
||||
def read(self, size=-1):
|
||||
# Check for cancellation before reading more data
|
||||
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)
|
||||
self.read_bytes += len(data)
|
||||
|
@ -1,11 +1,15 @@
|
||||
from collections import deque
|
||||
import json
|
||||
import os
|
||||
import queue
|
||||
import sqlite3
|
||||
import time
|
||||
import requests
|
||||
from pathlib import Path
|
||||
import threading
|
||||
import time
|
||||
|
||||
import requests
|
||||
|
||||
from .I18N import _
|
||||
from .settings import Settings
|
||||
|
||||
# Stałe – auth_token (CSRF token) oraz ciasteczka
|
||||
@ -51,7 +55,11 @@ class TagsRepo:
|
||||
regenerate = False
|
||||
if not Path(self.db_path).is_file():
|
||||
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()
|
||||
if regenerate:
|
||||
self.regenerate_db()
|
||||
@ -132,9 +140,11 @@ class TagsRepo:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
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.
|
||||
|
||||
@ -146,7 +156,7 @@ class TagsRepo:
|
||||
cursor = conn.cursor()
|
||||
|
||||
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 tag_aliases")
|
||||
conn.commit()
|
||||
@ -162,12 +172,19 @@ class TagsRepo:
|
||||
while True:
|
||||
if cancel_event and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(("label", "Anulowano pobieranie tagów."))
|
||||
progress_queue.put(("label", _("Anulowano pobieranie tagów.")))
|
||||
conn.close()
|
||||
return
|
||||
|
||||
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()
|
||||
url = f"https://danbooru.donmai.us/tags.json?limit=1000&page=a{last_id}"
|
||||
response = requests.get(url)
|
||||
@ -176,7 +193,9 @@ class TagsRepo:
|
||||
progress_queue.put(
|
||||
(
|
||||
"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
|
||||
@ -189,7 +208,9 @@ class TagsRepo:
|
||||
for item in data:
|
||||
if cancel_event and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(("label", "Anulowano przetwarzanie tagów."))
|
||||
progress_queue.put(
|
||||
("label", _("Anulowano przetwarzanie tagów."))
|
||||
)
|
||||
conn.close()
|
||||
return
|
||||
tag_id = item.get("id")
|
||||
@ -223,7 +244,9 @@ class TagsRepo:
|
||||
time.sleep(min_interval - elapsed)
|
||||
|
||||
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 = [(idx,) + row for idx, row in enumerate(data_list)]
|
||||
@ -244,13 +267,20 @@ class TagsRepo:
|
||||
while True:
|
||||
if cancel_event and cancel_event.is_set():
|
||||
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()
|
||||
return
|
||||
|
||||
if progress_queue:
|
||||
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()
|
||||
url = (
|
||||
@ -263,7 +293,9 @@ class TagsRepo:
|
||||
progress_queue.put(
|
||||
(
|
||||
"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
|
||||
@ -277,7 +309,7 @@ class TagsRepo:
|
||||
if cancel_event and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(
|
||||
("label", "Anulowano przetwarzanie aliasów tagów.")
|
||||
("label", _("Anulowano przetwarzanie aliasów tagów."))
|
||||
)
|
||||
conn.close()
|
||||
return
|
||||
@ -296,7 +328,12 @@ class TagsRepo:
|
||||
time.sleep(min_interval - elapsed)
|
||||
|
||||
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 = [(idx,) + row for idx, row in enumerate(data_list)]
|
||||
@ -335,14 +372,19 @@ class TagsRepo:
|
||||
if cancel_event and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(
|
||||
("label", "Anulowano pobieranie implikacji tagów.")
|
||||
("label", _("Anulowano pobieranie implikacji tagów."))
|
||||
)
|
||||
conn.close()
|
||||
return
|
||||
|
||||
if progress_queue:
|
||||
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()
|
||||
url = f"https://danbooru.donmai.us/tag_implications.json?limit=1000&page=a{last_id}"
|
||||
@ -352,7 +394,9 @@ class TagsRepo:
|
||||
progress_queue.put(
|
||||
(
|
||||
"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
|
||||
@ -366,7 +410,7 @@ class TagsRepo:
|
||||
if cancel_event and cancel_event.is_set():
|
||||
if progress_queue:
|
||||
progress_queue.put(
|
||||
("label", "Anulowano przetwarzanie implikacji tagów.")
|
||||
("label", _("Anulowano przetwarzanie implikacji tagów."))
|
||||
)
|
||||
conn.close()
|
||||
return
|
||||
@ -411,7 +455,12 @@ class TagsRepo:
|
||||
|
||||
if progress_queue:
|
||||
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()
|
||||
|
||||
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):
|
||||
closure = set()
|
||||
|
@ -1,6 +1,7 @@
|
||||
import tkinter as tk
|
||||
from tkinter import font
|
||||
|
||||
from .I18N import _
|
||||
from .TagsRepo import TagsRepo
|
||||
from .common import open_tag_wiki_url
|
||||
from .tag_processing import process_tag
|
||||
@ -24,7 +25,37 @@ class AutocompleteEntry(tk.Entry):
|
||||
self.bind("<Up>", self.on_up)
|
||||
self.bind("<Return>", 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):
|
||||
if event.keysym in ("Down", "Up", "Return", "Tab"):
|
||||
@ -99,7 +130,7 @@ class AutocompleteEntry(tk.Entry):
|
||||
self.suggestion_map[display_text] = tag_insert
|
||||
return suggestions
|
||||
except Exception as e:
|
||||
print("Błąd przy pobieraniu sugestii:", e)
|
||||
print(_("Błąd przy pobieraniu sugestii:"), e)
|
||||
return []
|
||||
|
||||
def show_listbox(self):
|
||||
@ -139,12 +170,22 @@ class AutocompleteEntry(tk.Entry):
|
||||
|
||||
def on_listbox_click(self, event):
|
||||
if self.listbox:
|
||||
index = self.listbox.curselection()
|
||||
if index:
|
||||
value = self.listbox.get(index)
|
||||
# Process click first before changing focus
|
||||
index = self.listbox.nearest(event.y)
|
||||
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.insert(tk.END, value)
|
||||
self.hide_listbox()
|
||||
# Then explicitly manage focus
|
||||
self.focus_set() # Use focus_set() instead of focus_force()
|
||||
self.hide_listbox()
|
||||
return "break"
|
||||
|
||||
def on_listbox_motion(self, event):
|
||||
|
@ -3,10 +3,11 @@ import subprocess
|
||||
from bs4 import BeautifulSoup
|
||||
import requests
|
||||
|
||||
from .I18N import _
|
||||
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."""
|
||||
# Usuń prefiksy
|
||||
for prefix in [
|
||||
@ -31,7 +32,7 @@ def open_webbrowser(url, settings: Settings):
|
||||
subprocess.run([settings.browser, url], check=True)
|
||||
return
|
||||
except Exception as e:
|
||||
print("Błąd przy otwieraniu przeglądarki:", e)
|
||||
print(_("Błąd przy otwieraniu przeglądarki:"), e)
|
||||
import webbrowser
|
||||
|
||||
webbrowser.open(url)
|
||||
@ -68,18 +69,18 @@ def login(settings: Settings):
|
||||
shm_session = session.cookies.get("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(f"shm_user: {shm_user}")
|
||||
print(f"shm_session: {shm_session}")
|
||||
print(_("Login successful. Cookies stored in session."))
|
||||
|
||||
return session
|
||||
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
|
||||
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
|
||||
shm_user = session.cookies.get("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>
|
||||
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:
|
||||
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
|
||||
@ -118,7 +121,7 @@ def get_auth_token(session, settings):
|
||||
auth_input = soup.find("input", {"name": "auth_token"})
|
||||
if auth_input and auth_input.has_attr("value"):
|
||||
auth_token = auth_input["value"]
|
||||
print(f"Found auth_token: {auth_token}")
|
||||
print(_("Found auth_token:"), auth_token)
|
||||
return auth_token
|
||||
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 importlib
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from .I18N import _
|
||||
|
||||
# Na Windowsie używamy DPAPI
|
||||
if sys.platform.startswith("win"):
|
||||
try:
|
||||
@ -49,6 +49,7 @@ def get_browser_paths_windows():
|
||||
|
||||
return browsers
|
||||
|
||||
|
||||
def get_browsers_linux():
|
||||
"""Detects installed browsers on Linux by checking available executables."""
|
||||
browsers = {"Default": None}
|
||||
@ -114,6 +115,7 @@ class Settings:
|
||||
self.default_tags = "artist:kapitan meta:ai-generated"
|
||||
self.cache_expiry = 604800 # 7 dni w sekundach
|
||||
self.browser = ""
|
||||
self.i18n = _
|
||||
self.installed_browsers = detect_installed_browsers()
|
||||
self.load_settings()
|
||||
self.installed_browsers_reverse = {
|
||||
@ -139,6 +141,7 @@ class Settings:
|
||||
self.default_tags = "artist:kapitan meta:ai-generated"
|
||||
self.cache_expiry = 604800 # 7 dni w sekundach
|
||||
self.browser = ""
|
||||
self.i18n.set_language("en")
|
||||
try:
|
||||
if os.path.exists(self.get_settings_path()):
|
||||
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)
|
||||
if self.browser not in self.installed_browsers:
|
||||
self.browser = ""
|
||||
self.i18n.set_language(data.get("language", "en"))
|
||||
except Exception as e:
|
||||
print("Błąd podczas ładowania ustawień:", e)
|
||||
print(_("Błąd podczas ładowania ustawień:"), e)
|
||||
|
||||
def save_settings(self):
|
||||
"""Zapisuje ustawienia do pliku."""
|
||||
@ -166,6 +170,7 @@ class Settings:
|
||||
"default_tags": self.default_tags,
|
||||
"cache_expiry": self.cache_expiry,
|
||||
"browser": self.browser,
|
||||
"language": self.i18n.current_lang,
|
||||
}
|
||||
# Na Windowsie szyfrujemy hasło
|
||||
if sys.platform.startswith("win"):
|
||||
@ -176,4 +181,4 @@ class Settings:
|
||||
with open(self.get_settings_path(), "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=4)
|
||||
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
|
||||
import re
|
||||
|
||||
from .I18N import _
|
||||
from .TagsRepo import TagsRepo
|
||||
|
||||
|
||||
@ -15,7 +16,7 @@ def get_character_tags(tags_repo: TagsRepo):
|
||||
conn.close()
|
||||
return {row[0] for row in rows}
|
||||
except Exception as e:
|
||||
print("Błąd przy pobieraniu tagów postaci:", e)
|
||||
print(_("Błąd przy pobieraniu tagów postaci:"), e)
|
||||
return set()
|
||||
|
||||
|
||||
@ -30,7 +31,7 @@ def get_copyright_tags(tags_repo: TagsRepo):
|
||||
conn.close()
|
||||
return {row[0] for row in rows}
|
||||
except Exception as e:
|
||||
print("Błąd przy pobieraniu tagów copyright:", e)
|
||||
print(_("Błąd przy pobieraniu tagów copyright:"), e)
|
||||
return set()
|
||||
|
||||
|
||||
@ -161,7 +162,7 @@ def process_tag(tag, tags_repo: TagsRepo):
|
||||
tag_lookup = row[0]
|
||||
conn.close()
|
||||
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
|
||||
try:
|
||||
@ -177,5 +178,5 @@ def process_tag(tag, tags_repo: TagsRepo):
|
||||
# Tag nie istnieje
|
||||
return tag_lookup, None
|
||||
except Exception as e:
|
||||
print("Błąd podczas odczytu tags:", e)
|
||||
print(_("Błąd podczas odczytu tags:"), e)
|
||||
return tag_lookup, None
|
||||
|
@ -2,6 +2,7 @@ import os
|
||||
import pickle
|
||||
import sqlite3
|
||||
import time
|
||||
from .I18N import _
|
||||
from .settings import Settings
|
||||
|
||||
|
||||
@ -59,7 +60,7 @@ class TaggerCache:
|
||||
else:
|
||||
self.delete_cache_entry(file_md5)
|
||||
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
|
||||
|
||||
def __setitem__(self, file_md5, result):
|
||||
@ -84,7 +85,7 @@ class TaggerCache:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
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):
|
||||
try:
|
||||
@ -94,7 +95,7 @@ class TaggerCache:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
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):
|
||||
try:
|
||||
@ -107,7 +108,7 @@ class TaggerCache:
|
||||
conn.commit()
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print("Błąd przy czyszczeniu przeterminowanego cache:", e)
|
||||
print(_("Błąd przy czyszczeniu przeterminowanego cache:"), e)
|
||||
|
||||
def clear_cache(self):
|
||||
try:
|
||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "kapitanbooru-uploader"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
description = "A GUI application for uploading images to KapitanBooru"
|
||||
authors = [
|
||||
{name = "Michał Leśniak", email = "kapitan@mlesniak.pl"}
|
||||
@ -25,4 +25,8 @@ license = {file = "LICENSE"}
|
||||
kapitanbooru-uploader = "kapitanbooru_uploader.__main__:main"
|
||||
|
||||
[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
|
||||
)
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user