mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-08 12:13:31 -04:00
switch hto monorepo structure
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
# DankMaterialShell Translations
|
||||
|
||||
This directory contains translation files for DankMaterialShell extracted from all qsTr() calls in the QML codebase.
|
||||
|
||||
## Files
|
||||
|
||||
- **en.json** - Source language file with English strings and file references
|
||||
- **template.json** - Empty template for creating new translations
|
||||
- **extract_translations.py** - Script to regenerate translation files
|
||||
|
||||
## POEditor Format
|
||||
|
||||
The JSON files follow POEditor's import format:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"term": "string to translate",
|
||||
"context": "file:line",
|
||||
"reference": "Modules/Settings/AboutTab.qml:45",
|
||||
"comment": ""
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Field Descriptions
|
||||
|
||||
- **term**: The source string in English (from qsTr() calls)
|
||||
- **context**: Primary location where the string appears (file:line)
|
||||
- **reference**: All locations where this string is used (comma-separated)
|
||||
- **comment**: Additional notes for translators (currently empty)
|
||||
|
||||
## How to Create a New Translation
|
||||
|
||||
1. Copy `template.json` to your language code (e.g., `es.json` for Spanish)
|
||||
2. Fill in the `translation` field for each entry:
|
||||
```json
|
||||
{
|
||||
"term": "Settings",
|
||||
"translation": "Configuración",
|
||||
"context": "Modals/Settings/SettingsModal.qml:147",
|
||||
"reference": "Modals/Settings/SettingsModal.qml:147",
|
||||
"comment": ""
|
||||
}
|
||||
```
|
||||
3. Import to POEditor or use directly in your translation workflow
|
||||
|
||||
## Regenerating Translation Files
|
||||
|
||||
To update the translation files after code changes:
|
||||
|
||||
```bash
|
||||
cd /home/brandon/.config/quickshell/DankMaterialShellGit/translations
|
||||
./extract_translations.py
|
||||
```
|
||||
|
||||
This will scan all QML files and regenerate `en.json` and `template.json`.
|
||||
|
||||
## Notes
|
||||
|
||||
- Strings are deduplicated - if the same string appears in multiple locations, references are merged
|
||||
- File paths are relative to the project root
|
||||
- Line numbers are preserved for accurate context
|
||||
- Empty strings and comments are reserved for translator notes
|
||||
@@ -0,0 +1,185 @@
|
||||
# Translation Workflow for DankMaterialShell
|
||||
|
||||
## POEditor Integration Guide
|
||||
|
||||
### Initial Setup
|
||||
|
||||
1. **Create POEditor Project**
|
||||
- Go to https://poeditor.com
|
||||
- Create new project "DankMaterialShell"
|
||||
- Add languages you want to support (es, fr, de, etc.)
|
||||
|
||||
2. **Import Source Strings**
|
||||
- Upload `en.json` as the source language
|
||||
- POEditor will detect 336 translatable strings
|
||||
- Format: Key-Value JSON
|
||||
|
||||
### Translation Workflow
|
||||
|
||||
#### Method 1: Using POEditor (Recommended)
|
||||
|
||||
1. **Upload source file**
|
||||
```bash
|
||||
# Upload en.json to POEditor
|
||||
```
|
||||
|
||||
2. **Translate in POEditor**
|
||||
- Use POEditor's web interface
|
||||
- Invite translators
|
||||
- Track progress per language
|
||||
|
||||
3. **Export translations**
|
||||
```bash
|
||||
# Download from POEditor as JSON
|
||||
# Save to translations/{language_code}.json
|
||||
# Example: translations/es.json, translations/fr.json
|
||||
```
|
||||
|
||||
4. **Test translations**
|
||||
```bash
|
||||
# Set your locale and restart shell
|
||||
export LANG=es_ES.UTF-8
|
||||
qs -p .
|
||||
```
|
||||
|
||||
#### Method 2: Manual Translation
|
||||
|
||||
1. **Copy template**
|
||||
```bash
|
||||
cp translations/template.json translations/es.json
|
||||
```
|
||||
|
||||
2. **Fill in translations**
|
||||
```json
|
||||
{
|
||||
"term": "Settings",
|
||||
"context": "Modals/Settings/SettingsModal.qml:147",
|
||||
"reference": "Modals/Settings/SettingsModal.qml:147",
|
||||
"comment": "",
|
||||
"translation": "Configuración"
|
||||
}
|
||||
```
|
||||
|
||||
3. **Upload to POEditor** (optional)
|
||||
|
||||
### Updating Translations After Code Changes
|
||||
|
||||
1. **Re-extract strings**
|
||||
```bash
|
||||
cd translations
|
||||
python3 extract_translations.py
|
||||
```
|
||||
|
||||
2. **Upload updated en.json to POEditor**
|
||||
- POEditor will detect new/removed strings
|
||||
- Preserves existing translations
|
||||
|
||||
3. **Download updated translations**
|
||||
|
||||
### Translation File Format
|
||||
|
||||
POEditor-compatible JSON format:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"term": "source string in English",
|
||||
"context": "file:line where it appears",
|
||||
"reference": "full/path/to/file.qml:123",
|
||||
"comment": "optional context for translators",
|
||||
"translation": "translated string"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Supported Languages
|
||||
|
||||
The shell will auto-detect your system locale. Supported format:
|
||||
- `es.json` → Spanish (es_ES, es_MX, etc.)
|
||||
- `fr.json` → French (fr_FR, fr_CA, etc.)
|
||||
- `de.json` → German
|
||||
- `zh.json` → Chinese
|
||||
- `ja.json` → Japanese
|
||||
- `pt.json` → Portuguese
|
||||
- etc.
|
||||
|
||||
### Testing Translations
|
||||
|
||||
1. **Change system locale**
|
||||
```bash
|
||||
export LANG=es_ES.UTF-8
|
||||
export LC_ALL=es_ES.UTF-8
|
||||
```
|
||||
|
||||
2. **Restart shell**
|
||||
```bash
|
||||
qs -p .
|
||||
```
|
||||
|
||||
3. **Verify translations appear correctly**
|
||||
|
||||
### File Structure
|
||||
|
||||
```
|
||||
translations/
|
||||
├── en.json # Source language (English)
|
||||
├── template.json # Empty template for new languages
|
||||
├── es.json # Spanish translation
|
||||
├── fr.json # French translation
|
||||
├── extract_translations.py # Auto-extraction script
|
||||
├── README.md # Technical documentation
|
||||
└── WORKFLOW.md # This file
|
||||
```
|
||||
|
||||
### Translation Statistics
|
||||
|
||||
- **Total strings:** 336 unique terms
|
||||
- **Most translated components:**
|
||||
- Settings UI: 202 strings (60%)
|
||||
- Weather: 25 strings (7%)
|
||||
- System monitors: Various
|
||||
- Modals: 43 strings (13%)
|
||||
|
||||
### POEditor API Integration (Advanced)
|
||||
|
||||
For automated sync, use POEditor's API:
|
||||
|
||||
```bash
|
||||
# Export from POEditor
|
||||
curl -X POST https://api.poeditor.com/v2/projects/export \
|
||||
-d api_token="YOUR_TOKEN" \
|
||||
-d id="PROJECT_ID" \
|
||||
-d language="es" \
|
||||
-d type="key_value_json"
|
||||
|
||||
# Import to POEditor
|
||||
curl -X POST https://api.poeditor.com/v2/projects/upload \
|
||||
-d api_token="YOUR_TOKEN" \
|
||||
-d id="PROJECT_ID" \
|
||||
-d updating="terms_translations" \
|
||||
-d language="es" \
|
||||
-F file=@"translations/es.json"
|
||||
```
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Context matters:** Use the reference field to understand where strings appear
|
||||
2. **Test before committing:** Always test translations in the actual UI
|
||||
3. **Keep synchronized:** Re-extract after significant UI changes
|
||||
4. **Preserve formatting:** Keep placeholders like `%1`, `{0}` intact
|
||||
5. **Cultural adaptation:** Some strings may need cultural context, not just literal translation
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
**Translations not loading?**
|
||||
- Check file exists: `~/.config/DankMaterialShell/translations/{language_code}.json`
|
||||
- Verify JSON syntax: `python3 -m json.tool translations/es.json`
|
||||
- Check console for errors: `qs -v -p .`
|
||||
|
||||
**Wrong language loading?**
|
||||
- Check system locale: `echo $LANG`
|
||||
- Verify file naming: Must match locale prefix (es_ES → es.json)
|
||||
|
||||
**Missing strings?**
|
||||
- Re-run extraction: `python3 extract_translations.py`
|
||||
- Compare with en.json to find new strings
|
||||
File diff suppressed because it is too large
Load Diff
+98
@@ -0,0 +1,98 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
from pathlib import Path
|
||||
from collections import defaultdict
|
||||
|
||||
def extract_qstr_strings(root_dir):
|
||||
translations = defaultdict(list)
|
||||
qstr_pattern = re.compile(r'qsTr\(["\']([^"\']+)["\']\)')
|
||||
i18n_pattern = re.compile(r'I18n\.tr\(["\']([^"\']+)["\']\)')
|
||||
|
||||
for qml_file in Path(root_dir).rglob('*.qml'):
|
||||
relative_path = qml_file.relative_to(root_dir)
|
||||
|
||||
with open(qml_file, 'r', encoding='utf-8') as f:
|
||||
for line_num, line in enumerate(f, 1):
|
||||
qstr_matches = qstr_pattern.findall(line)
|
||||
for match in qstr_matches:
|
||||
translations[match].append({
|
||||
'file': str(relative_path),
|
||||
'line': line_num
|
||||
})
|
||||
|
||||
i18n_matches = i18n_pattern.findall(line)
|
||||
for match in i18n_matches:
|
||||
translations[match].append({
|
||||
'file': str(relative_path),
|
||||
'line': line_num
|
||||
})
|
||||
|
||||
return translations
|
||||
|
||||
def create_poeditor_json(translations):
|
||||
poeditor_data = []
|
||||
|
||||
for term, occurrences in sorted(translations.items()):
|
||||
references = []
|
||||
|
||||
for occ in occurrences:
|
||||
ref = f"{occ['file']}:{occ['line']}"
|
||||
references.append(ref)
|
||||
|
||||
entry = {
|
||||
"term": term,
|
||||
"context": term,
|
||||
"reference": ", ".join(references),
|
||||
"comment": ""
|
||||
}
|
||||
poeditor_data.append(entry)
|
||||
|
||||
return poeditor_data
|
||||
|
||||
def create_template_json(translations):
|
||||
template_data = []
|
||||
|
||||
for term in sorted(translations.keys()):
|
||||
entry = {
|
||||
"term": term,
|
||||
"translation": "",
|
||||
"context": "",
|
||||
"reference": "",
|
||||
"comment": ""
|
||||
}
|
||||
template_data.append(entry)
|
||||
|
||||
return template_data
|
||||
|
||||
def main():
|
||||
script_dir = Path(__file__).parent
|
||||
root_dir = script_dir.parent
|
||||
translations_dir = script_dir
|
||||
|
||||
print("Extracting qsTr() strings from QML files...")
|
||||
translations = extract_qstr_strings(root_dir)
|
||||
|
||||
print(f"Found {len(translations)} unique strings")
|
||||
|
||||
poeditor_data = create_poeditor_json(translations)
|
||||
en_json_path = translations_dir / 'en.json'
|
||||
with open(en_json_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(poeditor_data, f, indent=2, ensure_ascii=False)
|
||||
print(f"Created source language file: {en_json_path}")
|
||||
|
||||
template_data = create_template_json(translations)
|
||||
template_json_path = translations_dir / 'template.json'
|
||||
with open(template_json_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(template_data, f, indent=2, ensure_ascii=False)
|
||||
print(f"Created template file: {template_json_path}")
|
||||
|
||||
print("\nSummary:")
|
||||
print(f" - Unique strings: {len(translations)}")
|
||||
print(f" - Total occurrences: {sum(len(occs) for occs in translations.values())}")
|
||||
print(f" - Source file: {en_json_path}")
|
||||
print(f" - Template file: {template_json_path}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
def replace_qstr_in_file(file_path, root_dir):
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
original_content = content
|
||||
relative_path = file_path.relative_to(root_dir)
|
||||
|
||||
qstr_pattern = re.compile(r'qsTr\("([^"]+)"\)')
|
||||
|
||||
def replacement(match):
|
||||
term = match.group(1)
|
||||
context = term
|
||||
return f'I18n.tr("{term}", "{context}")'
|
||||
|
||||
content = qstr_pattern.sub(replacement, content)
|
||||
|
||||
if content != original_content:
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
return True
|
||||
return False
|
||||
|
||||
def main():
|
||||
script_dir = Path(__file__).parent
|
||||
root_dir = script_dir.parent
|
||||
|
||||
modified_count = 0
|
||||
|
||||
for qml_file in root_dir.rglob('*.qml'):
|
||||
if 'translations' in str(qml_file):
|
||||
continue
|
||||
|
||||
try:
|
||||
if replace_qstr_in_file(qml_file, root_dir):
|
||||
modified_count += 1
|
||||
print(f"Modified: {qml_file.relative_to(root_dir)}")
|
||||
except Exception as e:
|
||||
print(f"Error processing {qml_file}: {e}")
|
||||
|
||||
print(f"\nTotal files modified: {modified_count}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user