diff --git a/.gitignore b/.gitignore index b09e1b4..3b40373 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ log.txt /dump.dat /lock.file settings.json -/lang/English.json logs/ .claude/ diff --git a/CLAUDE.md b/CLAUDE.md index a1e63a4..b325016 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -68,7 +68,7 @@ src/ ├── models/ # Domain models (Game, Channel, Campaign, Drop, Benefit) ├── config/ # Configuration (constants, paths, operations, settings, client_info) ├── utils/ # Pure utilities (string, JSON, async helpers, rate_limiter, backoff) -├── i18n/ # Translation system (translator) +├── i18n/ # Translation system (Translator class, TypedDict schemas) ├── auth/ # Authentication (auth_state for OAuth and token management) ├── api/ # External API (HTTP client, GraphQL client) ├── websocket/ # Real-time updates (websocket connection, pool) @@ -79,6 +79,13 @@ src/ ├── exceptions.py # Custom exceptions ├── version.py # Version string └── __main__.py # Entry point (replaces old main.py) + +lang/ # Translation JSON files (19 languages) +├── English.json # Default/fallback translations +├── Español.json +├── Français.json +├── Deutsch.json +└── ... # 15 more languages ``` ### Core Components @@ -205,6 +212,48 @@ Runs in background to trigger: - Channel cleanup when drops start/end (based on time_triggers) - Inventory reload every ~60 minutes +### Translation System + +**Architecture:** +- All translations stored as JSON files in `lang/` directory (19 languages supported) +- English (`lang/English.json`) is the single source of truth and fallback language +- Strongly typed with TypedDict schema defined in `src/i18n/translator.py` +- Translator class (`src/i18n/translator.py`) handles language loading and fallback +- Singleton instance `_` available via `from src.i18n import _` + +**Supported Languages:** +- English, Dansk (Danish), Deutsch (German), Español (Spanish), Français (French) +- Indonesian, Italiano (Italian), Nederlandse (Dutch), Polski (Polish), Português (Portuguese) +- Română (Romanian), Türkçe (Turkish), Čeština (Czech) +- Русский (Russian), Українська (Ukrainian), العربية (Arabic) +- 日本語 (Japanese), 简体中文 (Simplified Chinese), 繁體中文 (Traditional Chinese) + +**Translation Structure:** +```python +Translation = { + "language_name": str, # Display name of language + "english_name": str, # English name of language + "status": StatusMessages, # Console status messages + "login": LoginMessages, # Login-related messages + "error": ErrorMessages, # Error messages + "gui": GUIMessages # All web GUI text (tabs, settings, help, etc.) +} +``` + +**Usage:** +```python +from src.i18n import _ + +# Access translations +status_text = _("gui", "status", "idle") # Returns "Idle" +login_text = _("login", "status", "logged_in") # Returns "Logged in" +``` + +**Language Persistence:** +- Language selection persisted in `settings.json` (DATA_DIR) +- Dynamic language switching supported in web GUI +- Changes take effect immediately without restart + ## Key Files - **src/config/constants.py** - Core enums (State, WebsocketTopic), logging config, type aliases @@ -214,7 +263,10 @@ Runs in background to trigger: - **src/config/settings.py** - Application settings with JSON persistence - **src/exceptions.py** - Custom exceptions (LoginException, CaptchaRequired, ExitRequest, ReloadRequest, etc.) - **src/utils/** - Helper utilities (string_utils, json_utils, async_helpers, rate_limiter, backoff) -- **src/i18n/translator.py** - i18n support with JSON translation files (20 languages supported) +- **src/i18n/** - Internationalization package with TypedDict schema and Translator class + - **translator.py** - Translator class with typed translation schema (Translation TypedDict) + - **__init__.py** - Exports translation types and `_` (Translator instance) +- **lang/** - Translation JSON files for 19 languages (English.json is the single source of truth) - **src/version.py** - Version string - **src/web/app.py** - FastAPI application with REST API and Socket.IO - **src/web/managers/cache.py** - ImageCache for campaign artwork caching @@ -308,4 +360,4 @@ The application uses a web-based interface accessible via browser: - Multi-account support - Channel points mining - Mining for unlinked campaigns -- Desktop GUI (removed in favor of web-only) +- Desktop GUI (removed in favor of web-only) \ No newline at end of file diff --git a/add_language_names.py b/add_language_names.py new file mode 100644 index 0000000..c372294 --- /dev/null +++ b/add_language_names.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +"""Add language_name field to all translation JSON files.""" + +import json +from pathlib import Path + +LANG_PATH = Path(__file__).parent / "lang" + +def add_language_names(): + """Add language_name field to each translation file based on filename.""" + for filepath in LANG_PATH.glob("*.json"): + # Extract language name from filename (without .json extension) + language_name = filepath.stem + + print(f"Processing {filepath.name}...") + + # Read the JSON file + with open(filepath, 'r', encoding='utf-8') as f: + data = json.load(f) + + # Add language_name at the beginning + updated_data = {"language_name": language_name} + updated_data.update(data) + + # Write back to file with proper formatting + with open(filepath, 'w', encoding='utf-8') as f: + json.dump(updated_data, f, ensure_ascii=False, indent=4) + + print(f" ✓ Added language_name: {language_name}") + +if __name__ == "__main__": + add_language_names() + print("\n✓ All translation files updated!") diff --git a/add_more_translations.py b/add_more_translations.py new file mode 100644 index 0000000..29ab795 --- /dev/null +++ b/add_more_translations.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +""" +Script to add missing translation keys to all language files. +Adds English text as placeholders where translations are missing. +""" + +import json +import os +from pathlib import Path + +# Translations to add/update for each language +# Format: {language_code: {key_path: translation}} +TRANSLATIONS = { + "English": { + # Already updated, this is our reference + }, + "Simplified Chinese": { + "gui.login.oauth_prompt": "在此网站输入代码:", + "gui.login.oauth_activate": "Twitch 激活", + "gui.login.oauth_confirm": "我已输入代码", + "gui.progress.no_drop": "无活跃掉宝", + "gui.progress.return_to_auto": "返回自动模式", + "gui.progress.manual_mode_info": "手动模式:正在挖掘", + "gui.channels.no_channels": "尚未跟踪任何频道...", + "gui.channels.no_channels_for_games": "所选游戏未找到频道...", + "gui.channels.channel_count": "频道", + "gui.channels.channel_count_plural": "频道", + "gui.channels.viewers": "观众", + "gui.inventory.no_campaigns": "尚未加载任何活动...", + "gui.inventory.claimed_drops": "已领取", + "gui.settings.general": "常规设置", + "gui.settings.dark_mode": "深色模式", + "gui.settings.reload_campaigns": "重新加载活动", + "gui.help.about": "关于 Twitch 掉宝矿工", + "gui.help.about_text": "此应用程序可在不下载流数据的情况下自动挖掘定时 Twitch 掉宝。", + "gui.help.how_to_use": "使用方法", + "gui.help.how_to_use_items": [ + "使用您的 Twitch 账号登录(OAuth 设备代码流程)", + "在 twitch.tv/drops/campaigns 关联您的账号", + "矿工将自动发现活动并开始挖掘", + "在设置中配置优先游戏以关注您想要的内容", + "在主界面和库存选项卡中监控进度" + ], + "gui.help.features": "功能", + "gui.help.features_items": [ + "无流挖掘 - 节省带宽", + "游戏优先级和排除列表", + "同时跟踪最多 199 个频道", + "自动切换频道", + "实时进度跟踪" + ], + "gui.help.important_notes": "重要提示", + "gui.help.important_notes_items": [ + "挖掘时请勿在同一账号上观看流", + "保护好您的 cookies.jar 文件", + "需要关联游戏账号才能掉宝" + ], + "gui.help.github_repo": "GitHub 仓库", + "gui.header.language": "语言:", + "gui.header.initializing": "初始化中...", + "gui.header.connected": "已连接", + "gui.header.disconnected": "已断开", + }, +} + +# Default English translations for all languages +DEFAULT_TRANSLATIONS = { + "gui.login.user_id_label": "User ID:", + "gui.login.waiting_auth": "Waiting for authentication...", + "gui.login.oauth_prompt": "Enter this code at:", + "gui.login.oauth_activate": "Twitch Activate", + "gui.login.oauth_confirm": "I've entered the code", + "gui.progress.no_drop": "No active drop", + "gui.progress.return_to_auto": "Return to Auto Mode", + "gui.progress.manual_mode_info": "Manual Mode: Mining", + "gui.channels.no_channels": "No channels tracked yet...", + "gui.channels.no_channels_for_games": "No channels found for selected games...", + "gui.channels.channel_count": "channel", + "gui.channels.channel_count_plural": "channels", + "gui.channels.viewers": "viewers", + "gui.inventory.no_campaigns": "No campaigns loaded yet...", + "gui.inventory.claimed_drops": "claimed", + "gui.settings.general": "General Settings", + "gui.settings.dark_mode": "Dark Mode", + "gui.settings.reload_campaigns": "Reload Campaigns", + "gui.help.about": "About Twitch Drops Miner", + "gui.help.about_text": "This application automatically mines timed Twitch drops without downloading stream data.", + "gui.help.how_to_use": "How to Use", + "gui.help.how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "gui.help.features": "Features", + "gui.help.features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "gui.help.important_notes": "Important Notes", + "gui.help.important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ], + "gui.help.github_repo": "GitHub Repository", + "gui.header.language": "Language:", + "gui.header.initializing": "Initializing...", + "gui.header.connected": "Connected", + "gui.header.disconnected": "Disconnected", +} + + +def set_nested_value(data, key_path, value): + """Set a value in a nested dictionary using dot notation.""" + keys = key_path.split(".") + current = data + for key in keys[:-1]: + if key not in current: + current[key] = {} + current = current[key] + current[keys[-1]] = value + + +def get_nested_value(data, key_path, default=None): + """Get a value from a nested dictionary using dot notation.""" + keys = key_path.split(".") + current = data + for key in keys: + if not isinstance(current, dict) or key not in current: + return default + current = current[key] + return current + + +def update_language_file(file_path): + """Update a language file with missing translations.""" + with open(file_path, 'r', encoding='utf-8') as f: + data = json.load(f) + + language_name = data.get("english_name", "Unknown") + print(f"Updating {language_name}...") + + # Get language-specific translations or use defaults + lang_translations = TRANSLATIONS.get(language_name, {}) + + updated = False + for key_path, default_value in DEFAULT_TRANSLATIONS.items(): + # Check if translation exists + current_value = get_nested_value(data, key_path) + + # Use language-specific translation if available, otherwise use default + new_value = lang_translations.get(key_path, default_value) + + # Only update if missing or if we have a language-specific translation + if current_value is None or (key_path in lang_translations): + set_nested_value(data, key_path, new_value) + updated = True + print(f" - Updated {key_path}") + + if updated: + # Write back with proper formatting + with open(file_path, 'w', encoding='utf-8') as f: + json.dump(data, f, ensure_ascii=False, indent=4) + print(f" ✓ Saved {language_name}") + else: + print(f" ✓ {language_name} already up to date") + + return updated + + +def main(): + lang_dir = Path(__file__).parent / "lang" + + if not lang_dir.exists(): + print(f"Error: Language directory not found: {lang_dir}") + return + + print("Updating language files...") + print("=" * 60) + + total_updated = 0 + for lang_file in sorted(lang_dir.glob("*.json")): + if update_language_file(lang_file): + total_updated += 1 + print() + + print("=" * 60) + print(f"Updated {total_updated} language files") + print() + print("Note: Some languages have English placeholders.") + print("Please translate these to the appropriate language.") + + +if __name__ == "__main__": + main() diff --git a/lang/Dansk.json b/lang/Dansk.json index 0009a77..66e2204 100644 --- a/lang/Dansk.json +++ b/lang/Dansk.json @@ -1,4 +1,5 @@ { + "language_name": "Dansk", "english_name": "Danish", "status": { "terminated": "\nProgram Afsluttet.\nLuk vinduet for at afslutte programmet.", @@ -16,7 +17,14 @@ "incorrect_email_code": "Forkert email kode.", "incorrect_twofa_code": "Forkert 2FA-kode.", "email_code_required": "Email kode påkrævet. Tjek din email.", - "twofa_code_required": "2FA token påkrævet." + "twofa_code_required": "2FA token påkrævet.", + "status": { + "logged_in": "Logget ind", + "logged_out": "Logget ud", + "logging_in": "Logger ind...", + "required": "Login påkrævet", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "Dit loginforsøg blev afvist af CAPTCHA.\nPrøv venligst igen om 12+ timer", @@ -46,10 +54,6 @@ "login": { "name": "Login formular", "labels": "Status:\nBruger ID:", - "logged_in": "Logget ind", - "logged_out": "Logget ud", - "logging_in": "Logger ind...", - "required": "Login påkrævet", "request": "Log venligst ind for at fortsætte.", "username": "Brugernavn", "password": "Kodeord", @@ -122,7 +126,9 @@ "all_games_selected": "Alle spil er valgt eller ingen tilgængelige spil.", "actions": "Handlinger", "connection_quality": "Forbindelseskvalitet:", - "minimum_refresh": "Minimum opdateringsinterval (minutter):" + "minimum_refresh": "Minimum opdateringsinterval (minutter):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/Deutsch.json b/lang/Deutsch.json index ece6688..42d0716 100644 --- a/lang/Deutsch.json +++ b/lang/Deutsch.json @@ -1,4 +1,5 @@ { + "language_name": "Deutsch", "english_name": "German", "status": { "terminated": "\nAnwendung gestoppt.\nFenster schließen, um die Anwendung zu beenden", @@ -16,7 +17,14 @@ "incorrect_email_code": "Falscher E-Mail Code.", "incorrect_twofa_code": "Falscher 2FA Code.", "email_code_required": "E-Mail Code erforderlich. Bitte E-Mail prüfen.", - "twofa_code_required": "2FA Token erforderlich." + "twofa_code_required": "2FA Token erforderlich.", + "status": { + "logged_in": "Angemeldet", + "logged_out": "Abgemeldet", + "logging_in": "Anmelden...", + "required": "Anmeldung erforderlich", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "Der Anmeldeversuch wurde durch CAPTCHA verweigert.\nBitte versuche es in mindestens 12 Stunden erneut.", @@ -46,10 +54,6 @@ "login": { "name": "Login", "labels": "Status:\nBenutzer ID:", - "logged_in": "Angemeldet", - "logged_out": "Abgemeldet", - "logging_in": "Anmelden...", - "required": "Anmeldung erforderlich", "request": "Bitte einloggen um fortzufahren.", "username": "Benutzername", "password": "Passwort", @@ -122,7 +126,9 @@ "all_games_selected": "Alle Spiele sind ausgewählt oder keine Spiele verfügbar.", "actions": "Aktionen", "connection_quality": "Verbindungsqualität:", - "minimum_refresh": "Minimales Aktualisierungsintervall (Minuten):" + "minimum_refresh": "Minimales Aktualisierungsintervall (Minuten):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/English.json b/lang/English.json new file mode 100644 index 0000000..bbad36e --- /dev/null +++ b/lang/English.json @@ -0,0 +1,175 @@ +{ + "language_name": "English", + "english_name": "English", + "status": { + "terminated": "\nApplication Terminated.\nClose the window to exit the application.", + "watching": "Watching: {channel}", + "goes_online": "{channel} goes ONLINE, switching...", + "goes_offline": "{channel} goes OFFLINE, switching...", + "claimed_drop": "Claimed drop: {drop}", + "no_channel": "No available channels to watch. Waiting for an ONLINE channel...", + "no_campaign": "No active campaigns to mine drops for. Waiting for an active campaign..." + }, + "login": { + "unexpected_content": "Unexpected content type returned, usually due to being redirected. Do you need to login for internet access?", + "error_code": "Login error code: {error_code}", + "incorrect_login_pass": "Incorrect username or password.", + "incorrect_email_code": "Incorrect email code.", + "incorrect_twofa_code": "Incorrect 2FA code.", + "email_code_required": "Email code required. Check your email.", + "twofa_code_required": "2FA token required.", + "status": { + "logged_in": "Logged in", + "logged_out": "Logged out", + "logging_in": "Logging in...", + "required": "Login required", + "waiting_auth": "Waiting for authentication..." + } + }, + "error": { + "captcha": "Your login attempt was denied by CAPTCHA.\nPlease try again in 12+ hours.", + "site_down": "Twitch is down, retrying in {seconds} seconds...", + "no_connection": "Cannot connect to Twitch, retrying in {seconds} seconds..." + }, + "gui": { + "output": "Output", + "status": { + "name": "Status", + "idle": "Idle", + "ready": "Ready", + "exiting": "Exiting...", + "terminated": "Terminated", + "cleanup": "Cleaning up channels...", + "gathering": "Gathering channels...", + "switching": "Switching the channel...", + "fetching_inventory": "Fetching inventory...", + "fetching_campaigns": "Fetching campaigns...", + "adding_campaigns": "Adding campaigns to inventory... {counter}" + }, + "tabs": { + "main": "Main", + "inventory": "Inventory", + "settings": "Settings", + "help": "Help" + }, + "login": { + "name": "Login Form", + "labels": "Status:\nUser ID:", + "request": "Please log in to continue.", + "username": "Username", + "password": "Password", + "twofa_code": "2FA code (optional)", + "button": "Login", + "oauth_prompt": "Enter this code at:", + "oauth_activate": "Twitch Activate", + "oauth_confirm": "I've entered the code" + }, + "websocket": { + "name": "Websocket Status", + "websocket": "Websocket #{id}:", + "initializing": "Initializing...", + "connected": "Connected", + "disconnected": "Disconnected", + "connecting": "Connecting...", + "disconnecting": "Disconnecting...", + "reconnecting": "Reconnecting..." + }, + "progress": { + "name": "Campaign Progress", + "drop": "Drop:", + "game": "Game:", + "campaign": "Campaign:", + "remaining": "{time} remaining", + "drop_progress": "Progress:", + "campaign_progress": "Progress:", + "no_drop": "No active drop", + "return_to_auto": "Return to Auto Mode", + "manual_mode_info": "Manual Mode: Mining" + }, + "channels": { + "name": "Channels", + "online": "ONLINE ✔", + "pending": "OFFLINE ⏳", + "offline": "OFFLINE ❌", + "no_channels": "No channels tracked yet...", + "no_channels_for_games": "No channels found for selected games...", + "channel_count": "channel", + "channel_count_plural": "channels", + "viewers": "viewers" + }, + "inventory": { + "no_campaigns": "No campaigns loaded yet...", + "status": { + "active": "Active ✔", + "upcoming": "Upcoming ⏳", + "expired": "Expired ❌", + "claimed": "Claimed ✔" + }, + "starts": "Starts: {time}", + "ends": "Ends: {time}", + "claimed_drops": "claimed" + }, + "settings": { + "general": "General Settings", + "dark_mode": "Dark Mode", + "reload": "Reload", + "reload_campaigns": "Reload Campaigns", + "games_to_watch": "Games to Watch", + "games_help": "Select games to watch. Order matters - drag to reorder priority (top = highest priority).", + "search_games": "Search games...", + "select_all": "Select All", + "deselect_all": "Deselect All", + "selected_games": "Selected Games (drag to reorder)", + "available_games": "Available Games", + "no_games_selected": "No games selected. Check games below to add them.", + "no_games_match": "No games match your search.", + "all_games_selected": "All games are selected or no games available.", + "actions": "Actions", + "connection_quality": "Connection Quality:", + "minimum_refresh": "Minimum Refresh Interval (minutes):" + }, + "help": { + "links": { + "name": "Useful Links" + }, + "how_it_works": "How It Works", + "how_it_works_text": "Every several seconds, the application pretends to watch a particular stream by fetching stream metadata - this is enough to advance the drops. Note that this completely bypasses the need to download any actual stream of video and sound. To keep the status (ONLINE or OFFLINE) of the channels up-to-date, there's a websocket connection established that receives events about streams going up or down, or updates regarding the current number of viewers.", + "getting_started": "Getting Started", + "getting_started_text": "1. Login to the application.\n2. Ensure your Twitch account is linked to all campaigns you're interested in mining.\n3. If you're interested in mining everything possible, change the Priority Mode to anything other than \"Priority list only\" and press on \"Reload\".\n4. If you want to mine specific games first, use the \"Priority\" list to set up an ordered list of games of your choice. Games from the top of the list will be attempted to be mined first, before the ones lower down the list.\n5. Keep the \"Priority mode\" selected as \"Priority list only\", to avoid mining games that are not on the priority list. Or not - it's up to you.\n6. Use the \"Exclude\" list to tell the application which games should never be mined.\n7. Changing the contents of either of the lists, or changing the \"Priority mode\", requires you to press on \"Reload\" for the changes to take an effect.", + "about": "About Twitch Drops Miner", + "about_text": "This application automatically mines timed Twitch drops without downloading stream data.", + "how_to_use": "How to Use", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features": "Features", + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes": "Important Notes", + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ], + "github_repo": "GitHub Repository" + }, + "header": { + "title": "Twitch Drops Miner", + "language": "Language:", + "initializing": "Initializing...", + "auto_mode": "AUTO", + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" + } + } +} diff --git a/lang/Español.json b/lang/Español.json index 9e1f24f..8896e0d 100644 --- a/lang/Español.json +++ b/lang/Español.json @@ -1,4 +1,5 @@ { + "language_name": "Español", "english_name": "Spanish", "status": { "terminated": "\nLa aplicación se ha detenido.\nPor favor, cierre la aplicación.", @@ -16,7 +17,14 @@ "incorrect_email_code": "El código de verificación de email es incorrecto.", "incorrect_twofa_code": "El código 2FA es incorrecto.", "email_code_required": "Código de verificación de email requerido. \nPor favor, revise su email.", - "twofa_code_required": "Token 2FA requerido." + "twofa_code_required": "Token 2FA requerido.", + "status": { + "logged_in": "Conectado", + "logged_out": "Desconectado", + "logging_in": "Iniciando sesión...", + "required": "Se requiere inicio de sesión", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "El inicio de sesión fue rechazado por CAPTCHA.\nPor favor, intente nuevamente en aprox. 12 horas.", @@ -46,10 +54,6 @@ "login": { "name": "Inicio de sesión", "labels": "Estado:\nUsuario:", - "logged_in": "Conectado", - "logged_out": "Desconectado", - "logging_in": "Iniciando sesión...", - "required": "Se requiere inicio de sesión", "request": "Por favor, inicie sesión para continuar.", "username": "Usuario", "password": "Contraseña", @@ -122,7 +126,9 @@ "all_games_selected": "Todos los juegos están seleccionados o no hay juegos disponibles.", "actions": "Acciones", "connection_quality": "Calidad de conexión:", - "minimum_refresh": "Intervalo mínimo de actualización (minutos):" + "minimum_refresh": "Intervalo mínimo de actualización (minutos):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/Français.json b/lang/Français.json index 4ce5d61..1268209 100644 --- a/lang/Français.json +++ b/lang/Français.json @@ -1,4 +1,5 @@ { + "language_name": "Français", "english_name": "French", "status": { "terminated": "\nL'application a été arrêtée.\nVeuillez fermer l'application.", @@ -16,7 +17,14 @@ "incorrect_email_code": "Le code de vérification de l'e-mail est incorrect.", "incorrect_twofa_code": "Le code 2FA est incorrect.", "email_code_required": "Code de vérification par e-mail requis. Merci de consulter vos emails.", - "twofa_code_required": "Code 2FA requis." + "twofa_code_required": "Code 2FA requis.", + "status": { + "logged_in": "Connecté", + "logged_out": "Déconnecté", + "logging_in": "Connexion en cours...", + "required": "Connexion requise", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "La connexion a été rejetée par CAPTCHA. Veuillez réessayer dans env. 12 heures.", @@ -46,10 +54,6 @@ "login": { "name": "Formulaire de connexion", "labels": "Statut :\nIdentifiant utilisateur :", - "logged_in": "Connecté", - "logged_out": "Déconnecté", - "logging_in": "Connexion en cours...", - "required": "Connexion requise", "request": "Veuillez vous connecter pour continuer.", "username": "Nom d'utilisateur", "password": "Mot de passe", @@ -122,7 +126,9 @@ "all_games_selected": "Tous les jeux sont sélectionnés ou aucun jeu disponible.", "actions": "Actions", "connection_quality": "Qualité de connexion:", - "minimum_refresh": "Intervalle minimum de rafraîchissement (minutes):" + "minimum_refresh": "Intervalle minimum de rafraîchissement (minutes):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/Indonesian.json b/lang/Indonesian.json index fb51887..f3ca31c 100644 --- a/lang/Indonesian.json +++ b/lang/Indonesian.json @@ -1,4 +1,5 @@ { + "language_name": "Indonesian", "english_name": "Indonesian", "status": { "terminated": "\nAplikasi telah dihentikan. \nSilakan tutup aplikasi.", @@ -16,7 +17,14 @@ "incorrect_email_code": "Kode verifikasi email salah.", "incorrect_twofa_code": "Kode verifikasi 2FA salah.", "email_code_required": "Diperlukan kode verifikasi email. Silakan periksa email Anda.", - "twofa_code_required": "Diperlukan kode 2FA." + "twofa_code_required": "Diperlukan kode 2FA.", + "status": { + "logged_in": "Online", + "logged_out": "Terputus", + "logging_in": "Koneksi dalam proses...", + "required": "Diperlukan sambungan", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "Sambungan ditolak oleh CAPTCHA. Silakan coba lagi dalam waktu sekitar 12 jam.", @@ -46,10 +54,6 @@ "login": { "name": "Formulir masuk", "labels": "Status :\nID Pengguna :", - "logged_in": "Online", - "logged_out": "Terputus", - "logging_in": "Koneksi dalam proses...", - "required": "Diperlukan sambungan", "request": "Silakan masuk untuk melanjutkan.", "username": "Nama pengguna", "password": "Kata sandi", @@ -122,7 +126,9 @@ "all_games_selected": "Semua game telah dipilih atau tidak ada game tersedia.", "actions": "Tindakan", "connection_quality": "Kualitas Koneksi:", - "minimum_refresh": "Interval Refresh Minimum (menit):" + "minimum_refresh": "Interval Refresh Minimum (menit):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/Italiano.json b/lang/Italiano.json index 400689d..301c1fb 100644 --- a/lang/Italiano.json +++ b/lang/Italiano.json @@ -1,4 +1,5 @@ { + "language_name": "Italiano", "english_name": "Italian", "status": { "terminated": "\nApplicazione Terminata.\nChiudi la finestra per uscire dall'applicazione.", @@ -16,7 +17,14 @@ "incorrect_email_code": "Codice email errato.", "incorrect_twofa_code": "Codice 2FA errato.", "email_code_required": "Codice email richiesto. Controlla la tua email.", - "twofa_code_required": "Token 2FA richiesto." + "twofa_code_required": "Token 2FA richiesto.", + "status": { + "logged_in": "Loggato", + "logged_out": "Non loggato", + "logging_in": "Loggando...", + "required": "Login richiesto", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "Il tuo tentativo di login è stato negato da CAPTCHA.\nRiprova tra 12+ ore.", @@ -46,10 +54,6 @@ "login": { "name": "Dettagli Login", "labels": "Stato:\nID Utente:", - "logged_in": "Loggato", - "logged_out": "Non loggato", - "logging_in": "Loggando...", - "required": "Login richiesto", "request": "Per favore, effettua il login per continuare.", "username": "Nome utente", "password": "Password", @@ -122,7 +126,9 @@ "all_games_selected": "Tutti i giochi sono selezionati o non ci sono giochi disponibili.", "actions": "Azioni", "connection_quality": "Qualità della connessione:", - "minimum_refresh": "Intervallo minimo di aggiornamento (minuti):" + "minimum_refresh": "Intervallo minimo di aggiornamento (minuti):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/Nederlandse.json b/lang/Nederlandse.json index 0ba839f..153506c 100644 --- a/lang/Nederlandse.json +++ b/lang/Nederlandse.json @@ -1,4 +1,5 @@ { + "language_name": "Nederlandse", "english_name": "Dutch", "status": { "terminated": "\nToepassing gestopt.\nSLuit het venster om de toepassing af te sluiten", @@ -16,7 +17,14 @@ "incorrect_email_code": "Onjuiste e-mailcode.", "incorrect_twofa_code": "Onjuiste 2FA Code.", "email_code_required": "E-mailcode vereist. Controleer de e-mail.", - "twofa_code_required": "2FA Token vereist." + "twofa_code_required": "2FA Token vereist.", + "status": { + "logged_in": "Geregistreerd", + "logged_out": "Uitgelogd", + "logging_in": "Aanmelden...", + "required": "Aanmelden vereist", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "De inlogpoging is geweigerd door CAPTCHA.\nProbeer het over minimaal 12 uur opnieuw.", @@ -46,10 +54,6 @@ "login": { "name": "Login", "labels": "Status:\nGebruiker ID:", - "logged_in": "Geregistreerd", - "logged_out": "Uitgelogd", - "logging_in": "Aanmelden...", - "required": "Aanmelden vereist", "request": "Log in om door te gaan.", "username": "Gebruikersnaam", "password": "Wachtwoord", @@ -122,7 +126,9 @@ "all_games_selected": "Alle games zijn geselecteerd of er zijn geen games beschikbaar.", "actions": "Acties", "connection_quality": "Verbindingskwaliteit:", - "minimum_refresh": "Minimaal verversingsinterval (minuten):" + "minimum_refresh": "Minimaal verversingsinterval (minuten):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/Polski.json b/lang/Polski.json index 7576d88..23fbd79 100644 --- a/lang/Polski.json +++ b/lang/Polski.json @@ -1,4 +1,5 @@ { + "language_name": "Polski", "english_name": "Polish", "status": { "terminated": "\nAplikacja została zatrzymana.\nZamknij okno, aby wyjść z aplikacji.", @@ -16,7 +17,14 @@ "incorrect_email_code": "Nieprawidłowy kod z e-maila.", "incorrect_twofa_code": "Nieprawidłowy kod 2FA.", "email_code_required": "Wymagany kod z e-maila.", - "twofa_code_required": "Wymagany token 2FA." + "twofa_code_required": "Wymagany token 2FA.", + "status": { + "logged_in": "Zalogowano", + "logged_out": "Wylogowano", + "logging_in": "Logowanie...", + "required": "Wymagane zalogowanie", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "Próba logowania została odrzucona przez CAPTCHA.\nProszę spróbować ponownie za co najmniej 12 godzin.", @@ -46,10 +54,6 @@ "login": { "name": "Logowanie", "labels": "Status:\nIdentyfikator:", - "logged_in": "Zalogowano", - "logged_out": "Wylogowano", - "logging_in": "Logowanie...", - "required": "Wymagane zalogowanie", "request": "Zaloguj się, by kontynuować.", "username": "Nazwa użytkownika", "password": "Hasło", @@ -123,7 +127,9 @@ "all_games_selected": "Wszystkie gry są wybrane lub brak dostępnych gier.", "actions": "Akcje", "connection_quality": "Jakość połączenia:", - "minimum_refresh": "Minimalny interwał odświeżania (minuty):" + "minimum_refresh": "Minimalny interwał odświeżania (minuty):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -138,14 +144,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/Português.json b/lang/Português.json index 5f221d8..f790d0d 100644 --- a/lang/Português.json +++ b/lang/Português.json @@ -1,4 +1,5 @@ { + "language_name": "Português", "english_name": "Portuguese", "status": { "terminated": "\nAplicação Finalizada.\nFeche a janela para sair do programa.", @@ -16,7 +17,14 @@ "incorrect_email_code": "Codigo de email incorreto", "incorrect_twofa_code": "Codigo F2A incorreto", "email_code_required": "Código no email necessário. Verifique sua caixa de entrada", - "twofa_code_required": "Token 2FA necessário." + "twofa_code_required": "Token 2FA necessário.", + "status": { + "logged_in": "Entrou", + "logged_out": "Saiu", + "logging_in": "Entrando...", + "required": "Login necessário", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "Seu login foi negado pelo CAPTCHA.\nPor favor tente em aproximadamente 12+ horas.", @@ -46,10 +54,6 @@ "login": { "name": "Tela de login", "labels": "Status:\nUser ID:", - "logged_in": "Entrou", - "logged_out": "Saiu", - "logging_in": "Entrando...", - "required": "Login necessário", "request": "Por favor, entre para continuar.", "username": "Usuário", "password": "Senha", @@ -122,7 +126,9 @@ "all_games_selected": "Todos os jogos estão selecionados ou não há jogos disponíveis.", "actions": "Ações", "connection_quality": "Qualidade da conexão:", - "minimum_refresh": "Intervalo mínimo de atualização (minutos):" + "minimum_refresh": "Intervalo mínimo de atualização (minutos):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/Română.json b/lang/Română.json index 249eb62..495f230 100644 --- a/lang/Română.json +++ b/lang/Română.json @@ -1,4 +1,5 @@ { + "language_name": "Română", "english_name": "Romanian", "status": { "terminated": "\nAplicația a fost oprită.\nVă rugăm să închideți aplicația.", @@ -16,7 +17,14 @@ "incorrect_email_code": "Codul de verificare din e-mail este incorect.", "incorrect_twofa_code": "Codul 2FA este incorect.", "email_code_required": "Codul de verificare din e-mail este necesar. Vă rugăm să vă verificați e-mailul.", - "twofa_code_required": "Este necesar un cod 2FA." + "twofa_code_required": "Este necesar un cod 2FA.", + "status": { + "logged_in": "Conectat", + "logged_out": "Deconectat", + "logging_in": "Se conectează...", + "required": "Este necesară conectarea", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "Conexiunea a fost respinsă de CAPTCHA. Vă rugăm să încercați din nou în aprox. 12 ore.", @@ -46,10 +54,6 @@ "login": { "name": "Formular de conectare", "labels": "Statut :\nID-ul de utilizator :", - "logged_in": "Conectat", - "logged_out": "Deconectat", - "logging_in": "Se conectează...", - "required": "Este necesară conectarea", "request": "Vă rugăm să vă conectați pentru a continua.", "username": "Nume utilizator", "password": "Parolă", @@ -122,7 +126,9 @@ "all_games_selected": "Toate jocurile sunt selectate sau nu sunt jocuri disponibile.", "actions": "Acțiuni", "connection_quality": "Calitatea conexiunii:", - "minimum_refresh": "Interval minim de reîmprospătare (minute):" + "minimum_refresh": "Interval minim de reîmprospătare (minute):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/Türkçe.json b/lang/Türkçe.json index ac64205..caaf9b2 100644 --- a/lang/Türkçe.json +++ b/lang/Türkçe.json @@ -1,4 +1,5 @@ { + "language_name": "Türkçe", "english_name": "Turkish", "status": { "terminated": "\nUygulama durduruldu.\nUygulamadan çıkmak için pencereyi kapatın", @@ -16,7 +17,14 @@ "incorrect_email_code": "Yanlış e-posta kodu.", "incorrect_twofa_code": "Yanlış 2FA kodu.", "email_code_required": "E-posta kodu gerekli. Lütfen e-postayı kontrol edin.", - "twofa_code_required": "2FA belirteci gerekli." + "twofa_code_required": "2FA belirteci gerekli.", + "status": { + "logged_in": "Giriş Yapıldı", + "logged_out": "Çıkış Yapıldı", + "logging_in": "Kayıt Ol...", + "required": "Kaydolmak gerekiyor", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "Giriş girişimi CAPTCHA tarafından reddedildi.\nLütfen en az 12 saat içinde tekrar deneyin.", @@ -46,10 +54,6 @@ "login": { "name": "Giriş Yap", "labels": "Durum:\nKullanıcı ID:", - "logged_in": "Giriş Yapıldı", - "logged_out": "Çıkış Yapıldı", - "logging_in": "Kayıt Ol...", - "required": "Kaydolmak gerekiyor", "request": "Devam etmek için lütfen giriş yapınız.", "username": "Kullanıcı Adı", "password": "Parola", @@ -122,7 +126,9 @@ "all_games_selected": "Tüm oyunlar seçildi veya mevcut oyun yok.", "actions": "İşlemler", "connection_quality": "Bağlantı kalitesi:", - "minimum_refresh": "Minimum yenileme aralığı (dakika):" + "minimum_refresh": "Minimum yenileme aralığı (dakika):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/Čeština.json b/lang/Čeština.json index 3f21ed6..2f59701 100644 --- a/lang/Čeština.json +++ b/lang/Čeština.json @@ -1,4 +1,5 @@ { + "language_name": "Čeština", "english_name": "Czech", "status": { "terminated": "\nAplikace byla ukončena.", @@ -15,7 +16,14 @@ "incorrect_email_code": "Nesprávný E-Mail kód.", "incorrect_twofa_code": "Nesprávný dvoufaktorový token", "email_code_required": "K přihlášení je vyžadován kód který byl zaslán na váš E-Mail.", - "twofa_code_required": "K přihlášení je vyžadován dvoufaktorový kód." + "twofa_code_required": "K přihlášení je vyžadován dvoufaktorový kód.", + "status": { + "logged_in": "Přihlášeno", + "logged_out": "Odhlášeno", + "logging_in": "Přihlašování...", + "required": "Potřebujete se nejdříve přihlásit", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "Vaše připojení bylo zamítnuto službou Twitch, zkuste to znovu za 12 hodin.", @@ -45,10 +53,6 @@ "login": { "name": "Přihlášení k službě Twitch", "labels": "Uživatelské ID:", - "logged_in": "Přihlášeno", - "logged_out": "Odhlášeno", - "logging_in": "Přihlašování...", - "required": "Potřebujete se nejdříve přihlásit", "request": "Pro přístup je potřeba přihlášení", "username": "Uživatelské Jméno", "password": "Heslo", @@ -122,7 +126,9 @@ "all_games_selected": "Všechny hry jsou vybrány nebo nejsou k dispozici žádné hry.", "actions": "Akce", "connection_quality": "Kvalita připojení:", - "minimum_refresh": "Minimální interval obnovení (minuty):" + "minimum_refresh": "Minimální interval obnovení (minuty):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/Русский.json b/lang/Русский.json index 6ee1283..7d623ba 100644 --- a/lang/Русский.json +++ b/lang/Русский.json @@ -1,4 +1,5 @@ { + "language_name": "Русский", "english_name": "Russian", "status": { "terminated": "\nПриложение остановлено.\nЗакройте окно, чтобы выйти из приложения.", @@ -16,7 +17,14 @@ "incorrect_email_code": "Неправильный код электронной почты.", "incorrect_twofa_code": "Неправильный код 2FA.", "email_code_required": "Требуется код электронной почты. Пожалуйста, проверьте электронную почту.", - "twofa_code_required": "Требуется код 2FA." + "twofa_code_required": "Требуется код 2FA.", + "status": { + "logged_in": "Авторизован", + "logged_out": "не авторизован", + "logging_in": "Авторизация...", + "required": "Требуется авторизация", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "Попытка входа в систему была отклонена CAPTCHA.\nПожалуйста, повторите попытку не менее чем через 12 часов.", @@ -46,10 +54,6 @@ "login": { "name": "Авторизация", "labels": "Статус:\nID пользователя:", - "logged_in": "Авторизован", - "logged_out": "не авторизован", - "logging_in": "Авторизация...", - "required": "Требуется авторизация", "request": "Пожалуйста, авторизуйтесь, чтобы продолжить.", "username": "Имя пользователя", "password": "Пароль", @@ -123,7 +127,9 @@ "all_games_selected": "Все игры выбраны или нет доступных игр.", "actions": "Действия", "connection_quality": "Качество соединения:", - "minimum_refresh": "Минимальный интервал обновления (минуты):" + "minimum_refresh": "Минимальный интервал обновления (минуты):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -138,14 +144,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/Українська.json b/lang/Українська.json index d26325f..4d9e141 100644 --- a/lang/Українська.json +++ b/lang/Українська.json @@ -1,4 +1,5 @@ { + "language_name": "Українська", "english_name": "Ukrainian", "status": { "terminated": "\nЗастосунок зупинено.\nЗакрийте вікно для виходу з програми.", @@ -16,7 +17,14 @@ "incorrect_email_code": "Неправильний код електронної пошти.", "incorrect_twofa_code": "Неправильний код двофакторної аутентифікації.", "email_code_required": "Потрібен код електронної пошти.", - "twofa_code_required": "Потрібен жетон двофакторної аутентифікації." + "twofa_code_required": "Потрібен жетон двофакторної аутентифікації.", + "status": { + "logged_in": "Увійдено", + "logged_out": "Вийдено", + "logging_in": "Вхід...", + "required": "Потрібен вхід", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "Ваша спроба входу була відхилена через капчу.\nБудь ласка, спробуйте ще раз через 12 або більше годин.", @@ -46,10 +54,6 @@ "login": { "name": "Форма для входу", "labels": "Стан:\nІдентифікатор користувача:", - "logged_in": "Увійдено", - "logged_out": "Вийдено", - "logging_in": "Вхід...", - "required": "Потрібен вхід", "request": "Будь ласка, увійдіть, щоб продовжити.", "username": "Ім'я користувача", "password": "Пароль", @@ -122,7 +126,9 @@ "all_games_selected": "Всі ігри вибрано або немає доступних ігор.", "actions": "Дії", "connection_quality": "Якість з'єднання:", - "minimum_refresh": "Мінімальний інтервал оновлення (хвилини):" + "minimum_refresh": "Мінімальний інтервал оновлення (хвилини):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/العربية.json b/lang/العربية.json index d2f2b9d..9c923bc 100644 --- a/lang/العربية.json +++ b/lang/العربية.json @@ -1,4 +1,5 @@ { + "language_name": "العربية", "english_name": "Arabic", "status": { "terminated": "تم إنهاء التطبيق. \n أغلق النافذة للخروج من التطبيق.", @@ -16,7 +17,14 @@ "incorrect_email_code": ".كود البريد الإلكتروني غير صحيح", "incorrect_twofa_code": ".كود المصادقة الثنائية غير صحيح", "email_code_required": ".كود البريد الإلكتروني مطلوب. تحقق من بريدك الالكتروني", - "twofa_code_required": ".رمز المصادقة الثنائية مطلوب" + "twofa_code_required": ".رمز المصادقة الثنائية مطلوب", + "status": { + "logged_in": "تم تسجيل الدخول", + "logged_out": "تم تسجيل الخروج", + "logging_in": "...تسجيل الدخول", + "required": "تسجيل الدخول مطلوب", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": ".يرجى المحاولة مجدداَ بعد مرور 12 ساعة \n .CAPTCHA تم رفض محاولة تسجيل الدخول الخاصة بك من قبل", @@ -46,10 +54,6 @@ "login": { "name": "تسجيل الدخول و معلومات عن الحساب", "labels": "الحالة ➜\nالمستخدم ID ➜", - "logged_in": "تم تسجيل الدخول", - "logged_out": "تم تسجيل الخروج", - "logging_in": "...تسجيل الدخول", - "required": "تسجيل الدخول مطلوب", "request": ".الرجاء تسجيل الدخول للمتابعة", "username": "اسم المستخدم", "password": "كلمة المرور", @@ -122,7 +126,9 @@ "all_games_selected": "تم تحديد جميع الألعاب أو لا توجد ألعاب متاحة.", "actions": "الإجراءات", "connection_quality": "جودة الاتصال:", - "minimum_refresh": "الحد الأدنى لفترة التحديث (بالدقائق):" + "minimum_refresh": "الحد الأدنى لفترة التحديث (بالدقائق):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/日本語.json b/lang/日本語.json index 4f7a720..2631c2f 100644 --- a/lang/日本語.json +++ b/lang/日本語.json @@ -1,4 +1,5 @@ { + "language_name": "日本語", "english_name": "Japanese", "status": { "terminated": "\nアプリケーションが終了しました。\nウィンドウを閉じてアプリケーションを終了します。", @@ -16,7 +17,14 @@ "incorrect_email_code": "メールコードが間違っています。", "incorrect_twofa_code": "2段階認証コードが間違っています。", "email_code_required": "メールコードが必要です。メールを確認してください。", - "twofa_code_required": "2段階認証トークンが必要です。" + "twofa_code_required": "2段階認証トークンが必要です。", + "status": { + "logged_in": "ログイン済み", + "logged_out": "ログアウト済み", + "logging_in": "ログイン中...", + "required": "ログインが必要です", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "ログイン試行がCAPTCHAによって拒否されました。\n12時間以上後に再試行してください。", @@ -46,10 +54,6 @@ "login": { "name": "ログインフォーム", "labels": "ステータス:\nユーザーID:", - "logged_in": "ログイン済み", - "logged_out": "ログアウト済み", - "logging_in": "ログイン中...", - "required": "ログインが必要です", "request": "続行するにはログインしてください。", "username": "ユーザー名", "password": "パスワード", @@ -122,7 +126,9 @@ "all_games_selected": "すべてのゲームが選択されているか、利用可能なゲームがありません。", "actions": "アクション", "connection_quality": "接続品質:", - "minimum_refresh": "最小更新間隔(分):" + "minimum_refresh": "最小更新間隔(分):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/lang/简体中文.json b/lang/简体中文.json index e171589..ef76bd5 100644 --- a/lang/简体中文.json +++ b/lang/简体中文.json @@ -1,4 +1,5 @@ { + "language_name": "简体中文", "english_name": "Simplified Chinese", "status": { "terminated": "\n应用程序已终止,请关闭窗口以退出应用程序.", @@ -16,7 +17,14 @@ "incorrect_email_code": "电子邮箱验证码错误.", "incorrect_twofa_code": "手机2FA令牌双重验证码错误.", "email_code_required": "需要电子邮件验证码,查看你的邮箱.", - "twofa_code_required": "需要手机2FA令牌双重验证." + "twofa_code_required": "需要手机2FA令牌双重验证.", + "status": { + "logged_in": "已登录", + "logged_out": "未登录", + "logging_in": "正在登录中...", + "required": "请先登录", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "您的登录存在异常,Twitch官方已进行限制,请在12小时后重试.", @@ -46,18 +54,14 @@ "login": { "name": "登录Twitch账号", "labels": "状态:\n用户ID:", - "logged_in": "已登录", - "logged_out": "未登录", - "logging_in": "正在登录中...", - "required": "请先登录", "request": "请先登录以访问更多内容.", "username": "用户名", "password": "密码", "twofa_code": "令牌验证码", "button": "登录", - "oauth_prompt": "Enter this code at:", - "oauth_activate": "Twitch Activate", - "oauth_confirm": "I've entered the code" + "oauth_prompt": "在此网站输入代码:", + "oauth_activate": "Twitch 激活", + "oauth_confirm": "我已输入代码" }, "websocket": { "name": "网络协议连接状态", @@ -77,20 +81,20 @@ "campaign": "掉宝活动名称:", "drop_progress": "掉宝进度:", "campaign_progress": "活动进度:", - "no_drop": "No active drop", - "return_to_auto": "Return to Auto Mode", - "manual_mode_info": "Manual Mode: Mining" + "no_drop": "无活跃掉宝", + "return_to_auto": "返回自动模式", + "manual_mode_info": "手动模式:正在挖掘" }, "channels": { "name": "活动频道", "online": "ONLINE ✔", "pending": "OFFLINE ⏳", "offline": "OFFLINE ❌", - "no_channels": "No channels tracked yet...", - "no_channels_for_games": "No channels found for selected games...", - "channel_count": "channel", - "channel_count_plural": "channels", - "viewers": "viewers" + "no_channels": "尚未跟踪任何频道...", + "no_channels_for_games": "所选游戏未找到频道...", + "channel_count": "频道", + "channel_count_plural": "频道", + "viewers": "观众" }, "inventory": { "status": { @@ -101,14 +105,11 @@ }, "starts": "开始时间: {time}", "ends": "结束时间: {time}", - "no_campaigns": "No campaigns loaded yet...", - "claimed_drops": "claimed" + "no_campaigns": "尚未加载任何活动...", + "claimed_drops": "已领取" }, "settings": { - "general": { - "name": "功能设置", - "dark_mode": "深色模式: " - }, + "general": "常规设置", "reload": "刷新", "games_to_watch": "观看游戏", "games_help": "选择要观看的游戏。顺序很重要 - 拖动以重新排序优先级(顶部 = 最高优先级)。", @@ -122,7 +123,9 @@ "all_games_selected": "所有游戏都已选择或没有可用游戏。", "actions": "操作", "connection_quality": "连接质量:", - "minimum_refresh": "最小刷新间隔(分钟):" + "minimum_refresh": "最小刷新间隔(分钟):", + "dark_mode": "深色模式", + "reload_campaigns": "重新加载活动" }, "help": { "links": { @@ -132,19 +135,40 @@ "how_it_works_text": "每隔约 60 秒,应用程序会向当前正在观看的频道发送一个“观看了一分钟”事件 - 这足以推进掉宝进度。这完全不需要下载任何实际视频流和声音。为了使频道的状态(在线或离线)保持最新,建立了websocket连接,用于接收有关直播开播下播事件,或当前观众数量的更新。", "getting_started": "入门设置", "getting_started_text": "1. 登录应用程序。\n2.确保所有感兴趣挖掘的游戏的账号已连接到您的Twitch账号。\n3.如果您想挖掘所有掉宝,请不要勾选“仅参与优先掉宝游戏”,然后按“刷新”。\n4.如果您想优先挖掘特定游戏,请使用“优先掉宝游戏”列表设置您想挖掘的游戏的优先顺序。程序将尝试先挖掘列表顶部的游戏,然后再挖掘后边的游戏。\n5.勾选“仅参与优先掉宝游戏”时,不会挖掘不在优先列表中的游戏。勾不勾选取决于你。\n6。使用“不参与掉宝游戏”列表设置永不挖掘的游戏。\n7.更改任一列表的内容,或更改“仅参与优先掉宝游戏”勾选状态,需要手动按“刷新”才能使更改生效。", - "about": "About Twitch Drops Miner", - "about_text": "This application automatically mines timed Twitch drops without downloading stream data.", - "how_to_use": "How to Use", - "features": "Features", - "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "about": "关于 Twitch 掉宝矿工", + "about_text": "此应用程序可在不下载流数据的情况下自动挖掘定时 Twitch 掉宝。", + "how_to_use": "使用方法", + "features": "功能", + "important_notes": "重要提示", + "github_repo": "GitHub 仓库", + "how_to_use_items": [ + "使用您的 Twitch 账号登录(OAuth 设备代码流程)", + "在 twitch.tv/drops/campaigns 关联您的账号", + "矿工将自动发现活动并开始挖掘", + "在设置中配置优先游戏以关注您想要的内容", + "在主界面和库存选项卡中监控进度" + ], + "features_items": [ + "无流挖掘 - 节省带宽", + "游戏优先级和排除列表", + "同时跟踪最多 199 个频道", + "自动切换频道", + "实时进度跟踪" + ], + "important_notes_items": [ + "挖掘时请勿在同一账号上观看流", + "保护好您的 cookies.jar 文件", + "需要关联游戏账号才能掉宝" + ] }, "header": { "title": "Twitch Drops Miner", - "language": "Language:", - "initializing": "Initializing...", + "language": "语言:", + "initializing": "初始化中...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "已连接", + "disconnected": "已断开" } } } \ No newline at end of file diff --git a/lang/繁體中文.json b/lang/繁體中文.json index ea57906..561d340 100644 --- a/lang/繁體中文.json +++ b/lang/繁體中文.json @@ -1,4 +1,5 @@ { + "language_name": "繁體中文", "english_name": "Traditional Chinese", "status": { "terminated": "\n應用程式已終止,請關閉視窗以離開應用程式。", @@ -16,7 +17,14 @@ "incorrect_email_code": "電子郵件驗證碼錯誤。", "incorrect_twofa_code": "2FA 驗證碼不正確。", "email_code_required": "需要電子郵件驗證碼,請查看您的電子郵件信箱。", - "twofa_code_required": "需要 2FA 驗證碼。" + "twofa_code_required": "需要 2FA 驗證碼。", + "status": { + "logged_in": "已登入", + "logged_out": "未登入", + "logging_in": "正在登入…", + "required": "需要登入", + "waiting_auth": "Waiting for authentication..." + } }, "error": { "captcha": "您的登入被 CAPTCHA 拒絕,請於12小時後重試。", @@ -46,10 +54,6 @@ "login": { "name": "登入 Twitch", "labels": "狀態:\n用戶ID:", - "logged_in": "已登入", - "logged_out": "未登入", - "logging_in": "正在登入…", - "required": "需要登入", "request": "請先登入以繼續。", "username": "使用者名稱", "password": "密碼", @@ -122,7 +126,9 @@ "all_games_selected": "所有遊戲都已選擇或沒有可用遊戲。", "actions": "操作", "connection_quality": "連線品質:", - "minimum_refresh": "最小重新整理間隔(分鐘):" + "minimum_refresh": "最小重新整理間隔(分鐘):", + "dark_mode": "Dark Mode", + "reload_campaigns": "Reload Campaigns" }, "help": { "links": { @@ -137,14 +143,35 @@ "how_to_use": "How to Use", "features": "Features", "important_notes": "Important Notes", - "github_repo": "GitHub Repository" + "github_repo": "GitHub Repository", + "how_to_use_items": [ + "Login using your Twitch account (OAuth device code flow)", + "Link your accounts at twitch.tv/drops/campaigns", + "The miner will automatically discover campaigns and start mining", + "Configure priority games in Settings to focus on what you want", + "Monitor progress in the Main and Inventory tabs" + ], + "features_items": [ + "Stream-less drop mining - saves bandwidth", + "Game priority and exclusion lists", + "Tracks up to 199 channels simultaneously", + "Automatic channel switching", + "Real-time progress tracking" + ], + "important_notes_items": [ + "Do not watch streams on the same account while mining", + "Keep your cookies.jar file secure", + "Requires linked game accounts for drops" + ] }, "header": { "title": "Twitch Drops Miner", "language": "Language:", "initializing": "Initializing...", "auto_mode": "AUTO", - "manual_mode": "MANUAL" + "manual_mode": "MANUAL", + "connected": "Connected", + "disconnected": "Disconnected" } } } \ No newline at end of file diff --git a/src/auth/auth_state.py b/src/auth/auth_state.py index aa4e459..a262580 100644 --- a/src/auth/auth_state.py +++ b/src/auth/auth_state.py @@ -232,7 +232,7 @@ class _AuthState: # looks like we're missing something login_form: LoginForm = self._twitch.gui.login logger.info("Checking login") - login_form.update(_("gui", "login", "logging_in"), None) + login_form.update(_("login", "status", "logging_in"), None) for _client_mismatch_attempt in range(2): for _invalid_token_attempt in range(2): cookie = jar.filter_cookies(client_info.CLIENT_URL) @@ -271,7 +271,7 @@ class _AuthState: self.user_id = int(validate_response["user_id"]) cookie["persistent"] = str(self.user_id) logger.info(f"Login successful, user ID: {self.user_id}") - login_form.update(_("gui", "login", "logged_in"), self.user_id) + login_form.update(_("login", "status", "logged_in"), self.user_id) # update our cookie and save it jar.update_cookies(cookie, client_info.CLIENT_URL) jar.save(COOKIES_PATH) diff --git a/src/i18n/__init__.py b/src/i18n/__init__.py index ce22454..77fcc32 100644 --- a/src/i18n/__init__.py +++ b/src/i18n/__init__.py @@ -23,7 +23,6 @@ from .translator import ( Translation, Translator, _, - default_translation, ) @@ -46,7 +45,6 @@ __all__ = [ "GUIHeader", "GUIMessages", "Translation", - "default_translation", "Translator", "_", ] diff --git a/src/i18n/translator.py b/src/i18n/translator.py index bf9a7bc..e30414b 100644 --- a/src/i18n/translator.py +++ b/src/i18n/translator.py @@ -1,11 +1,12 @@ from __future__ import annotations +import json from collections import abc -from typing import TYPE_CHECKING, Any, TypedDict +from typing import TYPE_CHECKING, Any, TypedDict, cast from src.config import DEFAULT_LANG, LANG_PATH from src.exceptions import MinerException -from src.utils.json_utils import json_load, json_save +from src.utils.json_utils import json_load if TYPE_CHECKING: @@ -22,6 +23,14 @@ class StatusMessages(TypedDict): no_campaign: str +class LoginStatus(TypedDict): + logged_in: str + logged_out: str + logging_in: str + required: str + waiting_auth: str + + class LoginMessages(TypedDict): error_code: str unexpected_content: str @@ -30,6 +39,7 @@ class LoginMessages(TypedDict): incorrect_login_pass: str incorrect_email_code: str incorrect_twofa_code: str + status: LoginStatus class ErrorMessages(TypedDict): @@ -62,11 +72,7 @@ class GUITabs(TypedDict): class GUILoginForm(TypedDict): name: str labels: str - logging_in: str - logged_in: str - logged_out: str request: str - required: str username: str password: str twofa_code: str @@ -133,8 +139,10 @@ class GUISettingsGeneral(TypedDict): class GUISettings(TypedDict): - general: GUISettingsGeneral + general: str + dark_mode: str reload: str + reload_campaigns: str games_to_watch: str games_help: str search_games: str @@ -163,8 +171,11 @@ class GUIHelp(TypedDict): about: str about_text: str how_to_use: str + how_to_use_items: list[str] features: str + features_items: list[str] important_notes: str + important_notes_items: list[str] github_repo: str @@ -174,6 +185,8 @@ class GUIHeader(TypedDict): initializing: str auto_mode: str manual_mode: str + connected: str + disconnected: str class GUIMessages(TypedDict): @@ -191,7 +204,7 @@ class GUIMessages(TypedDict): class Translation(TypedDict): - language_name: NotRequired[str] + language_name: str english_name: str status: StatusMessages login: LoginMessages @@ -199,202 +212,47 @@ class Translation(TypedDict): gui: GUIMessages -default_translation: Translation = { - "english_name": "English", - "status": { - "terminated": "\nApplication Terminated.\nClose the window to exit the application.", - "watching": "Watching: {channel}", - "goes_online": "{channel} goes ONLINE, switching...", - "goes_offline": "{channel} goes OFFLINE, switching...", - "claimed_drop": "Claimed drop: {drop}", - "no_channel": "No available channels to watch. Waiting for an ONLINE channel...", - "no_campaign": "No active campaigns to mine drops for. Waiting for an active campaign...", - }, - "login": { - "unexpected_content": ( - "Unexpected content type returned, usually due to being redirected. " - "Do you need to login for internet access?" - ), - "error_code": "Login error code: {error_code}", - "incorrect_login_pass": "Incorrect username or password.", - "incorrect_email_code": "Incorrect email code.", - "incorrect_twofa_code": "Incorrect 2FA code.", - "email_code_required": "Email code required. Check your email.", - "twofa_code_required": "2FA token required.", - }, - "error": { - "captcha": "Your login attempt was denied by CAPTCHA.\nPlease try again in 12+ hours.", - "site_down": "Twitch is down, retrying in {seconds} seconds...", - "no_connection": "Cannot connect to Twitch, retrying in {seconds} seconds...", - }, - "gui": { - "output": "Output", - "status": { - "name": "Status", - "idle": "Idle", - "ready": "Ready", - "exiting": "Exiting...", - "terminated": "Terminated", - "cleanup": "Cleaning up channels...", - "gathering": "Gathering channels...", - "switching": "Switching the channel...", - "fetching_inventory": "Fetching inventory...", - "fetching_campaigns": "Fetching campaigns...", - "adding_campaigns": "Adding campaigns to inventory... {counter}", - }, - "tabs": { - "main": "Main", - "inventory": "Inventory", - "settings": "Settings", - "help": "Help", - }, - "login": { - "name": "Login Form", - "labels": "Status:\nUser ID:", - "logged_in": "Logged in", - "logged_out": "Logged out", - "logging_in": "Logging in...", - "required": "Login required", - "request": "Please log in to continue.", - "username": "Username", - "password": "Password", - "twofa_code": "2FA code (optional)", - "button": "Login", - "oauth_prompt": "Enter this code at:", - "oauth_activate": "Twitch Activate", - "oauth_confirm": "I've entered the code", - }, - "websocket": { - "name": "Websocket Status", - "websocket": "Websocket #{id}:", - "initializing": "Initializing...", - "connected": "Connected", - "disconnected": "Disconnected", - "connecting": "Connecting...", - "disconnecting": "Disconnecting...", - "reconnecting": "Reconnecting...", - }, - "progress": { - "name": "Campaign Progress", - "drop": "Drop:", - "game": "Game:", - "campaign": "Campaign:", - "remaining": "{time} remaining", - "drop_progress": "Progress:", - "campaign_progress": "Progress:", - "no_drop": "No active drop", - "return_to_auto": "Return to Auto Mode", - "manual_mode_info": "Manual Mode: Mining", - }, - "channels": { - "name": "Channels", - "online": "ONLINE ✔", - "pending": "OFFLINE ⏳", - "offline": "OFFLINE ❌", - "no_channels": "No channels tracked yet...", - "no_channels_for_games": "No channels found for selected games...", - "channel_count": "channel", - "channel_count_plural": "channels", - "viewers": "viewers", - }, - "inventory": { - "no_campaigns": "No campaigns loaded yet...", - "status": { - "active": "Active ✔", - "upcoming": "Upcoming ⏳", - "expired": "Expired ❌", - "claimed": "Claimed ✔", - }, - "starts": "Starts: {time}", - "ends": "Ends: {time}", - "claimed_drops": "claimed", - }, - "settings": { - "general": { - "name": "General", - "dark_mode": "Dark mode: ", - }, - "reload": "Reload", - "games_to_watch": "Games to Watch", - "games_help": "Select games to watch. Order matters - drag to reorder priority (top = highest priority).", - "search_games": "Search games...", - "select_all": "Select All", - "deselect_all": "Deselect All", - "selected_games": "Selected Games (drag to reorder)", - "available_games": "Available Games", - "no_games_selected": "No games selected. Check games below to add them.", - "no_games_match": "No games match your search.", - "all_games_selected": "All games are selected or no games available.", - "actions": "Actions", - "connection_quality": "Connection Quality:", - "minimum_refresh": "Minimum Refresh Interval (minutes):", - }, - "help": { - "links": { - "name": "Useful Links", - }, - "how_it_works": "How It Works", - "how_it_works_text": ( - "Every several seconds, the application pretends to watch a particular stream " - "by fetching stream metadata - this is enough to advance the drops. " - "Note that this completely bypasses the need to download " - "any actual stream of video and sound. " - "To keep the status (ONLINE or OFFLINE) of the channels up-to-date, " - "there's a websocket connection established that receives events about streams " - "going up or down, or updates regarding the current number of viewers." - ), - "getting_started": "Getting Started", - "getting_started_text": ( - "1. Login to the application.\n" - "2. Ensure your Twitch account is linked to all campaigns " - "you're interested in mining.\n" - "3. If you're interested in mining everything possible, " - 'change the Priority Mode to anything other than "Priority list only" ' - 'and press on "Reload".\n' - '4. If you want to mine specific games first, use the "Priority" list ' - "to set up an ordered list of games of your choice. " - "Games from the top of the list will be attempted to be mined first, " - "before the ones lower down the list.\n" - '5. Keep the "Priority mode" selected as "Priority list only", ' - "to avoid mining games that are not on the priority list. " - "Or not - it's up to you.\n" - '6. Use the "Exclude" list to tell the application ' - "which games should never be mined.\n" - "7. Changing the contents of either of the lists, or changing " - 'the "Priority mode", requires you to press on "Reload" ' - "for the changes to take an effect." - ), - "about": "About Twitch Drops Miner", - "about_text": "This application automatically mines timed Twitch drops without downloading stream data.", - "how_to_use": "How to Use", - "features": "Features", - "important_notes": "Important Notes", - "github_repo": "GitHub Repository", - }, - "header": { - "title": "Twitch Drops Miner", - "language": "Language:", - "initializing": "Initializing...", - "auto_mode": "AUTO", - "manual_mode": "MANUAL", - }, - }, -} +# Load English translation from JSON file (single source of truth) +def _load_english_translation() -> Translation: + """Load the English translation from lang/English.json. + + This is the fallback translation used when other translations are missing keys. + """ + english_path = LANG_PATH / "English.json" + try: + with open(english_path, "r", encoding="utf-8") as f: + return cast(Translation, json.load(f)) + except Exception as e: + raise MinerException( + f"Failed to load English translation from {english_path}: {e}" + ) from e + + +# Module-level English translation (loaded once at import time) +_english_translation = _load_english_translation() class Translator: def __init__(self) -> None: self._langs: list[str] = [] - # start with (and always copy) the default translation - self._translation: Translation = default_translation.copy() - # if we're in dev, update the template English.json file - default_langpath = LANG_PATH.joinpath(f"{DEFAULT_LANG}.json") - json_save(default_langpath, default_translation) + # start with English translation (loaded from JSON) + self._translation: Translation = _english_translation.copy() self._translation["language_name"] = DEFAULT_LANG - # load available translation names + # load available languages from JSON files by reading language_name field for filepath in LANG_PATH.glob("*.json"): - self._langs.append(filepath.stem) + try: + with open(filepath, "r", encoding="utf-8") as f: + data = json.load(f) + if "language_name" in data: + self._langs.append(data["language_name"]) + else: + # fallback to filename if language_name is missing + self._langs.append(filepath.stem) + except Exception: + # if we can't read the file, skip it + continue self._langs.sort() + # ensure DEFAULT_LANG is first in the list if DEFAULT_LANG in self._langs: self._langs.remove(DEFAULT_LANG) self._langs.insert(0, DEFAULT_LANG) @@ -414,15 +272,22 @@ class Translator: # same language as loaded selected return elif language == DEFAULT_LANG: - # default language selected - use the memory value - self._translation = default_translation.copy() + # default language selected - use English from JSON + self._translation = _english_translation.copy() + self._translation["language_name"] = DEFAULT_LANG else: - self._translation = json_load( - LANG_PATH.joinpath(f"{language}.json"), default_translation - ) - if "language_name" in self._translation: - raise ValueError("Translations cannot define 'language_name'") - self._translation["language_name"] = language + # find the JSON file with matching language_name field + for filepath in LANG_PATH.glob("*.json"): + try: + with open(filepath, "r", encoding="utf-8") as f: + data = json.load(f) + if data.get("language_name") == language: + self._translation = json_load(filepath, _english_translation) + return + except Exception: + continue + # if we can't find a matching file, raise an error + raise ValueError(f"Cannot find translation file for language: {language}") def __call__(self, *path: str) -> str: if not path: diff --git a/src/web/app.py b/src/web/app.py index 1eb51cf..5cbe00a 100644 --- a/src/web/app.py +++ b/src/web/app.py @@ -201,8 +201,11 @@ async def get_translations(): "login": { "title": _("gui", "login", "name"), "status_label": _("gui", "login", "labels").split("\n")[0].rstrip(":"), - "logged_in": _("gui", "login", "logged_in"), - "not_logged_in": _("gui", "login", "logged_out"), + "logged_in": _("login", "status", "logged_in"), + "not_logged_in": _("login", "status", "logged_out"), + "logging_in": _("login", "status", "logging_in"), + "required": _("login", "status", "required"), + "request": _("gui", "login", "request"), "username": _("gui", "login", "username"), "password": _("gui", "login", "password"), "twofa_code": _("gui", "login", "twofa_code"), @@ -251,8 +254,8 @@ async def get_translations(): }, "settings": { "title": _("gui", "tabs", "settings"), - "general": _("gui", "settings", "general", "name"), - "dark_mode": _("gui", "settings", "general", "dark_mode").rstrip(": "), + "general": _("gui", "settings", "general"), + "dark_mode": _("gui", "settings", "dark_mode"), "games_to_watch": _("gui", "settings", "games_to_watch"), "games_help": _("gui", "settings", "games_help"), "search_games": _("gui", "settings", "search_games"), @@ -264,7 +267,7 @@ async def get_translations(): "no_games_match": _("gui", "settings", "no_games_match"), "all_games_selected": _("gui", "settings", "all_games_selected"), "actions": _("gui", "settings", "actions"), - "reload_campaigns": _("gui", "settings", "reload"), + "reload_campaigns": _("gui", "settings", "reload_campaigns"), "connection_quality": _("gui", "settings", "connection_quality"), "minimum_refresh": _("gui", "settings", "minimum_refresh"), }, @@ -273,12 +276,15 @@ async def get_translations(): "about": _("gui", "help", "about"), "about_text": _("gui", "help", "about_text"), "how_to_use": _("gui", "help", "how_to_use"), + "how_to_use_items": list(_._translation["gui"]["help"]["how_to_use_items"]), "how_it_works": _("gui", "help", "how_it_works"), "how_it_works_text": _("gui", "help", "how_it_works_text"), "getting_started": _("gui", "help", "getting_started"), "getting_started_text": _("gui", "help", "getting_started_text"), "features": _("gui", "help", "features"), + "features_items": list(_._translation["gui"]["help"]["features_items"]), "important_notes": _("gui", "help", "important_notes"), + "important_notes_items": list(_._translation["gui"]["help"]["important_notes_items"]), "useful_links": _("gui", "help", "links", "name"), "github_repo": _("gui", "help", "github_repo"), }, diff --git a/src/web/managers/login.py b/src/web/managers/login.py index baf0d3a..930b692 100644 --- a/src/web/managers/login.py +++ b/src/web/managers/login.py @@ -35,7 +35,7 @@ class LoginFormManager: self._manager = manager self._login_event = asyncio.Event() self._login_data: LoginData | None = None - self._status = "Logged out" + self._status = _("login", "status", "logged_out") self._user_id: int | None = None self._oauth_pending: dict[str, str] | None = ( None # Store OAuth code for late-connecting clients @@ -68,19 +68,6 @@ class LoginFormManager: self._broadcaster.emit("login_status", {"status": status, "user_id": user_id}) ) - async def ask_login(self) -> LoginData: - """Request login credentials from the user. - - Returns: - LoginData containing submitted credentials - """ - self.update(_("gui", "login", "required"), None) - self._login_event.clear() - await self._broadcaster.emit("login_required", {}) - # Wait for user to submit login (will be cancelled on shutdown) - await self._login_event.wait() - return self._login_data - async def ask_enter_code(self, page_url, user_code: str): """Request OAuth device code entry from the user. @@ -91,7 +78,7 @@ class LoginFormManager: page_url: URL where user should enter the code (e.g., twitch.tv/activate) user_code: The device code to enter """ - self.update(_("gui", "login", "required"), None) + self.update(_("login", "status", "required"), None) self._login_event.clear() # Store OAuth code for late-connecting clients self._oauth_pending = {"url": str(page_url), "code": user_code} diff --git a/web/index.html b/web/index.html index 99aedd5..664a539 100644 --- a/web/index.html +++ b/web/index.html @@ -46,7 +46,7 @@

Login

-
Not logged in
+