Add translation management tool suite and update Chinese translations

- Introduced a comprehensive guide for the translation management tool suite, detailing components, usage, and best practices.
- Added `translation-manager.py`, `translation-cleaner.py`, and `manage-translations.sh` scripts for managing translations.
- Updated the Chinese translation file (`zh_CN.json`) to improve existing translations and remove unused keys.
- Enhanced documentation with examples and troubleshooting tips for better user experience.
This commit is contained in:
月月
2025-06-17 14:29:33 +08:00
parent b32734b9f5
commit 89e25e3504
8 changed files with 1348 additions and 661 deletions
+18 -314
View File
@@ -35,7 +35,6 @@
"Closes right sidebar on press": "Closes right sidebar on press",
"Copy": "Copy",
"Copy code": "Copy code",
"Ctrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Ctrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window",
"Current API endpoint: {0}\nSet it with {1}mode PROVIDER": "Current API endpoint: {0}\nSet it with {1}mode PROVIDER",
"Current model: {0}\nSet it with {1}model MODEL": "Current model: {0}\nSet it with {1}model MODEL",
"Decrease brightness": "Decrease brightness",
@@ -48,7 +47,6 @@
"Download complete": "Download complete",
"Edit": "Edit",
"Enter text to translate...": "Enter text to translate...",
"Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly",
"Finished tasks will go here": "Finished tasks will go here",
"For desktop wallpapers | Good quality": "For desktop wallpapers | Good quality",
"For storing API keys and other sensitive information": "For storing API keys and other sensitive information",
@@ -63,10 +61,7 @@
"Input": "Input",
"Intelligence": "Intelligence",
"Interface": "Interface",
"Interrupts possibility of overview being toggled on release. ": "Interrupts possibility of overview being toggled on release. ",
"Invalid API provider. Supported: \n- ": "Invalid API provider. Supported: \n- ",
"Invalid arguments. Must provide `key` and `value`.": "Invalid arguments. Must provide `key` and `value`.",
"Invalid model. Supported: \n```\n": "Invalid model. Supported: \n```\n",
"Jump to current month": "Jump to current month",
"Keep system awake": "Keep system awake",
"Large images | God tier quality, no NSFW.": "Large images | God tier quality, no NSFW.",
@@ -86,8 +81,6 @@
"Nothing here!": "Nothing here!",
"Notifications": "Notifications",
"OK": "OK",
"Online via {0} | {1}'s model": "Online via {0} | {1}'s model",
"Online | Google's model\nGives up-to-date information with search.": "Online | Google's model\nGives up-to-date information with search.",
"Open file link": "Open file link",
"Opens cheatsheet on press": "Opens cheatsheet on press",
"Opens left sidebar on press": "Opens left sidebar on press",
@@ -98,7 +91,6 @@
"Output": "Output",
"Page {0}": "Page {0}",
"Plasma Settings": "Plasma Settings",
"Provider set to ": "Provider set to ",
"Reboot": "Reboot",
"Reboot to firmware settings": "Reboot to firmware settings",
"Reload Hyprland & Quickshell": "Reload Hyprland & Quickshell",
@@ -122,19 +114,15 @@
"Shutdown": "Shutdown",
"Silent": "Silent",
"Sleep": "Sleep",
"Switched to search mode. Continue with the user's request.": "Switched to search mode. Continue with the user's request.",
"System": "System",
"Task Manager": "Task Manager",
"Task description": "Task description",
"Temperature must be between 0 and 2": "Temperature must be between 0 and 2",
"Temperature set to {0}": "Temperature set to {0}",
"Temperature: {0}": "Temperature: {0}",
"That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number",
"The current API used. Endpoint: ": "The current API used. Endpoint: ",
"The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "The hentai one | Great quantity, a lot of NSFW, quality varies wildly",
"The popular one | Best quantity, but quality can vary wildly": "The popular one | Best quantity, but quality can vary wildly",
"Thinking": "Thinking",
"This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ": "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ",
"To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.": "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.",
"Toggle clipboard query on overview widget": "Toggle clipboard query on overview widget",
"Toggle emoji query on overview widget": "Toggle emoji query on overview widget",
@@ -155,318 +143,34 @@
"Unknown Album": "Unknown Album",
"Unknown Artist": "Unknown Artist",
"Unknown Title": "Unknown Title",
"Unknown command: ": "Unknown command: ",
"Unknown function call: {0}": "Unknown function call: {0}",
"Uptime: {0}": "Uptime: {0}",
"Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in **bold** to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`. When making changes to the user's config, you must get the config to know what values there are before setting.": "Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in **bold** to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`. When making changes to the user's config, you must get the config to know what values there are before setting.",
"View Markdown source": "View Markdown source",
"Volume": "Volume",
"Volume mixer": "Volume mixer",
"Waifus only | Excellent quality, limited quantity": "Waifus only | Excellent quality, limited quantity",
"Waiting for response...": "Waiting for response...",
"Workspace": "Workspace",
"\nSet with /mode PROVIDER": "\nSet with /mode PROVIDER",
"about": "About",
"accessed": "Accessed",
"account": "Account",
"active": "Active",
"active_state": "Active",
"addon": "Add-on",
"addons": "Add-ons",
"address": "Address",
"admin": "Administrator",
"align": "Align",
"always_on_top": "Always on Top",
"animate": "Animate",
"app": "App",
"appearance": "Appearance",
"application": "Application",
"applications": "Applications",
"apply": "Apply",
"apps": "Apps",
"auto": "Auto",
"background": "Background",
"balanced": "Balanced",
"bar": "Bar",
"bars": "Bars",
"battery": "Battery",
"bluetooth": "Bluetooth",
"blur": "Blur",
"border": "Border",
"bottom": "Bottom",
"brightness": "Brightness",
"bring_to_front": "Bring to Front",
"bytes": "{0} bytes",
"cancel": "Cancel",
"center": "Center",
"characters": "{0} characters",
"charging": "Charging",
"clear_all": "Clear All",
"clicked": "Clicked",
"close": "Close",
"collapse": "Collapse",
"color": "Color",
"command": "Command",
"connected": "Connected",
"connecting": "Connecting...",
"context_menu": "Context Menu",
"copy": "Copy",
"cpu": "CPU",
"created": "Created",
"critical": "Critical",
"customize": "Customize",
"cut": "Cut",
"dark": "Dark",
"dashboard": "Dashboard",
"date": "Date",
"debug": "Debug",
"default": "Default",
"degrees": "{0}°",
"delete": "Delete",
"demo": "Demo",
"desktop": "Desktop",
"dialog": "Dialog",
"disabled": "Disabled",
"discharging": "Discharging",
"disconnected": "Disconnected",
"disk": "Disk",
"display": "Display",
"distribute": "Distribute",
"dock": "Dock",
"documents": "Documents",
"double_clicked": "Double Clicked",
"downloads": "Downloads",
"dragged": "Dragged",
"dropdown": "Dropdown",
"dropped": "Dropped",
"effect": "Effect",
"email": "Email",
"enabled": "Enabled",
"error": "Error",
"ethernet": "Ethernet",
"example": "Example",
"execute": "Execute",
"exit": "Exit",
"expand": "Expand",
"extension": "Extension",
"extensions": "Extensions",
"fan": "Fan",
"favorites": "Favorites",
"file": "File",
"files": "Files",
"filter": "Filter",
"flip": "Flip",
"floating": "Floating",
"focus": "Focus",
"folder": "Folder",
"folders": "Folders",
"foreground": "Foreground",
"format": "Format",
"full": "Full",
"fullscreen": "Fullscreen",
"gigabytes": "{0} GB",
"gigahertz": "{0} GHz",
"glow": "Glow",
"group": "Group",
"guest": "Guest",
"headphones": "Headphones",
"help": "Help",
"hertz": "{0} Hz",
"hibernate": "Hibernate",
"hidden": "Hidden",
"hide": "Hide",
"highlight": "Highlight",
"hint": "Hint",
"home": "Home",
"hour": "Hour",
"hours": "Hours",
"hover": "Hover",
"inactive": "Inactive",
"info": "Info",
"input": "Input",
"install": "Install",
"justify": "Justify",
"keybinds": "Key Bindings",
"keyboard": "Keyboard",
"kilobytes": "{0} KB",
"kilohertz": "{0} kHz",
"landscape": "Landscape",
"language": "Language",
"launcher": "Launcher",
"left": "Left",
"light": "Light",
"loading": "Loading...",
"lock": "Lock",
"log": "Log",
"logout": "Logout",
"long_pressed": "Long Pressed",
"low": "Low",
"manual": "Manual",
"margin": "Margin",
"maximize": "Maximize",
"megabytes": "{0} MB",
"megahertz": "{0} MHz",
"memory": "Memory",
"menu": "Menu",
"menubar": "Menu Bar",
"microphone": "Microphone",
"minimize": "Minimize",
"minute": "Minute",
"minutes": "Minutes",
"mirror": "Mirror",
"modal": "Modal",
"modified": "Modified",
"monitor": "Monitor",
"mouse": "Mouse",
"move": "Move",
"music": "Music",
"mute": "Mute",
"network": "Network",
"next": "Next",
"no": "No",
"no_notifications": "No notifications",
"notifications": "Notifications",
"off": "Off",
"ok": "OK",
"on": "On",
"opacity": "Opacity",
"open": "Open",
"orientation": "Orientation",
"outline": "Outline",
"output": "Output",
"overview": "Overview",
"owner": "Owner",
"package": "Package",
"packages": "Packages",
"padding": "Padding",
"panel": "Panel",
"panels": "Panels",
"panned": "Panned",
"password": "Password",
"paste": "Paste",
"pause": "Pause",
"percent": "{0}%",
"performance": "Performance",
"permissions": "Permissions",
"phone": "Phone",
"pictures": "Pictures",
"pinched": "Pinched",
"pinned": "Pinned",
"pixels": "{0}px",
"placeholder": "Placeholder",
"play": "Play",
"plugin": "Plugin",
"plugins": "Plugins",
"popup": "Popup",
"portrait": "Portrait",
"power": "Power",
"power_saver": "Power Saver",
"pressed": "Pressed",
"previous": "Previous",
"profile": "Profile",
"properties": "Properties",
"quiet": "Quiet",
"quit": "Quit",
"read": "Read",
"recent": "Recent",
"recording": "Recording",
"refresh": "Refresh",
"refresh_rate": "Refresh Rate",
"reload": "Reload",
"rename": "Rename",
"reset": "Reset",
"resize": "Resize",
"resolution": "Resolution",
"restart": "Restart",
"restore": "Restore",
"right": "Right",
"right_clicked": "Right Clicked",
"rotate": "Rotate",
"rotated": "Rotated",
"sample": "Sample",
"save": "Save",
"scale": "Scale",
"screenshot": "Screenshot",
"scrolled": "Scrolled",
"search": "Search",
"second": "Second",
"seconds": "Seconds",
"selection": "Selection",
"send_to_back": "Send to Back",
"settings": "Settings",
"shadow": "Shadow",
"shortcuts": "Shortcuts",
"show": "Show",
"shutdown": "Shutdown",
"sidebar": "Sidebar",
"silent": "Silent",
"size": "Size",
"snapped": "Snapped",
"software": "Software",
"space": "Space",
"speaker": "Speaker",
"statusbar": "Status Bar",
"sticky": "Sticky",
"stop": "Stop",
"style": "Style",
"success": "Success",
"suspend": "Suspend",
"swiped": "Swiped",
"systray": "System Tray",
"tapped": "Tapped",
"taskbar": "Taskbar",
"temperature": "Temperature",
"terabytes": "{0} TB",
"terminal": "Terminal",
"test": "Test",
"theme": "Theme",
"tiled": "Tiled",
"time": "Time",
"tips": "Tips",
"today": "Today",
"tomorrow": "Tomorrow",
"toolbar": "Toolbar",
"tooltip": "Tooltip",
"top": "Top",
"touchpad": "Touchpad",
"trace": "Trace",
"transform": "Transform",
"transition": "Transition",
"transparency": "Transparency",
"trash": "Trash",
"tutorial": "Tutorial",
"type": "Type",
"uninstall": "Uninstall",
"unlock": "Unlock",
"unmute": "Unmute",
"unpinned": "Unpinned",
"unsticky": "Unsticky",
"update": "Update",
"upgrade": "Upgrade",
"user": "User",
"username": "Username",
"verbose": "Verbose",
"version": "Version",
"videos": "Videos",
"visible": "Visible",
"volume": "Volume",
"warning": "Warning",
"welcome": "Welcome",
"widget": "Widget",
"widgets": "Widgets",
"wifi": "Wi-Fi",
"window": "Window",
"windowed": "Windowed",
"windows": "Windows",
"workspace": "Workspace",
"workspaces": "Workspaces",
"write": "Write",
"yes": "Yes",
"yesterday": "Yesterday",
"zoomed": "Zoomed",
"{0} (copied)": "{0} (copied)",
"{0} Safe Storage": "{0} Safe Storage",
"{0} does not require an API key": "{0} does not require an API key",
"{0} queries pending": "{0} queries pending",
"{0} | Right-click to configure": "{0} | Right-click to configure"
"{0} | Right-click to configure": "{0} | Right-click to configure",
"Set with /mode PROVIDER": "Set with /mode PROVIDER",
"Invalid API provider. Supported: \n-": "Invalid API provider. Supported: \n-",
"Unknown command:": "Unknown command:",
"Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window",
"This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key.": "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key.",
"The current API used. Endpoint:": "The current API used. Endpoint:",
"Provider set to": "Provider set to",
"Invalid model. Supported: \n```": "Invalid model. Supported: \n```",
"Interrupts possibility of overview being toggled on release.": "Interrupts possibility of overview being toggled on release.",
"Enter tags, or \"{0}\" for commands": "Enter tags, or \"{0}\" for commands",
"That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number",
"Online | Google's model\nGives up-to-date information with search.": "Online | Google's model\nGives up-to-date information with search.",
"Online via {0} | {1}'s model": "Online via {0} | {1}'s model",
"Switched to search mode. Continue with the user's request.": "Switched to search mode. Continue with the user's request.",
"Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly",
"Message the model... \"{0}\" for commands": "Message the model... \"{0}\" for commands",
"To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For {0}:\n\n**Link**: {1}\n\n{2}": "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For {0}:\n\n**Link**: {1}\n\n{2}"
}
@@ -0,0 +1,111 @@
# Translation Management Tools
This directory contains a toolset for managing project translation files.
## Directory Structure
```
translations/
├── tools/ # Translation management tools directory
│ ├── translation-manager.py # Main translation manager
│ ├── translation-cleaner.py # Translation maintenance tool
│ ├── manage-translations.sh # Convenient wrapper script
│ ├── translation-tools-guide.md # Detailed usage documentation
│ └── README.md # This file
├── en_US.json # English translation file
├── zh_CN.json # Chinese translation file
└── ... # Other language files
```
## Quick Start
### Running from tools directory
```bash
# Enter tools directory
cd .config/quickshell/translations/tools
# Check current translation status
./manage-translations.sh status
# Update all translation files
./manage-translations.sh update
# Update specific language
./manage-translations.sh update -l zh_CN
# Clean unused keys
./manage-translations.sh clean
# Sync all language files
./manage-translations.sh sync
```
### Running from project root directory
```bash
# Run from project root directory (recommended to use relative paths)
.config/quickshell/translations/tools/manage-translations.sh status
.config/quickshell/translations/tools/manage-translations.sh update
```
## Tool Description
### 🛠️ `manage-translations.sh` - Main Entry Point
Convenient command-line interface that integrates all translation management functions.
### 🔍 `translation-manager.py` - Core Manager
- Extract translatable texts
- Compare translation file differences
- Interactive translation updates
### 🧹 `translation-cleaner.py` - Maintenance Tool
- Clean unused translation keys
- Sync language file structure
- Create backup files
## Common Workflows
### After adding new translatable texts
```bash
./manage-translations.sh update
```
### Clean up after code refactoring
```bash
./manage-translations.sh clean
```
### Add new language
```bash
./manage-translations.sh update -l new_language_code
```
### Check translation status
```bash
./manage-translations.sh status
```
## Documentation
- 📖 [Detailed Usage Guide](./translation-tools-guide.md)
## Important Notes
1. **Running Location**: Tools automatically detect relative paths, can be run from tools directory or project root
2. **Backup**: Cleanup operations automatically create backup files
3. **Encoding**: All files use UTF-8 encoding
4. **Permissions**: Ensure scripts have execution permissions
## Supported Translation Formats
The tool recognizes translatable texts in the following formats:
```qml
qsTr("Your text here")
qsTr('Single quotes work too')
i18n.t("JavaScript translations")
```
---
If you have any issues, please refer to the detailed documentation or check error messages in script output.
@@ -0,0 +1,149 @@
#!/bin/bash
# Translation management script - convenient wrapper
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TRANSLATIONS_DIR="$(dirname "$SCRIPT_DIR")"
SOURCE_DIR="$(dirname "$(dirname "$TRANSLATIONS_DIR")")"
show_help() {
echo "Translation Management Tool - Convenient Wrapper"
echo ""
echo "Usage: $0 [options] <command>"
echo ""
echo "Commands:"
echo " extract Extract translatable texts to temporary file"
echo " update Update translation files (add missing/remove extra keys)"
echo " clean Clean unused translation keys"
echo " sync Sync keys across all language files"
echo " status Show translation status"
echo ""
echo "Options:"
echo " -l, --lang LANG Specify language (e.g.: zh_CN)"
echo " -t, --trans-dir DIR Translation files directory (default: $TRANSLATIONS_DIR)"
echo " -s, --source-dir DIR Source code directory (default: $SOURCE_DIR)"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
echo " $0 extract # Extract translatable texts"
echo " $0 update -l zh_CN # Update Chinese translations"
echo " $0 update # Update all translations"
echo " $0 clean # Clean unused keys"
echo " $0 sync # Sync keys across all languages"
echo " $0 status # Show translation status"
}
show_status() {
echo "Analyzing translation status..."
# Extract current text count
echo "=== Current Project Status ==="
python3 "$SCRIPT_DIR/translation-manager.py" \
--translations-dir "$TRANSLATIONS_DIR" \
--source-dir "$SOURCE_DIR" \
--extract-only | grep "Extracted"
echo ""
echo "=== Translation File Status ==="
if [ -d "$TRANSLATIONS_DIR" ]; then
for file in "$TRANSLATIONS_DIR"/*.json; do
if [ -f "$file" ]; then
lang=$(basename "$file" .json)
count=$(jq 'length' "$file" 2>/dev/null || echo "error")
echo " $lang: $count keys"
fi
done
else
echo " Translation directory does not exist: $TRANSLATIONS_DIR"
fi
}
# Parse command line arguments
LANG_CODE=""
COMMAND=""
while [[ $# -gt 0 ]]; do
case $1 in
-l|--lang)
LANG_CODE="$2"
shift 2
;;
-t|--trans-dir)
TRANSLATIONS_DIR="$2"
shift 2
;;
-s|--source-dir)
SOURCE_DIR="$2"
shift 2
;;
-h|--help)
show_help
exit 0
;;
extract|update|clean|sync|status)
if [ -n "$COMMAND" ]; then
echo "Error: Only one command can be specified"
exit 1
fi
COMMAND="$1"
shift
;;
*)
echo "Unknown option: $1"
show_help
exit 1
;;
esac
done
if [ -z "$COMMAND" ]; then
echo "Error: A command must be specified"
show_help
exit 1
fi
# Check dependencies
if ! command -v python3 >/dev/null 2>&1; then
echo "Error: python3 is required"
exit 1
fi
if [ "$COMMAND" = "status" ] && ! command -v jq >/dev/null 2>&1; then
echo "Warning: jq is not installed, status display may be incomplete"
fi
# Build base arguments
BASE_ARGS="--translations-dir $TRANSLATIONS_DIR --source-dir $SOURCE_DIR"
case $COMMAND in
extract)
echo "Extracting translatable texts..."
python3 "$SCRIPT_DIR/translation-manager.py" $BASE_ARGS --extract-only --show-temp
;;
update)
echo "Updating translation files..."
if [ -n "$LANG_CODE" ]; then
python3 "$SCRIPT_DIR/translation-manager.py" $BASE_ARGS --language "$LANG_CODE"
else
python3 "$SCRIPT_DIR/translation-manager.py" $BASE_ARGS
fi
;;
clean)
echo "Cleaning unused translation keys..."
python3 "$SCRIPT_DIR/translation-cleaner.py" $BASE_ARGS --clean
;;
sync)
echo "Syncing translation keys..."
python3 "$SCRIPT_DIR/translation-cleaner.py" $BASE_ARGS --sync
;;
status)
show_status
;;
*)
echo "Unknown command: $COMMAND"
show_help
exit 1
;;
esac
@@ -0,0 +1,196 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Translation File Maintenance Helper
Used to clean and organize translation files, removing unused keys
"""
import os
import sys
import json
import argparse
import importlib.util
from pathlib import Path
from typing import Dict, Set, List
# Import from the same directory using importlib
current_dir = os.path.dirname(os.path.abspath(__file__))
manager_path = os.path.join(current_dir, 'translation-manager.py')
spec = importlib.util.spec_from_file_location("translation_manager", manager_path)
translation_manager = importlib.util.module_from_spec(spec)
spec.loader.exec_module(translation_manager)
TranslationManager = translation_manager.TranslationManager
def clean_translation_files(translations_dir: str, source_dir: str, backup: bool = True):
"""Clean translation files by removing unused keys"""
print("Starting translation file cleanup...")
# Create manager
manager = TranslationManager(translations_dir, source_dir)
# Extract currently used texts
print("Extracting currently used translatable texts...")
current_texts = manager.extract_translatable_texts()
print(f"Extracted {len(current_texts)} currently used texts")
# Get all language files
languages = manager.get_available_languages()
if not languages:
print("No translation files found")
return
print(f"Found language files: {', '.join(languages)}")
total_removed = 0
for lang in languages:
print(f"\nProcessing language: {lang}")
# Load translation file
translations = manager.load_translation_file(lang)
original_count = len(translations)
if backup:
# Create backup
backup_file = Path(translations_dir) / f"{lang}.json.backup"
with open(backup_file, 'w', encoding='utf-8') as f:
json.dump(translations, f, ensure_ascii=False, indent=2)
print(f"Created backup: {backup_file}")
# Find unused keys
unused_keys = set(translations.keys()) - current_texts
if unused_keys:
print(f"Found {len(unused_keys)} unused keys:")
for i, key in enumerate(sorted(unused_keys)[:10], 1): # Only show first 10
print(f" {i}. \"{key[:50]}{'...' if len(key) > 50 else ''}\"")
if len(unused_keys) > 10:
print(f" ... and {len(unused_keys) - 10} more keys")
response = input(f"Delete these {len(unused_keys)} unused keys? (y/n): ")
if response.lower().strip() in ['y', 'yes']:
# Delete unused keys
for key in unused_keys:
del translations[key]
# Save cleaned file
manager.save_translation_file(lang, translations)
removed_count = len(unused_keys)
total_removed += removed_count
print(f"Deleted {removed_count} keys")
else:
print("Skipped deletion")
else:
print("No unused keys found")
new_count = len(translations)
print(f"Original key count: {original_count}, after cleanup: {new_count}")
print(f"\nCleanup completed! Total deleted {total_removed} unused keys.")
def sync_translations(translations_dir: str, source_lang: str = "en_US", target_langs: List[str] = None):
"""Sync translation keys to ensure all language files have the same keys"""
print(f"Starting translation key sync using {source_lang} as reference...")
translations_path = Path(translations_dir)
# Load source language file
source_file = translations_path / f"{source_lang}.json"
if not source_file.exists():
print(f"Error: Source language file does not exist: {source_file}")
return
with open(source_file, 'r', encoding='utf-8') as f:
source_translations = json.load(f)
source_keys = set(source_translations.keys())
print(f"Source language {source_lang} has {len(source_keys)} keys")
# Get target language list
if target_langs is None:
target_langs = []
for file_path in translations_path.glob("*.json"):
lang_code = file_path.stem
if lang_code != source_lang:
target_langs.append(lang_code)
if not target_langs:
print("No target language files found")
return
print(f"Target languages: {', '.join(target_langs)}")
for target_lang in target_langs:
print(f"\nSyncing language: {target_lang}")
target_file = translations_path / f"{target_lang}.json"
if target_file.exists():
with open(target_file, 'r', encoding='utf-8') as f:
target_translations = json.load(f)
else:
target_translations = {}
target_keys = set(target_translations.keys())
# Find missing and extra keys
missing_keys = source_keys - target_keys
extra_keys = target_keys - source_keys
print(f" Missing keys: {len(missing_keys)}")
print(f" Extra keys: {len(extra_keys)}")
# Add missing keys
if missing_keys:
for key in missing_keys:
# Use source language value as placeholder by default
target_translations[key] = source_translations[key]
print(f" Added {len(missing_keys)} missing keys")
# Ask whether to delete extra keys
if extra_keys:
response = input(f" Delete {len(extra_keys)} extra keys? (y/n): ")
if response.lower().strip() in ['y', 'yes']:
for key in extra_keys:
del target_translations[key]
print(f" Deleted {len(extra_keys)} extra keys")
# Save file
with open(target_file, 'w', encoding='utf-8') as f:
json.dump(target_translations, f, ensure_ascii=False, indent=2)
print(f" Saved: {target_file}")
def main():
parser = argparse.ArgumentParser(description="Translation File Maintenance Helper")
parser.add_argument("--translations-dir", "-t",
default=".config/quickshell/translations",
help="Translation files directory")
parser.add_argument("--source-dir", "-s",
default=".config/quickshell",
help="Source code directory")
parser.add_argument("--clean", "-c", action="store_true",
help="Clean unused translation keys")
parser.add_argument("--sync", action="store_true",
help="Sync translation keys")
parser.add_argument("--source-lang", default="en_US",
help="Source language for syncing (default: en_US)")
parser.add_argument("--no-backup", action="store_true",
help="Do not create backup files when cleaning")
args = parser.parse_args()
# Convert to absolute paths
translations_dir = os.path.abspath(args.translations_dir)
source_dir = os.path.abspath(args.source_dir)
if args.clean:
clean_translation_files(translations_dir, source_dir, backup=not args.no_backup)
elif args.sync:
sync_translations(translations_dir, args.source_lang)
else:
print("Please specify an operation:")
print(" --clean: Clean unused translation keys")
print(" --sync: Sync translation keys")
if __name__ == "__main__":
main()
@@ -0,0 +1,290 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Translation File Management Script
Used to update and extract translatable texts, manage JSON translation file key comparison
"""
import os
import json
import re
import sys
import argparse
from pathlib import Path
from typing import Dict, Set, List, Tuple
import tempfile
import subprocess
class TranslationManager:
def __init__(self, translations_dir: str, source_dir: str):
self.translations_dir = Path(translations_dir)
self.source_dir = Path(source_dir)
self.temp_extracted_file = None
# Ensure translation directory exists
self.translations_dir.mkdir(parents=True, exist_ok=True)
def extract_translatable_texts(self) -> Set[str]:
"""Extract translatable texts from source code"""
translatable_texts = set()
# Search patterns: Translation.tr("text") or Translation.tr('text')
# Improved regex that handles nested quotes correctly
patterns = [
r'Translation\.tr\s*\(\s*(["\'])(((?!\1)[^\\]|\\.)*)(\1)\s*\)', # Double or single quotes with escape support
r'Translation\.tr\s*\(\s*`([^`]*(?:\\.[^`]*)*?)`\s*\)', # Backticks (template strings)
]
# Search all .qml and .js files
file_extensions = ['*.qml', '*.js']
for ext in file_extensions:
for file_path in self.source_dir.rglob(ext):
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
for pattern in patterns:
matches = re.findall(pattern, content, re.MULTILINE | re.DOTALL)
for match in matches:
# Handle different match group structures
if isinstance(match, tuple):
# For improved regex, text is in the second group
if len(match) >= 3:
text = match[1] # Second group is the text content
else:
text = match[0] if match else ""
else:
text = match
# Decode escape characters
try:
clean_text = text.encode().decode('unicode_escape')
except:
clean_text = text
# Clean text (remove extra whitespace)
clean_text = clean_text.strip()
if clean_text:
translatable_texts.add(clean_text)
except (UnicodeDecodeError, IOError) as e:
print(f"Warning: Cannot read file {file_path}: {e}")
return translatable_texts
def create_temp_translation_file(self, texts: Set[str]) -> str:
"""Create temporary JSON file containing extracted texts"""
temp_data = {}
for text in sorted(texts):
temp_data[text] = text # Key and value are the same, indicating untranslated
# Create temporary file
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False, encoding='utf-8') as f:
json.dump(temp_data, f, ensure_ascii=False, indent=2)
self.temp_extracted_file = f.name
return self.temp_extracted_file
def load_translation_file(self, lang_code: str) -> Dict[str, str]:
"""Load translation file for specified language"""
file_path = self.translations_dir / f"{lang_code}.json"
if file_path.exists():
try:
with open(file_path, 'r', encoding='utf-8') as f:
return json.load(f)
except (json.JSONDecodeError, IOError) as e:
print(f"Warning: Cannot load translation file {file_path}: {e}")
return {}
return {}
def save_translation_file(self, lang_code: str, translations: Dict[str, str]):
"""Save translation file"""
file_path = self.translations_dir / f"{lang_code}.json"
try:
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(translations, f, ensure_ascii=False, indent=2)
print(f"Translation file saved: {file_path}")
except IOError as e:
print(f"Error: Cannot save translation file {file_path}: {e}")
def get_available_languages(self) -> List[str]:
"""Get list of available languages"""
languages = []
for file_path in self.translations_dir.glob("*.json"):
lang_code = file_path.stem
languages.append(lang_code)
return sorted(languages)
def compare_translations(self, extracted_texts: Set[str], target_lang: str) -> Tuple[Set[str], Set[str]]:
"""Compare extracted texts with existing translation file"""
existing_translations = self.load_translation_file(target_lang)
existing_keys = set(existing_translations.keys())
missing_keys = extracted_texts - existing_keys # Missing keys
extra_keys = existing_keys - extracted_texts # Extra keys
return missing_keys, extra_keys
def interactive_update(self, lang_code: str, missing_keys: Set[str], extra_keys: Set[str]):
"""Interactively update translation file"""
translations = self.load_translation_file(lang_code)
modified = False
# Handle missing keys
if missing_keys:
print(f"\nFound {len(missing_keys)} missing translation keys:")
for i, key in enumerate(sorted(missing_keys), 1):
print(f"{i}. \"{key}\"")
if self.ask_yes_no(f"\nAdd these {len(missing_keys)} missing keys?"):
for key in missing_keys:
translations[key] = key # Default value is the key itself
modified = True
print(f"Added {len(missing_keys)} keys")
# Handle extra keys
if extra_keys:
print(f"\nFound {len(extra_keys)} extra translation keys:")
for i, key in enumerate(sorted(extra_keys), 1):
print(f"{i}. \"{key}\" -> \"{translations.get(key, '')}\"")
if self.ask_yes_no(f"\nDelete these {len(extra_keys)} extra keys?"):
for key in extra_keys:
if key in translations:
del translations[key]
modified = True
print(f"Deleted {len(extra_keys)} keys")
# Save changes
if modified:
self.save_translation_file(lang_code, translations)
else:
print("No changes made")
def ask_yes_no(self, question: str) -> bool:
"""Ask user for confirmation"""
while True:
response = input(f"{question} (y/n): ").lower().strip()
if response in ['y', 'yes']:
return True
elif response in ['n', 'no']:
return False
else:
print("Please enter y/yes or n/no")
def cleanup(self):
"""Clean up temporary files"""
if self.temp_extracted_file and os.path.exists(self.temp_extracted_file):
os.unlink(self.temp_extracted_file)
def main():
parser = argparse.ArgumentParser(description="Translation file management tool")
parser.add_argument("--translations-dir", "-t",
default=".config/quickshell/translations",
help="Translation files directory (default: .config/quickshell/translations)")
parser.add_argument("--source-dir", "-s",
default=".config/quickshell",
help="Source code directory (default: .config/quickshell)")
parser.add_argument("--language", "-l",
help="Specify language code to process (e.g., zh_CN)")
parser.add_argument("--extract-only", "-e", action="store_true",
help="Only extract translatable texts to temporary file")
parser.add_argument("--show-temp", action="store_true",
help="Show temporary extracted file content")
args = parser.parse_args()
# Convert to absolute paths
translations_dir = os.path.abspath(args.translations_dir)
source_dir = os.path.abspath(args.source_dir)
print(f"Translation directory: {translations_dir}")
print(f"Source code directory: {source_dir}")
# Check if directories exist
if not os.path.exists(source_dir):
print(f"Error: Source code directory does not exist: {source_dir}")
sys.exit(1)
# Create manager
manager = TranslationManager(translations_dir, source_dir)
try:
# Extract translatable texts
print("\nExtracting translatable texts...")
extracted_texts = manager.extract_translatable_texts()
print(f"Extracted {len(extracted_texts)} translatable texts")
# Create temporary file
temp_file = manager.create_temp_translation_file(extracted_texts)
print(f"Created temporary file: {temp_file}")
if args.show_temp:
print("\nTemporary file contents:")
with open(temp_file, 'r', encoding='utf-8') as f:
print(f.read())
if args.extract_only:
print("Extract-only mode, program finished")
return
# Get available languages
available_languages = manager.get_available_languages()
if args.language:
target_languages = [args.language]
else:
print(f"\nAvailable languages: {', '.join(available_languages) if available_languages else 'None'}")
if not available_languages:
print("No existing translation files found")
lang_input = input("Enter language code to create (e.g.: zh_CN): ").strip()
if lang_input:
target_languages = [lang_input]
else:
print("No language specified, program finished")
return
else:
print("Choose language to process:")
for i, lang in enumerate(available_languages, 1):
print(f"{i}. {lang}")
print("a. Process all languages")
choice = input("Please choose (enter number, language code, or 'a'): ").strip()
if choice.lower() == 'a':
target_languages = available_languages
elif choice.isdigit() and 1 <= int(choice) <= len(available_languages):
target_languages = [available_languages[int(choice) - 1]]
elif choice in available_languages:
target_languages = [choice]
else:
print("Invalid choice, program finished")
return
# Process each language
for lang in target_languages:
print(f"\n{'='*50}")
print(f"Processing language: {lang}")
print('='*50)
missing_keys, extra_keys = manager.compare_translations(extracted_texts, lang)
if not missing_keys and not extra_keys:
print(f"Translation file for language {lang} is already up to date")
continue
print(f"Analysis results:")
print(f" Missing keys: {len(missing_keys)}")
print(f" Extra keys: {len(extra_keys)}")
if missing_keys or extra_keys:
manager.interactive_update(lang, missing_keys, extra_keys)
finally:
# Clean up temporary files
manager.cleanup()
if __name__ == "__main__":
main()
@@ -0,0 +1,246 @@
# Translation Management Tool Suite
This tool suite is used to manage project translation files, automatically extract translatable texts, compare differences between different language files, and provide maintenance functions.
## Tool Components
### 1. `translation-manager.py` - Main Translation Manager
- Extract translatable texts
- Compare and update translation files
- Interactive adding/removing translation keys
### 2. `translation-cleaner.py` - Translation File Maintenance Tool
- Clean unused translation keys
- Sync key structure across different language files
### 3. `manage-translations.sh` - Convenient Wrapper Script
- Provide unified command-line interface
- Display translation status
- Simplify common operations
## Quick Start
### Check Translation Status
```bash
./manage-translations.sh status
```
### Extract Translatable Texts
```bash
./manage-translations.sh extract
```
### Update Translation Files
```bash
# Update all languages
./manage-translations.sh update
# Update specific language
./manage-translations.sh update -l zh_CN
```
### Clean Unused Keys
```bash
./manage-translations.sh clean
```
### Sync Keys Across Languages
```bash
./manage-translations.sh sync
```
## Detailed Usage
### translation-manager.py
The main translation management tool that extracts translatable texts from source code and manages translation files.
#### Command Line Options
```bash
python3 translation-manager.py [options]
Options:
-h, --help Show help message
-t, --translations-dir DIR Translation files directory (default: .config/quickshell/translations)
-s, --source-dir DIR Source code directory (default: .config/quickshell)
-l, --language LANG Specify language code to process (e.g., zh_CN)
-e, --extract-only Only extract translatable texts to temporary file
--show-temp Show temporary extracted file content
```
#### Features
1. **Text Extraction**: Uses regex patterns to extract translatable texts from QML and JavaScript files
2. **Smart Filtering**: Automatically removes duplicates and cleans up extracted texts
3. **Interactive Updates**: Guides users through adding missing keys and removing extra ones
4. **Backup Support**: Creates backups before making changes
#### Supported Text Patterns
- `qsTr("text")` and `qsTr('text')`
- `i18n.t("text")` and `i18n.t('text')`
- Supports nested quotes and escape characters
- Handles multiline strings
### translation-cleaner.py
A maintenance tool for cleaning up and synchronizing translation files.
#### Command Line Options
```bash
python3 translation-cleaner.py [options]
Options:
-h, --help Show help message
-t, --translations-dir DIR Translation files directory
-s, --source-dir DIR Source code directory
-c, --clean Clean unused translation keys
--sync Sync translation keys
--source-lang LANG Source language for syncing (default: en_US)
--no-backup Do not create backup files when cleaning
```
#### Features
1. **Unused Key Cleanup**: Identifies and removes translation keys that are no longer used in the source code
2. **Key Synchronization**: Ensures all language files have the same set of keys
3. **Backup Protection**: Creates backup files before making destructive changes
4. **Interactive Confirmation**: Asks for user confirmation before deleting keys
### manage-translations.sh
A convenient wrapper script that provides a unified interface to all translation tools.
#### Commands
```bash
./manage-translations.sh [options] <command>
Commands:
extract Extract translatable texts to temporary file
update Update translation files (add missing/remove extra keys)
clean Clean unused translation keys
sync Sync keys across all language files
status Show translation status
Options:
-l, --lang LANG Specify language (e.g.: zh_CN)
-t, --trans-dir DIR Translation files directory
-s, --source-dir DIR Source code directory
-h, --help Show help message
```
## Workflow Examples
### Initial Setup
1. Create translation directory structure
2. Extract all translatable texts: `./manage-translations.sh extract`
3. Create initial translation files: `./manage-translations.sh update`
### Regular Maintenance
1. Check status: `./manage-translations.sh status`
2. Update translations after code changes: `./manage-translations.sh update`
3. Clean up unused keys periodically: `./manage-translations.sh clean`
### Adding New Language
1. Create new language file: `./manage-translations.sh update -l new_lang`
2. Sync keys if needed: `./manage-translations.sh sync`
## File Structure
```
translations/
├── tools/ # Translation management tools
│ ├── translation-manager.py # Main extraction and update tool
│ ├── translation-cleaner.py # Cleanup and sync tool
│ ├── manage-translations.sh # Wrapper script
│ └── translation-tools-guide.md # This documentation
├── en_US.json # English translations (reference)
├── zh_CN.json # Chinese translations
└── [other_lang].json # Other language files
```
## Configuration
### Text Extraction Patterns
The tool uses regex patterns to identify translatable texts. Current patterns include:
1. **QML qsTr patterns**:
- `qsTr("text")` and `qsTr('text')`
- Supports escaped quotes and nested quotes
2. **JavaScript i18n patterns**:
- `i18n.t("text")` and `i18n.t('text')`
- Supports template literals and complex expressions
3. **Custom patterns** can be added by modifying the patterns list in `translation-manager.py`
### File Extensions
By default, the tool processes:
- `.qml` files (QML/QtQuick)
- `.js` files (JavaScript)
Additional file types can be added by modifying the file extension filters.
## Best Practices
1. **Regular Updates**: Run `./manage-translations.sh status` regularly to check for new translatable texts
2. **Clean Periodically**: Use `./manage-translations.sh clean` to remove unused keys
3. **Backup Important**: Always backup translation files before major changes
4. **Consistent Patterns**: Use consistent function calls (`qsTr`, `i18n.t`) for translatable texts
5. **Review Changes**: Always review the changes before confirming deletions or additions
## Troubleshooting
### Common Issues
1. **Missing Texts**: If some translatable texts are not extracted, check if they match the supported patterns
2. **Path Issues**: Ensure the source and translation directory paths are correct
3. **Permission Errors**: Make sure the script has write permissions to the translation directory
4. **Encoding Issues**: All files should be saved in UTF-8 encoding
### Getting Help
For additional help:
- Use `--help` option with any tool
- Check the console output for error messages
- Verify file paths and permissions
## Advanced Usage
### Custom Regex Patterns
To add support for new translation function patterns, modify the `patterns` list in `TranslationManager.extract_translatable_texts()`:
```python
patterns = [
# Existing patterns...
r'customTranslate\s*\(\s*(["\'])((?:\\.|(?!\1)[^\\])*?)\1\s*\)', # Custom pattern
]
```
### Batch Processing
For processing multiple projects or directories:
```bash
# Process multiple directories
for dir in project1 project2 project3; do
./manage-translations.sh -s "$dir" -t "$dir/translations" update
done
```
### Integration with Build Systems
The tools can be integrated into build systems to automatically update translations:
```bash
# In your build script
./manage-translations.sh update --non-interactive
```
## Version History
- **v1.0**: Initial version with basic extraction and update functionality
- **v1.1**: Added improved regex patterns for better text extraction
- **v1.2**: Added cleaning and synchronization tools
- **v1.3**: Added English output and improved user interface
- **v1.4**: Moved all tools to dedicated tools directory and improved documentation
@@ -0,0 +1,287 @@
# 翻译管理工具套件
这套工具用于管理项目的翻译文件,自动提取可翻译文本,比较不同语言文件之间的差异,并提供维护功能。
## 工具组成
### 1. `translation-manager.py` - 主要翻译管理器
- 提取可翻译文本
- 比较和更新翻译文件
- 交互式添加/删除翻译键
### 2. `translation-cleaner.py` - 翻译文件维护工具
- 清理不再使用的翻译键
- 同步不同语言文件的键结构
### 3. `manage-translations.sh` - 便捷包装脚本
- 提供统一的命令行界面
- 显示翻译状态
- 简化常用操作
## 快速开始
### 使用便捷脚本(推荐)
```bash
# 进入工具目录
cd .config/quickshell/translations/tools
# 查看帮助
./manage-translations.sh --help
# 显示当前翻译状态
./manage-translations.sh status
# 提取可翻译文本
./manage-translations.sh extract
# 更新所有翻译文件
./manage-translations.sh update
# 更新特定语言
./manage-translations.sh update -l zh_CN
# 清理不再使用的键
./manage-translations.sh clean
# 同步所有语言文件的键
./manage-translations.sh sync
```
或者从项目根目录运行:
```bash
# 从项目根目录运行
.config/quickshell/translations/tools/manage-translations.sh status
.config/quickshell/translations/tools/manage-translations.sh update
```
## 详细使用说明
### 翻译管理器 (`translation-manager.py`)
基本用法:
```bash
# 处理所有语言
./translation-manager.py
# 指定特定语言
./translation-manager.py --language zh_CN
# 仅提取可翻译文本
./translation-manager.py --extract-only
# 显示提取的文本
./translation-manager.py --extract-only --show-temp
```
参数说明:
- `--translations-dir`, `-t`: 翻译文件目录(默认:`.config/quickshell/translations`
- `--source-dir`, `-s`: 源代码目录(默认:`.config/quickshell`
- `--language`, `-l`: 指定要处理的语言代码
- `--extract-only`, `-e`: 仅提取可翻译文本
- `--show-temp`: 显示临时提取文件的内容
### 翻译清理器 (`translation-cleaner.py`)
```bash
# 清理不再使用的翻译键
./translation-cleaner.py --clean
# 同步翻译键(以 en_US 为基准)
./translation-cleaner.py --sync
# 指定不同的源语言进行同步
./translation-cleaner.py --sync --source-lang zh_CN
# 清理时不创建备份
./translation-cleaner.py --clean --no-backup
```
## 工作流程
### 日常翻译更新流程
1. **检查状态**
```bash
./manage-translations.sh status
```
2. **更新翻译**
```bash
./manage-translations.sh update
```
3. **清理无用键**(可选):
```bash
./manage-translations.sh clean
```
### 新增语言流程
1. **创建新语言文件**
```bash
./manage-translations.sh update -l new_lang
```
2. **同步键结构**
```bash
./manage-translations.sh sync
```
### 大规模重构后的清理流程
1. **备份翻译文件**
```bash
cp -r .config/quickshell/translations .config/quickshell/translations.backup
```
2. **清理无用键**
```bash
./manage-translations.sh clean
```
3. **同步所有语言**
```bash
./manage-translations.sh sync
```
## 支持的翻译文本格式
工具可以识别以下格式的可翻译文本:
```qml
// 基本格式
Translation.tr("Hello, world!")
Translation.tr('Hello, world!')
Translation.tr(`Hello, world!`)
// 带换行符
Translation.tr("Line 1\nLine 2")
// 带转义字符
Translation.tr("Say \"Hello\"")
// 带参数占位符
Translation.tr("Hello, %1!").arg(name)
Translation.tr("{0} files selected").arg(count)
```
## 示例输出
### 状态显示
```
$ ./manage-translations.sh status
正在分析翻译状态...
=== 当前项目状态 ===
提取到 166 个可翻译文本
=== 翻译文件状态 ===
en_US: 470 个键
zh_CN: 470 个键
```
### 更新翻译
```
$ ./manage-translations.sh update -l zh_CN
更新翻译文件...
==================================================
处理语言: zh_CN
==================================================
分析结果:
缺少的键: 5
多余的键: 20
发现 5 个缺少的翻译键:
1. "New feature text"
2. "Another new text"
...
是否添加这 5 个缺少的键? (y/n): y
已添加 5 个键
发现 20 个多余的翻译键:
1. "Removed old text" -> "已删除的旧文本"
...
是否删除这 20 个多余的键? (y/n): y
已删除 20 个键
已保存翻译文件
```
### 清理无用键
```
$ ./manage-translations.sh clean
清理不再使用的翻译键...
处理语言: zh_CN
发现 50 个不再使用的键:
1. "old_unused_text"
2. "deprecated_message"
...
是否删除这 50 个不再使用的键? (y/n): y
已删除 50 个键
原始键数: 470, 清理后: 420
```
## 高级功能
### 自定义目录结构
```bash
# 使用自定义目录
./translation-manager.py \
--translations-dir /path/to/translations \
--source-dir /path/to/source
```
### 批量处理
```bash
# 创建处理脚本
cat > update-all-translations.sh << 'EOF'
#!/bin/bash
for lang in zh_CN ja_JP ko_KR; do
echo "Processing $lang..."
./manage-translations.sh update -l $lang
done
EOF
chmod +x update-all-translations.sh
./update-all-translations.sh
```
## 注意事项
1. **备份重要**:在执行清理操作前,工具会自动创建备份,但建议手动备份重要文件
2. **文本提取限制**
- 只支持静态字符串,不支持动态构建的字符串
- 必须使用 `Translation.tr()` 格式
3. **文件编码**:所有文件必须使用 UTF-8 编码
4. **键名规范**:建议使用英文作为键名,避免使用特殊字符
## 故障排除
### 常见问题
**Q: 提取的文本数量与预期不符?**
A: 检查是否所有可翻译文本都使用了 `Translation.tr()` 格式,确保没有动态构建的字符串。
**Q: 同步后某些翻译丢失?**
A: 检查源语言文件是否包含所有必要的键,考虑使用不同的源语言进行同步。
**Q: 清理操作删除了需要的键?**
A: 从自动创建的备份文件中恢复,检查源代码中是否正确使用了 `Translation.tr()`。
### 恢复备份
```bash
# 恢复单个文件
cp .config/quickshell/translations/zh_CN.json.backup .config/quickshell/translations/zh_CN.json
# 恢复所有文件
cp .config/quickshell/translations.backup/* .config/quickshell/translations/
```
+51 -347
View File
@@ -27,28 +27,26 @@
"Clear chat history": "清除聊天记录",
"Clear the current list of images": "清除当前图片列表",
"Close": "关闭",
"Closes cheatsheet on press": "Closes cheatsheet on press",
"Closes left sidebar on press": "Closes left sidebar on press",
"Closes media controls on press": "Closes media controls on press",
"Closes on screen keyboard on press": "Closes on screen keyboard on press",
"Closes overview": "Closes overview",
"Closes right sidebar on press": "Closes right sidebar on press",
"Closes cheatsheet on press": "按下时关闭快捷键表",
"Closes left sidebar on press": "按下时关闭左侧边栏",
"Closes media controls on press": "按下时关闭媒体控制",
"Closes on screen keyboard on press": "按下时关闭屏幕键盘",
"Closes overview": "关闭概览",
"Closes right sidebar on press": "按下时关闭右侧边栏",
"Copy": "复制",
"Copy code": "复制代码",
"Ctrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Ctrl+O 展开侧边栏\nCtrl+P 将侧边栏分离为窗口",
"Current API endpoint: {0}\nSet it with {1}mode PROVIDER": "当前 API 端点:{0}\n使用 {1}mode PROVIDER 设置",
"Current model: {0}\nSet it with {1}model MODEL": "当前模型:{0}\n使用 {1}model MODEL 设置",
"Decrease brightness": "Decrease brightness",
"Decrease brightness": "降低亮度",
"Delete": "删除",
"Desktop": "桌面",
"Detach left sidebar into a window/Attach it back": "Detach left sidebar into a window/Attach it back",
"Detach left sidebar into a window/Attach it back": "将左侧边栏分离为窗口/重新附加",
"Disable NSFW content": "禁用 NSFW 内容",
"Done": "完成",
"Download": "下载",
"Download complete": "下载完成",
"Edit": "编辑",
"Enter text to translate...": "输入要翻译的文本...",
"Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "实验性 | 在线 | Google 模型\n功能更多但搜索速度较慢",
"Finished tasks will go here": "已完成的任务将显示在这里",
"For desktop wallpapers | Good quality": "桌面壁纸专用 | 质量好",
"For storing API keys and other sensitive information": "用于存储 API 密钥和其他敏感信息",
@@ -56,17 +54,14 @@
"Get the next page of results": "获取下一页结果",
"Go to source ({0})": "转到源 ({0})",
"Hibernate": "休眠",
"Hides brightness OSD on press": "Hides brightness OSD on press",
"Hides volume OSD on press": "Hides volume OSD on press",
"Hold to show workspace numbers, release to show icons": "Hold to show workspace numbers, release to show icons",
"Increase brightness": "Increase brightness",
"Hides brightness OSD on press": "按下时隐藏亮度显示",
"Hides volume OSD on press": "按下时隐藏音量显示",
"Hold to show workspace numbers, release to show icons": "按住显示工作区编号,松开显示图标",
"Increase brightness": "提高亮度",
"Input": "输入",
"Intelligence": "智能",
"Interface": "界面",
"Interrupts possibility of overview being toggled on release. ": "Interrupts possibility of overview being toggled on release. ",
"Invalid API provider. Supported: \n- ": "无效的 API 提供商。支持的:\n- ",
"Invalid arguments. Must provide `key` and `value`.": "参数无效。必须提供 `key` 和 `value`。",
"Invalid model. Supported: \n```\n": "无效模型。支持的:\n```\n",
"Jump to current month": "跳转到当前月份",
"Keep system awake": "保持系统唤醒",
"Large images | God tier quality, no NSFW.": "大尺寸图片 | 顶级质量,无 NSFW",
@@ -86,24 +81,21 @@
"Nothing here!": "这里什么都没有!",
"Notifications": "通知",
"OK": "确定",
"Online via {0} | {1}'s model": "通过 {0} 在线 | {1} 模型",
"Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。",
"Open file link": "打开文件链接",
"Opens cheatsheet on press": "Opens cheatsheet on press",
"Opens left sidebar on press": "Opens left sidebar on press",
"Opens media controls on press": "Opens media controls on press",
"Opens on screen keyboard on press": "Opens on screen keyboard on press",
"Opens right sidebar on press": "Opens right sidebar on press",
"Opens session screen on press": "Opens session screen on press",
"Opens cheatsheet on press": "按下时打开快捷键表",
"Opens left sidebar on press": "按下时打开左侧边栏",
"Opens media controls on press": "按下时打开媒体控制",
"Opens on screen keyboard on press": "按下时打开屏幕键盘",
"Opens right sidebar on press": "按下时打开右侧边栏",
"Opens session screen on press": "按下时打开会话屏幕",
"Output": "输出",
"Page {0}": "第 {0} 页",
"Plasma Settings": "Plasma 设置",
"Provider set to ": "提供商设置为",
"Reboot": "重启",
"Reboot to firmware settings": "Reboot to firmware settings",
"Reboot to firmware settings": "重启到固件设置",
"Reload Hyprland & Quickshell": "重新加载 Hyprland 和 Quickshell",
"Run": "运行",
"Run command": "Run command",
"Run command": "运行命令",
"Save": "保存",
"Save to Downloads": "保存到下载文件夹",
"Scroll to change brightness": "滚动调节亮度",
@@ -122,351 +114,63 @@
"Shutdown": "关机",
"Silent": "静音",
"Sleep": "睡眠",
"Switched to search mode. Continue with the user's request.": "已切换到搜索模式。继续处理用户请求。",
"System": "系统",
"Task Manager": "任务管理器",
"Task description": "任务描述",
"Temperature must be between 0 and 2": "温度必须在 0 到 2 之间",
"Temperature set to {0}": "温度设置为 {0}",
"Temperature: {0}": "温度:{0}",
"That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果没有想到标签,请输入页码",
"The current API used. Endpoint: ": "当前使用的 API。端点:",
"The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "成人向 | 数量巨大,大量 NSFW,质量参差不齐",
"The popular one | Best quantity, but quality can vary wildly": "最受欢迎 | 数量最多,但质量参差不齐",
"Thinking": "思考中",
"This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ": "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ",
"To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.": "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.",
"Toggle clipboard query on overview widget": "Toggle clipboard query on overview widget",
"Toggle emoji query on overview widget": "Toggle emoji query on overview widget",
"Toggles cheatsheet on press": "Toggles cheatsheet on press",
"Toggles left sidebar on press": "Toggles left sidebar on press",
"Toggles media controls on press": "Toggles media controls on press",
"Toggles on screen keyboard on press": "Toggles on screen keyboard on press",
"Toggles overview on press": "Toggles overview on press",
"Toggles overview on release": "Toggles overview on release",
"Toggles right sidebar on press": "Toggles right sidebar on press",
"Toggles session screen on press": "Toggles session screen on press",
"To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.": "为确保此功能正常工作,请在自动触发的子映射中使用 binditn = MODKEYS, catchall,该子映射包含所有内容。",
"Toggle clipboard query on overview widget": "在概览组件中切换剪贴板查询",
"Toggle emoji query on overview widget": "在概览组件中切换表情符号查询",
"Toggles cheatsheet on press": "按下时切换快捷键表",
"Toggles left sidebar on press": "按下时切换左侧边栏",
"Toggles media controls on press": "按下时切换媒体控制",
"Toggles on screen keyboard on press": "按下时切换屏幕键盘",
"Toggles overview on press": "按下时切换概览",
"Toggles overview on release": "松开时切换概览",
"Toggles right sidebar on press": "按下时切换右侧边栏",
"Toggles session screen on press": "按下时切换会话屏幕",
"Translation goes here...": "翻译结果会显示在这里...",
"Translator": "翻译器",
"Triggers brightness OSD on press": "Triggers brightness OSD on press",
"Triggers volume OSD on press": "Triggers volume OSD on press",
"Triggers brightness OSD on press": "按下时触发亮度显示",
"Triggers volume OSD on press": "按下时触发音量显示",
"Unfinished": "未完成",
"Unknown": "未知",
"Unknown Album": "未知专辑",
"Unknown Artist": "未知艺术家",
"Unknown Title": "未知标题",
"Unknown command: ": "未知命令:",
"Unknown function call: {0}": "未知函数调用:{0}",
"Uptime: {0}": "运行时间:{0}",
"Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in **bold** to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`. When making changes to the user's config, you must get the config to know what values there are before setting.": "Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in **bold** to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`. When making changes to the user's config, you must get the config to know what values there are before setting.",
"View Markdown source": "查看 Markdown 源码",
"Volume": "音量",
"Volume mixer": "音量混合器",
"Waifus only | Excellent quality, limited quantity": "仅限角色 | 优秀质量,数量有限",
"Waiting for response...": "等待响应...",
"Workspace": "工作区",
"\nSet with /mode PROVIDER": "\n使用 /mode PROVIDER 设置",
"about": "关于",
"accessed": "访问时间",
"account": "账户",
"active": "活动",
"active_state": "活动状态",
"addon": "附加组件",
"addons": "附加组件",
"address": "地址",
"admin": "管理员",
"align": "对齐",
"always_on_top": "总在最前",
"animate": "动画",
"app": "应用",
"appearance": "外观",
"application": "应用程序",
"applications": "应用程序",
"apply": "应用",
"apps": "应用",
"auto": "自动",
"background": "背景",
"balanced": "平衡",
"bar": "栏",
"bars": "栏",
"battery": "电池",
"bluetooth": "蓝牙",
"blur": "模糊",
"border": "边框",
"bottom": "底部",
"brightness": "亮度",
"bring_to_front": "置于顶层",
"bytes": "{0} 字节",
"cancel": "取消",
"center": "居中",
"characters": "{0} 个字符",
"charging": "充电中",
"clear_all": "清除全部",
"clicked": "点击",
"close": "关闭",
"collapse": "折叠",
"color": "颜色",
"command": "命令",
"connected": "已连接",
"connecting": "连接中...",
"context_menu": "右键菜单",
"copy": "复制",
"cpu": "处理器",
"created": "创建时间",
"critical": "电量危险",
"customize": "自定义",
"cut": "剪切",
"dark": "深色",
"dashboard": "仪表板",
"date": "日期",
"debug": "调试",
"default": "默认",
"degrees": "{0}°",
"delete": "删除",
"demo": "演示",
"desktop": "桌面",
"dialog": "对话框",
"disabled": "已禁用",
"discharging": "放电中",
"disconnected": "已断开",
"disk": "磁盘",
"display": "显示",
"distribute": "分布",
"dock": "停靠栏",
"documents": "文档",
"double_clicked": "双击",
"downloads": "下载",
"dragged": "拖拽",
"dropdown": "下拉菜单",
"dropped": "拖放",
"effect": "效果",
"email": "邮箱",
"enabled": "已启用",
"error": "错误",
"ethernet": "以太网",
"example": "示例",
"execute": "执行",
"exit": "退出",
"expand": "展开",
"extension": "扩展",
"extensions": "扩展",
"fan": "风扇",
"favorites": "收藏夹",
"file": "文件",
"files": "文件",
"filter": "滤镜",
"flip": "翻转",
"floating": "浮动",
"focus": "焦点",
"folder": "文件夹",
"folders": "文件夹",
"foreground": "前景",
"format": "格式",
"full": "已充满",
"fullscreen": "全屏",
"gigabytes": "{0} GB",
"gigahertz": "{0} GHz",
"glow": "发光",
"group": "群组",
"guest": "访客",
"headphones": "耳机",
"help": "帮助",
"hertz": "{0} Hz",
"hibernate": "休眠",
"hidden": "隐藏",
"hide": "隐藏",
"highlight": "高亮",
"hint": "提示",
"home": "主目录",
"hour": "小时",
"hours": "小时",
"hover": "悬停",
"inactive": "非活动",
"info": "信息",
"input": "输入",
"install": "安装",
"justify": "两端对齐",
"keybinds": "按键绑定",
"keyboard": "键盘",
"kilobytes": "{0} KB",
"kilohertz": "{0} kHz",
"landscape": "横向",
"language": "语言",
"launcher": "启动器",
"left": "左对齐",
"light": "浅色",
"loading": "加载中...",
"lock": "锁定",
"log": "日志",
"logout": "注销",
"long_pressed": "长按",
"low": "电量低",
"manual": "手册",
"margin": "边距",
"maximize": "最大化",
"megabytes": "{0} MB",
"megahertz": "{0} MHz",
"memory": "内存",
"menu": "菜单",
"menubar": "菜单栏",
"microphone": "麦克风",
"minimize": "最小化",
"minute": "分钟",
"minutes": "分钟",
"mirror": "镜像",
"modal": "模态框",
"modified": "修改时间",
"monitor": "显示器",
"mouse": "鼠标",
"move": "移动",
"music": "音乐",
"mute": "静音",
"network": "网络",
"next": "下一个",
"no": "否",
"no_notifications": "无通知",
"notifications": "通知",
"off": "关",
"ok": "确定",
"on": "开",
"opacity": "不透明度",
"open": "打开",
"orientation": "方向",
"outline": "轮廓",
"output": "输出",
"overview": "概览",
"owner": "所有者",
"package": "软件包",
"packages": "软件包",
"padding": "内边距",
"panel": "面板",
"panels": "面板",
"panned": "平移",
"password": "密码",
"paste": "粘贴",
"pause": "暂停",
"percent": "{0}%",
"performance": "性能",
"permissions": "权限",
"phone": "电话",
"pictures": "图片",
"pinched": "捏合",
"pinned": "固定",
"pixels": "{0}像素",
"placeholder": "占位符",
"play": "播放",
"plugin": "插件",
"plugins": "插件",
"popup": "弹出窗口",
"portrait": "纵向",
"power": "电源",
"power_saver": "节能",
"pressed": "按下",
"previous": "上一个",
"profile": "配置文件",
"properties": "属性",
"quiet": "安静",
"quit": "退出",
"read": "读取",
"recent": "最近",
"recording": "录制",
"refresh": "刷新",
"refresh_rate": "刷新率",
"reload": "重新加载",
"rename": "重命名",
"reset": "重置",
"resize": "调整大小",
"resolution": "分辨率",
"restart": "重启",
"restore": "还原",
"right": "右对齐",
"right_clicked": "右键点击",
"rotate": "旋转",
"rotated": "旋转",
"sample": "样本",
"save": "保存",
"scale": "缩放",
"screenshot": "截图",
"scrolled": "滚动",
"search": "搜索",
"second": "秒",
"seconds": "秒",
"selection": "选择",
"send_to_back": "置于底层",
"settings": "设置",
"shadow": "阴影",
"shortcuts": "快捷键",
"show": "显示",
"shutdown": "关机",
"sidebar": "侧边栏",
"silent": "静默",
"size": "大小",
"snapped": "贴靠",
"software": "软件",
"space": "间距",
"speaker": "扬声器",
"statusbar": "状态栏",
"sticky": "粘性",
"stop": "停止",
"style": "样式",
"success": "成功",
"suspend": "挂起",
"swiped": "滑动",
"systray": "系统托盘",
"tapped": "轻触",
"taskbar": "任务栏",
"temperature": "温度",
"terabytes": "{0} TB",
"terminal": "终端",
"test": "测试",
"theme": "主题",
"tiled": "平铺",
"time": "时间",
"tips": "提示",
"today": "今天",
"tomorrow": "明天",
"toolbar": "工具栏",
"tooltip": "工具提示",
"top": "顶部",
"touchpad": "触摸板",
"trace": "跟踪",
"transform": "变换",
"transition": "过渡",
"transparency": "透明度",
"trash": "回收站",
"tutorial": "教程",
"type": "类型",
"uninstall": "卸载",
"unlock": "解锁",
"unmute": "取消静音",
"unpinned": "取消固定",
"unsticky": "取消粘性",
"update": "更新",
"upgrade": "升级",
"user": "用户",
"username": "用户名",
"verbose": "详细",
"version": "版本",
"videos": "视频",
"visible": "可见",
"volume": "音量",
"warning": "警告",
"welcome": "欢迎",
"widget": "组件",
"widgets": "组件",
"wifi": "Wi-Fi",
"window": "窗口",
"windowed": "窗口化",
"windows": "窗口",
"workspace": "工作区",
"workspaces": "工作区",
"write": "写入",
"yes": "是",
"yesterday": "昨天",
"zoomed": "缩放",
"{0} (copied)": "{0}(已复制)",
"{0} Safe Storage": "{0} 安全存储",
"{0} does not require an API key": "{0} 不需要 API 密钥",
"{0} queries pending": "{0} 个查询等待中",
"{0} | Right-click to configure": "{0} | 右键点击进行配置"
"{0} | Right-click to configure": "{0} | 右键点击进行配置",
"Set with /mode PROVIDER": "使用 /mode PROVIDER 设置",
"Invalid API provider. Supported: \n-": "无效的 API 提供商。支持的:\n-",
"Unknown command:": "未知命令:",
"Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "输入 /key 开始使用在线模型\nCtrl+O 展开侧边栏\nCtrl+P 将侧边栏分离为窗口",
"This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key.": "这是必要的,因为在按住键时,quickshell 中的 GlobalShortcut.onReleased 会在您是否按下其他键时触发。",
"The current API used. Endpoint:": "当前使用的 API。端点:",
"Provider set to": "提供商设置为",
"Invalid model. Supported: \n```": "无效模型。支持的:\n```",
"Interrupts possibility of overview being toggled on release.": "中断松开时切换概览的可能性。",
"Switched to search mode. Continue with the user's request.": "已切换到搜索模式。继续处理用户请求。",
"Message the model... \"{0}\" for commands": "与模型对话... \"{0}\" 查看命令",
"Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "实验性 | 在线 | Google 模型\n功能更多但搜索速度较慢",
"To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For {0}:\n\n**Link**: {1}\n\n{2}": "要设置 API 密钥,请将其与命令一起传递\n\n要查看密钥,请将 \"get\" 与命令一起传递<br/>\n\n### 对于 {0}\n\n**链接**{1}\n\n{2}",
"Enter tags, or \"{0}\" for commands": "输入标签,或 \"{0}\" 查看命令",
"Online via {0} | {1}'s model": "通过 {0} 在线 | {1} 的模型",
"That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果没有想到标签,请输入页码",
"Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。"
}