forked from Shinonome/dots-hyprland
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:
@@ -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/
|
||||
```
|
||||
@@ -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通过搜索提供最新信息。"
|
||||
}
|
||||
Reference in New Issue
Block a user