1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 13:32:50 -05:00

i18n: move translation checking to pre-commit hook

This commit is contained in:
bbedward
2025-11-06 12:37:26 -05:00
parent d9652c7334
commit 4ddcf4391a
6 changed files with 433 additions and 259 deletions

27
.githooks/pre-commit Executable file
View File

@@ -0,0 +1,27 @@
#!/bin/bash
set -euo pipefail
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$HOOK_DIR/.." && pwd)"
cd "$REPO_ROOT"
if [[ -z "${POEDITOR_API_TOKEN:-}" ]] || [[ -z "${POEDITOR_PROJECT_ID:-}" ]]; then
echo "Skipping i18n sync check (POEDITOR_API_TOKEN or POEDITOR_PROJECT_ID not set)"
exit 0
fi
if ! command -v python3 &>/dev/null; then
echo "Warning: python3 not found, skipping i18n check"
exit 0
fi
if ! python3 scripts/i18nsync.py check 2>&1; then
echo ""
echo "Run: python3 scripts/i18nsync.py sync"
echo ""
exit 1
fi
exit 0

View File

@@ -1,192 +0,0 @@
name: POEditor Diff & Sync
on:
push:
branches: [ master ]
workflow_dispatch: {}
concurrency:
group: poeditor-sync
cancel-in-progress: false
jobs:
sync-translations:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install jq
run: sudo apt-get update && sudo apt-get install -y jq
- name: Extract source strings from codebase
env:
API_TOKEN: ${{ secrets.POEDITOR_API_TOKEN }}
PROJECT_ID: ${{ secrets.POEDITOR_PROJECT_ID }}
run: |
set -euo pipefail
echo "::group::Extracting strings from QML files"
python3 translations/extract_translations.py
echo "::endgroup::"
echo "::group::Checking for changes in en.json"
if [[ -f "translations/en.json" ]]; then
jq -S . "translations/en.json" > /tmp/en_new.json
if [[ -f "translations/en.json.orig" ]]; then
jq -S . "translations/en.json.orig" > /tmp/en_old.json
else
git show HEAD:translations/en.json > /tmp/en_old.json 2>/dev/null || echo "[]" > /tmp/en_old.json
jq -S . /tmp/en_old.json > /tmp/en_old.json.tmp && mv /tmp/en_old.json.tmp /tmp/en_old.json
fi
if diff -q /tmp/en_new.json /tmp/en_old.json >/dev/null 2>&1; then
echo "No changes in source strings"
echo "source_changed=false" >> "$GITHUB_OUTPUT"
else
echo "Detected changes in source strings"
echo "source_changed=true" >> "$GITHUB_OUTPUT"
echo "::group::Uploading source strings to POEditor"
RESP=$(curl -sS -X POST https://api.poeditor.com/v2/projects/upload \
-F api_token="$API_TOKEN" \
-F id="$PROJECT_ID" \
-F updating="terms" \
-F file=@"translations/en.json")
STATUS=$(echo "$RESP" | jq -r '.response.status')
if [[ "$STATUS" != "success" ]]; then
echo "::warning::POEditor upload failed: $RESP"
else
TERMS_ADDED=$(echo "$RESP" | jq -r '.result.terms.added // 0')
TERMS_UPDATED=$(echo "$RESP" | jq -r '.result.terms.updated // 0')
TERMS_DELETED=$(echo "$RESP" | jq -r '.result.terms.deleted // 0')
echo "Terms added: $TERMS_ADDED, updated: $TERMS_UPDATED, deleted: $TERMS_DELETED"
fi
echo "::endgroup::"
fi
else
echo "::warning::translations/en.json not found"
echo "source_changed=false" >> "$GITHUB_OUTPUT"
fi
echo "::endgroup::"
id: extract
- name: Commit and push source strings
if: steps.extract.outputs.source_changed == 'true'
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add translations/en.json translations/template.json
git commit -m "i18n: update source strings from codebase"
for attempt in 1 2 3; do
if git push; then
echo "Successfully pushed source string updates"
exit 0
fi
echo "Push attempt $attempt failed, pulling and retrying..."
git pull --rebase
sleep $((attempt*2))
done
echo "Failed to push after retries" >&2
exit 1
- name: Export and update translations from POEditor
env:
API_TOKEN: ${{ secrets.POEDITOR_API_TOKEN }}
PROJECT_ID: ${{ secrets.POEDITOR_PROJECT_ID }}
run: |
set -euo pipefail
LANGUAGES=(
"ja:translations/poexports/ja.json"
"zh-Hans:translations/poexports/zh_CN.json"
"zh-Hant:translations/poexports/zh_TW.json"
"pt-br:translations/poexports/pt.json"
"tr:translations/poexports/tr.json"
"it:translations/poexports/it.json"
)
ANY_CHANGED=false
for lang_pair in "${LANGUAGES[@]}"; do
IFS=':' read -r PO_LANG REPO_FILE <<< "$lang_pair"
echo "::group::Processing $PO_LANG"
RESP=$(curl -sS -X POST https://api.poeditor.com/v2/projects/export \
-d api_token="$API_TOKEN" \
-d id="$PROJECT_ID" \
-d language="$PO_LANG" \
-d type="key_value_json")
STATUS=$(echo "$RESP" | jq -r '.response.status')
if [[ "$STATUS" != "success" ]]; then
echo "POEditor export request failed for $PO_LANG: $RESP" >&2
continue
fi
URL=$(echo "$RESP" | jq -r '.result.url')
if [[ -z "$URL" || "$URL" == "null" ]]; then
echo "No export URL returned for $PO_LANG" >&2
continue
fi
curl -sS -L "$URL" -o "/tmp/po_export_${PO_LANG}.json"
jq -S . "/tmp/po_export_${PO_LANG}.json" > "/tmp/po_export_${PO_LANG}.norm.json"
if [[ -f "$REPO_FILE" ]]; then
jq -S . "$REPO_FILE" > "/tmp/repo_${PO_LANG}.norm.json" || echo "{}" > "/tmp/repo_${PO_LANG}.norm.json"
else
echo "{}" > "/tmp/repo_${PO_LANG}.norm.json"
fi
if diff -q "/tmp/po_export_${PO_LANG}.norm.json" "/tmp/repo_${PO_LANG}.norm.json" >/dev/null; then
echo "No changes for $PO_LANG"
else
echo "Detected changes for $PO_LANG"
mkdir -p "$(dirname "$REPO_FILE")"
cp "/tmp/po_export_${PO_LANG}.norm.json" "$REPO_FILE"
ANY_CHANGED=true
fi
echo "::endgroup::"
done
echo "any_changed=$ANY_CHANGED" >> "$GITHUB_OUTPUT"
id: export
- name: Commit and push translation updates
if: steps.export.outputs.any_changed == 'true'
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add translations/poexports/*.json
git commit -m "i18n: update translations"
for attempt in 1 2 3; do
if git push; then
echo "Successfully pushed translation updates"
exit 0
fi
echo "Push attempt $attempt failed, pulling and retrying..."
git pull --rebase
sleep $((attempt*2))
done
echo "Failed to push after retries" >&2
exit 1

View File

@@ -159,14 +159,53 @@ Contributions welcome! Bug fixes, new widgets, theme improvements, or docs - it
**Contributing Code:** **Contributing Code:**
1. Fork the repository 1. Fork the repository
2. Make your changes 2. Set up the development environment
3. Open a pull request 3. Make your changes
4. Open a pull request
**Contributing Documentation:** **Contributing Documentation:**
1. Fork the [DankLinux-Docs](https://github.com/AvengeMedia/DankLinux-Docs) repository 1. Fork the [DankLinux-Docs](https://github.com/AvengeMedia/DankLinux-Docs) repository
2. Update files in the `docs/` folder 2. Update files in the `docs/` folder
3. Open a pull request 3. Open a pull request
### Development Setup
**Requirements:**
- `python3` - Translation management
**Git Hooks:**
Enable the pre-commit hook to check translation sync status:
```bash
git config core.hooksPath .githooks
```
**Translation Workflow**
Set POEditor credentials:
```bash
export POEDITOR_API_TOKEN="your_api_token"
export POEDITOR_PROJECT_ID="your_project_id"
```
Sync translations before committing:
```bash
python3 scripts/i18nsync.py sync
```
This script:
- Extracts strings from QML files
- Uploads changed English terms to POEditor
- Downloads updated translations from POEditor
- Stages all changes for commit
The pre-commit hook will block commits if translations are out of sync and remind you to run the sync script.
Without POEditor credentials, the hook is skipped and commits proceed normally.
Check the [issues](https://github.com/AvengeMedia/DankMaterialShell/issues) or join the community. Check the [issues](https://github.com/AvengeMedia/DankMaterialShell/issues) or join the community.
--- ---

300
scripts/i18nsync.py Executable file
View File

@@ -0,0 +1,300 @@
#!/usr/bin/env python3
import sys
import json
import os
import subprocess
from pathlib import Path
from urllib import request, parse
REPO_ROOT = Path(__file__).parent.parent
EN_JSON = REPO_ROOT / "translations" / "en.json"
TEMPLATE_JSON = REPO_ROOT / "translations" / "template.json"
POEXPORTS_DIR = REPO_ROOT / "translations" / "poexports"
SYNC_STATE = REPO_ROOT / ".git" / "i18n_sync_state.json"
LANGUAGES = {
"ja": "ja.json",
"zh-Hans": "zh_CN.json",
"zh-Hant": "zh_TW.json",
"pt-br": "pt.json",
"tr": "tr.json",
"it": "it.json",
}
def error(msg):
print(f"\033[91mError: {msg}\033[0m", file=sys.stderr)
sys.exit(1)
def warn(msg):
print(f"\033[93mWarning: {msg}\033[0m", file=sys.stderr)
def info(msg):
print(f"\033[94m{msg}\033[0m")
def success(msg):
print(f"\033[92m{msg}\033[0m")
def get_env_or_error(var):
value = os.environ.get(var)
if not value:
error(f"{var} environment variable not set")
return value
def poeditor_request(endpoint, data):
url = f"https://api.poeditor.com/v2/{endpoint}"
data_bytes = parse.urlencode(data).encode()
req = request.Request(url, data=data_bytes, method="POST")
try:
with request.urlopen(req) as response:
return json.loads(response.read().decode())
except Exception as e:
error(f"POEditor API request failed: {e}")
def extract_strings():
info("Extracting strings from QML files...")
extract_script = REPO_ROOT / "translations" / "extract_translations.py"
if not extract_script.exists():
error(f"Extract script not found: {extract_script}")
result = subprocess.run([sys.executable, str(extract_script)], cwd=REPO_ROOT)
if result.returncode != 0:
error("String extraction failed")
if not EN_JSON.exists():
error(f"Extraction did not produce {EN_JSON}")
def normalize_json(file_path):
if not file_path.exists():
return {}
with open(file_path) as f:
return json.load(f)
def json_changed(file_path, new_data):
old_data = normalize_json(file_path)
return json.dumps(old_data, sort_keys=True) != json.dumps(new_data, sort_keys=True)
def upload_source_strings(api_token, project_id):
if not EN_JSON.exists():
warn("No en.json to upload")
return False
info("Uploading source strings to POEditor...")
with open(EN_JSON, 'rb') as f:
boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW'
body = (
f'--{boundary}\r\n'
f'Content-Disposition: form-data; name="api_token"\r\n\r\n'
f'{api_token}\r\n'
f'--{boundary}\r\n'
f'Content-Disposition: form-data; name="id"\r\n\r\n'
f'{project_id}\r\n'
f'--{boundary}\r\n'
f'Content-Disposition: form-data; name="updating"\r\n\r\n'
f'terms\r\n'
f'--{boundary}\r\n'
f'Content-Disposition: form-data; name="file"; filename="en.json"\r\n'
f'Content-Type: application/json\r\n\r\n'
).encode() + f.read() + f'\r\n--{boundary}--\r\n'.encode()
req = request.Request(
'https://api.poeditor.com/v2/projects/upload',
data=body,
headers={'Content-Type': f'multipart/form-data; boundary={boundary}'}
)
try:
with request.urlopen(req) as response:
result = json.loads(response.read().decode())
except Exception as e:
error(f"Upload failed: {e}")
if result.get('response', {}).get('status') != 'success':
error(f"POEditor upload failed: {result}")
terms = result.get('result', {}).get('terms', {})
added = terms.get('added', 0)
updated = terms.get('updated', 0)
deleted = terms.get('deleted', 0)
if added or updated or deleted:
success(f"POEditor updated: {added} added, {updated} updated, {deleted} deleted")
return True
else:
info("No changes uploaded to POEditor")
return False
def download_translations(api_token, project_id):
info("Downloading translations from POEditor...")
POEXPORTS_DIR.mkdir(parents=True, exist_ok=True)
any_changed = False
for po_lang, filename in LANGUAGES.items():
repo_file = POEXPORTS_DIR / filename
info(f"Fetching {po_lang}...")
export_resp = poeditor_request('projects/export', {
'api_token': api_token,
'id': project_id,
'language': po_lang,
'type': 'key_value_json'
})
if export_resp.get('response', {}).get('status') != 'success':
warn(f"Export request failed for {po_lang}")
continue
url = export_resp.get('result', {}).get('url')
if not url:
warn(f"No export URL for {po_lang}")
continue
try:
with request.urlopen(url) as response:
new_data = json.loads(response.read().decode())
except Exception as e:
warn(f"Failed to download {po_lang}: {e}")
continue
if json_changed(repo_file, new_data):
with open(repo_file, 'w') as f:
json.dump(new_data, f, ensure_ascii=False, indent=2, sort_keys=True)
f.write('\n')
success(f"Updated {filename}")
any_changed = True
else:
info(f"No changes for {filename}")
return any_changed
def check_sync_status():
api_token = get_env_or_error('POEDITOR_API_TOKEN')
project_id = get_env_or_error('POEDITOR_PROJECT_ID')
extract_strings()
current_en = normalize_json(EN_JSON)
if not SYNC_STATE.exists():
return True
with open(SYNC_STATE) as f:
state = json.load(f)
last_en = state.get('en_json', {})
last_translations = state.get('translations', {})
if json.dumps(current_en, sort_keys=True) != json.dumps(last_en, sort_keys=True):
return True
for po_lang, filename in LANGUAGES.items():
repo_file = POEXPORTS_DIR / filename
current_trans = normalize_json(repo_file)
last_trans = last_translations.get(filename, {})
if json.dumps(current_trans, sort_keys=True) != json.dumps(last_trans, sort_keys=True):
return True
export_resp = poeditor_request('projects/export', {
'api_token': api_token,
'id': project_id,
'language': list(LANGUAGES.keys())[0],
'type': 'key_value_json'
})
if export_resp.get('response', {}).get('status') == 'success':
url = export_resp.get('result', {}).get('url')
if url:
try:
with request.urlopen(url) as response:
remote_data = json.loads(response.read().decode())
first_file = POEXPORTS_DIR / list(LANGUAGES.values())[0]
local_data = normalize_json(first_file)
if json.dumps(remote_data, sort_keys=True) != json.dumps(local_data, sort_keys=True):
return True
except:
pass
return False
def save_sync_state():
state = {
'en_json': normalize_json(EN_JSON),
'translations': {}
}
for filename in LANGUAGES.values():
repo_file = POEXPORTS_DIR / filename
state['translations'][filename] = normalize_json(repo_file)
SYNC_STATE.parent.mkdir(parents=True, exist_ok=True)
with open(SYNC_STATE, 'w') as f:
json.dump(state, f, indent=2)
def main():
if len(sys.argv) < 2:
error("Usage: i18nsync.py [check|sync]")
command = sys.argv[1]
if command == "check":
try:
if check_sync_status():
error("i18n out of sync - run 'python3 scripts/i18nsync.py sync' first")
else:
success("i18n in sync")
sys.exit(0)
except SystemExit:
raise
except Exception as e:
error(f"Check failed: {e}")
elif command == "sync":
api_token = get_env_or_error('POEDITOR_API_TOKEN')
project_id = get_env_or_error('POEDITOR_PROJECT_ID')
extract_strings()
current_en = normalize_json(EN_JSON)
staged_en = {}
try:
result = subprocess.run(
['git', 'show', f':{EN_JSON.relative_to(REPO_ROOT)}'],
capture_output=True,
text=True,
cwd=REPO_ROOT
)
if result.returncode == 0:
staged_en = json.loads(result.stdout)
except:
pass
strings_changed = json.dumps(current_en, sort_keys=True) != json.dumps(staged_en, sort_keys=True)
if strings_changed:
upload_source_strings(api_token, project_id)
else:
info("No changes in source strings")
translations_changed = download_translations(api_token, project_id)
if strings_changed or translations_changed:
subprocess.run(['git', 'add', 'translations/'], cwd=REPO_ROOT)
save_sync_state()
success("Sync complete - changes staged for commit")
else:
save_sync_state()
info("Already in sync")
else:
error(f"Unknown command: {command}")
if __name__ == '__main__':
main()

View File

@@ -62,7 +62,7 @@
{ {
"term": "About", "term": "About",
"context": "About", "context": "About",
"reference": "Modules/Settings/AboutTab.qml:363, Modals/Settings/SettingsSidebar.qml:44", "reference": "Modals/Settings/SettingsSidebar.qml:44, Modules/Settings/AboutTab.qml:363",
"comment": "" "comment": ""
}, },
{ {
@@ -116,7 +116,7 @@
{ {
"term": "All", "term": "All",
"context": "All", "context": "All",
"reference": "Services/AppSearchService.qml:217, Services/AppSearchService.qml:233, Modules/AppDrawer/AppDrawerPopout.qml:47, Modules/AppDrawer/CategorySelector.qml:11, Modules/AppDrawer/AppLauncher.qml:16, Modules/AppDrawer/AppLauncher.qml:27, Modules/AppDrawer/AppLauncher.qml:28, Modules/AppDrawer/AppLauncher.qml:45, Modules/AppDrawer/AppLauncher.qml:46, Modules/AppDrawer/AppLauncher.qml:80, Modals/Spotlight/SpotlightModal.qml:61", "reference": "Services/AppSearchService.qml:217, Services/AppSearchService.qml:233, Modals/Spotlight/SpotlightModal.qml:61, Modules/AppDrawer/CategorySelector.qml:11, Modules/AppDrawer/AppLauncher.qml:16, Modules/AppDrawer/AppLauncher.qml:27, Modules/AppDrawer/AppLauncher.qml:28, Modules/AppDrawer/AppLauncher.qml:45, Modules/AppDrawer/AppLauncher.qml:46, Modules/AppDrawer/AppLauncher.qml:80, Modules/AppDrawer/AppDrawerPopout.qml:47",
"comment": "" "comment": ""
}, },
{ {
@@ -602,7 +602,7 @@
{ {
"term": "Cancel", "term": "Cancel",
"context": "Cancel", "context": "Cancel",
"reference": "Modals/BluetoothPairingModal.qml:251, Modals/PolkitAuthModal.qml:291, Modals/DankColorPickerModal.qml:518, Modals/WifiPasswordModal.qml:494, Modules/Settings/PluginBrowser.qml:627, Modals/FileBrowser/FileBrowserOverwriteDialog.qml:83", "reference": "Modals/BluetoothPairingModal.qml:251, Modals/DankColorPickerModal.qml:518, Modals/PolkitAuthModal.qml:291, Modals/WifiPasswordModal.qml:494, Modals/FileBrowser/FileBrowserOverwriteDialog.qml:83, Modules/Settings/PluginBrowser.qml:627",
"comment": "" "comment": ""
}, },
{ {
@@ -698,7 +698,7 @@
{ {
"term": "Close", "term": "Close",
"context": "Close", "context": "Close",
"reference": "Modals/NetworkInfoModal.qml:131, Modals/NetworkWiredInfoModal.qml:131, Modules/SystemUpdatePopout.qml:335, Modules/DankBar/Widgets/RunningApps.qml:788", "reference": "Modules/SystemUpdatePopout.qml:335, Modals/NetworkWiredInfoModal.qml:131, Modals/NetworkInfoModal.qml:131, Modules/DankBar/Widgets/RunningApps.qml:788",
"comment": "" "comment": ""
}, },
{ {
@@ -1220,7 +1220,7 @@
{ {
"term": "Do Not Disturb", "term": "Do Not Disturb",
"context": "Do Not Disturb", "context": "Do Not Disturb",
"reference": "Modules/Notifications/Center/NotificationSettings.qml:131, Modules/Notifications/Center/NotificationHeader.qml:41", "reference": "Modules/Notifications/Center/NotificationHeader.qml:41, Modules/Notifications/Center/NotificationSettings.qml:131",
"comment": "" "comment": ""
}, },
{ {
@@ -1568,7 +1568,7 @@
{ {
"term": "Font Family", "term": "Font Family",
"context": "Font Family", "context": "Font Family",
"reference": "Modules/Settings/ThemeColorsTab.qml:1015, Modules/Notepad/NotepadSettings.qml:220", "reference": "Modules/Notepad/NotepadSettings.qml:220, Modules/Settings/ThemeColorsTab.qml:1015",
"comment": "" "comment": ""
}, },
{ {
@@ -1934,7 +1934,7 @@
{ {
"term": "Launch", "term": "Launch",
"context": "Launch", "context": "Launch",
"reference": "Modules/AppDrawer/AppDrawerPopout.qml:746, Modals/Spotlight/SpotlightContextMenu.qml:251", "reference": "Modals/Spotlight/SpotlightContextMenu.qml:251, Modules/AppDrawer/AppDrawerPopout.qml:746",
"comment": "" "comment": ""
}, },
{ {
@@ -1946,7 +1946,7 @@
{ {
"term": "Launch on dGPU", "term": "Launch on dGPU",
"context": "Launch on dGPU", "context": "Launch on dGPU",
"reference": "Modules/AppDrawer/AppDrawerPopout.qml:806, Modules/Dock/DockContextMenu.qml:417, Modals/Spotlight/SpotlightContextMenu.qml:312", "reference": "Modals/Spotlight/SpotlightContextMenu.qml:312, Modules/AppDrawer/AppDrawerPopout.qml:806, Modules/Dock/DockContextMenu.qml:417",
"comment": "" "comment": ""
}, },
{ {
@@ -2102,7 +2102,7 @@
{ {
"term": "Matugen Palette", "term": "Matugen Palette",
"context": "Matugen Palette", "context": "Matugen Palette",
"reference": "Modules/Settings/PersonalizationTab.qml:1785, Modules/Settings/ThemeColorsTab.qml:629", "reference": "Modules/Settings/ThemeColorsTab.qml:629, Modules/Settings/PersonalizationTab.qml:1785",
"comment": "" "comment": ""
}, },
{ {
@@ -2282,7 +2282,7 @@
{ {
"term": "Network Information", "term": "Network Information",
"context": "Network Information", "context": "Network Information",
"reference": "Modals/NetworkInfoModal.qml:61, Modals/NetworkWiredInfoModal.qml:61", "reference": "Modals/NetworkWiredInfoModal.qml:61, Modals/NetworkInfoModal.qml:61",
"comment": "" "comment": ""
}, },
{ {
@@ -2462,7 +2462,7 @@
{ {
"term": "Notification Popups", "term": "Notification Popups",
"context": "Notification Popups", "context": "Notification Popups",
"reference": "Modules/Settings/WidgetTweaksTab.qml:585, Modules/Settings/DisplaysTab.qml:24", "reference": "Modules/Settings/DisplaysTab.qml:24, Modules/Settings/WidgetTweaksTab.qml:585",
"comment": "" "comment": ""
}, },
{ {
@@ -2672,7 +2672,7 @@
{ {
"term": "Pin to Dock", "term": "Pin to Dock",
"context": "Pin to Dock", "context": "Pin to Dock",
"reference": "Modules/AppDrawer/AppDrawerPopout.qml:609, Modules/Dock/DockContextMenu.qml:370, Modals/Spotlight/SpotlightContextMenu.qml:110, Modals/Spotlight/SpotlightContextMenu.qml:113", "reference": "Modals/Spotlight/SpotlightContextMenu.qml:110, Modals/Spotlight/SpotlightContextMenu.qml:113, Modules/AppDrawer/AppDrawerPopout.qml:609, Modules/Dock/DockContextMenu.qml:370",
"comment": "" "comment": ""
}, },
{ {
@@ -3002,7 +3002,7 @@
{ {
"term": "Save", "term": "Save",
"context": "Save", "context": "Save",
"reference": "Modules/Notepad/Notepad.qml:480, Modules/Notepad/NotepadTextEditor.qml:511, Modals/FileBrowser/FileBrowserSaveRow.qml:55", "reference": "Modals/FileBrowser/FileBrowserSaveRow.qml:55, Modules/Notepad/NotepadTextEditor.qml:511, Modules/Notepad/Notepad.qml:480",
"comment": "" "comment": ""
}, },
{ {
@@ -3140,7 +3140,7 @@
{ {
"term": "Select the palette algorithm used for wallpaper-based colors", "term": "Select the palette algorithm used for wallpaper-based colors",
"context": "Select the palette algorithm used for wallpaper-based colors", "context": "Select the palette algorithm used for wallpaper-based colors",
"reference": "Modules/Settings/PersonalizationTab.qml:1786, Modules/Settings/ThemeColorsTab.qml:630", "reference": "Modules/Settings/ThemeColorsTab.qml:630, Modules/Settings/PersonalizationTab.qml:1786",
"comment": "" "comment": ""
}, },
{ {
@@ -3176,7 +3176,7 @@
{ {
"term": "Settings", "term": "Settings",
"context": "Settings", "context": "Settings",
"reference": "Services/AppSearchService.qml:176, Modules/DankDash/DankDashPopout.qml:249, Modals/Settings/SettingsModal.qml:168", "reference": "Services/AppSearchService.qml:176, Modals/Settings/SettingsModal.qml:168, Modules/DankDash/DankDashPopout.qml:249",
"comment": "" "comment": ""
}, },
{ {
@@ -3722,7 +3722,7 @@
{ {
"term": "Unpin from Dock", "term": "Unpin from Dock",
"context": "Unpin from Dock", "context": "Unpin from Dock",
"reference": "Modules/AppDrawer/AppDrawerPopout.qml:609, Modules/Dock/DockContextMenu.qml:370, Modals/Spotlight/SpotlightContextMenu.qml:113", "reference": "Modals/Spotlight/SpotlightContextMenu.qml:113, Modules/AppDrawer/AppDrawerPopout.qml:609, Modules/Dock/DockContextMenu.qml:370",
"comment": "" "comment": ""
}, },
{ {

View File

@@ -297,10 +297,10 @@
"CPU usage indicator": "CPU kullanım göstergesi" "CPU usage indicator": "CPU kullanım göstergesi"
}, },
"CUPS Insecure Filter Warning": { "CUPS Insecure Filter Warning": {
"CUPS Insecure Filter Warning": "" "CUPS Insecure Filter Warning": "CUPS Güvenli Olmayan Filtre Uyarısı"
}, },
"CUPS Missing Filter Warning": { "CUPS Missing Filter Warning": {
"CUPS Missing Filter Warning": "" "CUPS Missing Filter Warning": "CUPS Eksik Filtre Uyarısı"
}, },
"Cancel": { "Cancel": {
"Cancel": "İptal" "Cancel": "İptal"
@@ -426,7 +426,7 @@
"Connected Displays": "Bağlı Ekranlar" "Connected Displays": "Bağlı Ekranlar"
}, },
"Connecting to Device": { "Connecting to Device": {
"Connecting to Device": "" "Connecting to Device": "Cihaza Bağlanma"
}, },
"Connection failed. Check password and try again.": { "Connection failed. Check password and try again.": {
"Connection failed. Check password and try again.": "Bağlantı başarısız. Parolayı kontrol edin ve tekrar deneyin." "Connection failed. Check password and try again.": "Bağlantı başarısız. Parolayı kontrol edin ve tekrar deneyin."
@@ -468,7 +468,7 @@
"Corner Radius (0 = square corners)": "Köşe Yarıçapı (0 = kare köşeler)" "Corner Radius (0 = square corners)": "Köşe Yarıçapı (0 = kare köşeler)"
}, },
"Cover Open": { "Cover Open": {
"Cover Open": "" "Cover Open": "Kapak Açık"
}, },
"Create Dir": { "Create Dir": {
"Create Dir": "Dizin Oluştur" "Create Dir": "Dizin Oluştur"
@@ -639,7 +639,7 @@
"Donate on Ko-fi": "Ko-fi üzerinden bağış yap" "Donate on Ko-fi": "Ko-fi üzerinden bağış yap"
}, },
"Door Open": { "Door Open": {
"Door Open": "" "Door Open": "Kapıık"
}, },
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": { "Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": {
"Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": "Widget'ları sürükleyerek bölümler içinde yeniden sıralayın. Göz simgesini kullanarak widget'ları gizleyin/gösterin (aralıkları korur) veya X simgesini kullanarak tamamen kaldırın." "Drag widgets to reorder within sections. Use the eye icon to hide/show widgets (maintains spacing), or X to remove them completely.": "Widget'ları sürükleyerek bölümler içinde yeniden sıralayın. Göz simgesini kullanarak widget'ları gizleyin/gösterin (aralıkları korur) veya X simgesini kullanarak tamamen kaldırın."
@@ -717,7 +717,7 @@
"Enter password for ": "Parolayı girin " "Enter password for ": "Parolayı girin "
}, },
"Error": { "Error": {
"Error": "" "Error": "Hata"
}, },
"Exclusive Zone Offset": { "Exclusive Zone Offset": {
"Exclusive Zone Offset": "Özel Bölge Ofseti" "Exclusive Zone Offset": "Özel Bölge Ofseti"
@@ -732,10 +732,10 @@
"Failed to activate configuration": "Yapılandırma etkinleştirilemedi" "Failed to activate configuration": "Yapılandırma etkinleştirilemedi"
}, },
"Failed to cancel all jobs": { "Failed to cancel all jobs": {
"Failed to cancel all jobs": "" "Failed to cancel all jobs": "Tüm işler iptal edemedi"
}, },
"Failed to cancel selected job": { "Failed to cancel selected job": {
"Failed to cancel selected job": "" "Failed to cancel selected job": "Seçilen işler iptal edemedi"
}, },
"Failed to connect VPN": { "Failed to connect VPN": {
"Failed to connect VPN": "VPN bağlantısı kurulamadı" "Failed to connect VPN": "VPN bağlantısı kurulamadı"
@@ -756,13 +756,13 @@
"Failed to enable WiFi": "WiFi etkinleştirilemedi" "Failed to enable WiFi": "WiFi etkinleştirilemedi"
}, },
"Failed to pause printer": { "Failed to pause printer": {
"Failed to pause printer": "" "Failed to pause printer": "Yazıcıyı duraklatma başarısız"
}, },
"Failed to remove device": { "Failed to remove device": {
"Failed to remove device": "Cihaz kaldırılamadı" "Failed to remove device": "Cihaz kaldırılamadı"
}, },
"Failed to resume printer": { "Failed to resume printer": {
"Failed to resume printer": "" "Failed to resume printer": "Yazıcıyı devam ettirme başarısız"
}, },
"Failed to set profile image": { "Failed to set profile image": {
"Failed to set profile image": "Profil resmi ayarlanamadı" "Failed to set profile image": "Profil resmi ayarlanamadı"
@@ -912,7 +912,7 @@
"Icon Theme": "Simge Teması" "Icon Theme": "Simge Teması"
}, },
"Idle": { "Idle": {
"Idle": "" "Idle": "Boşta"
}, },
"Idle Inhibitor": { "Idle Inhibitor": {
"Idle Inhibitor": "Boşta Kalma Engelleyici" "Idle Inhibitor": "Boşta Kalma Engelleyici"
@@ -945,7 +945,7 @@
"Install plugins from the DMS plugin registry": "DMS eklenti deposundan eklentiler yükle" "Install plugins from the DMS plugin registry": "DMS eklenti deposundan eklentiler yükle"
}, },
"Interlock Open": { "Interlock Open": {
"Interlock Open": "" "Interlock Open": "Kilit Açık"
}, },
"Internet": { "Internet": {
"Internet": "İnternet" "Internet": "İnternet"
@@ -957,10 +957,10 @@
"Invert on mode change": "Mod değişikliğinde ters çevir" "Invert on mode change": "Mod değişikliğinde ters çevir"
}, },
"Jobs": { "Jobs": {
"Jobs": "" "Jobs": "İşler"
}, },
"Jobs: ": { "Jobs: ": {
"Jobs: ": "" "Jobs: ": "İşler:"
}, },
"Keyboard Layout Name": { "Keyboard Layout Name": {
"Keyboard Layout Name": "Klavye Düzeni Adı" "Keyboard Layout Name": "Klavye Düzeni Adı"
@@ -1059,16 +1059,16 @@
"Manual Show/Hide": "Manuel Göster/Gizle" "Manual Show/Hide": "Manuel Göster/Gizle"
}, },
"Marker Supply Empty": { "Marker Supply Empty": {
"Marker Supply Empty": "" "Marker Supply Empty": "Sarf Malzemesi Boş"
}, },
"Marker Supply Low": { "Marker Supply Low": {
"Marker Supply Low": "" "Marker Supply Low": "Sarf Malzemesi Az"
}, },
"Marker Waste Almost Full": { "Marker Waste Almost Full": {
"Marker Waste Almost Full": "" "Marker Waste Almost Full": "Atık Haznesi Neredeyse Dolu"
}, },
"Marker Waste Full": { "Marker Waste Full": {
"Marker Waste Full": "" "Marker Waste Full": "Atık Haznesi Dolu"
}, },
"Material Colors": { "Material Colors": {
"Material Colors": "Materyal Renkleri" "Material Colors": "Materyal Renkleri"
@@ -1092,16 +1092,16 @@
"Media Controls": "Medya Kontrolleri" "Media Controls": "Medya Kontrolleri"
}, },
"Media Empty": { "Media Empty": {
"Media Empty": "" "Media Empty": "Kağıt Bitti"
}, },
"Media Jam": { "Media Jam": {
"Media Jam": "" "Media Jam": "Kağıt Sıkışması"
}, },
"Media Low": { "Media Low": {
"Media Low": "" "Media Low": "Kağıt Az"
}, },
"Media Needed": { "Media Needed": {
"Media Needed": "" "Media Needed": "Kağıt Gerekli"
}, },
"Media Player Settings": { "Media Player Settings": {
"Media Player Settings": "Medya Oynatıcı Ayarları" "Media Player Settings": "Medya Oynatıcı Ayarları"
@@ -1143,7 +1143,7 @@
"Mount": "Bağlı" "Mount": "Bağlı"
}, },
"Moving to Paused": { "Moving to Paused": {
"Moving to Paused": "" "Moving to Paused": "Duraklatılıyor"
}, },
"Muted palette with subdued, calming tones.": { "Muted palette with subdued, calming tones.": {
"Muted palette with subdued, calming tones.": "Sakin ve yatıştırıcı tonlara sahip, yumuşak renk paleti." "Muted palette with subdued, calming tones.": "Sakin ve yatıştırıcı tonlara sahip, yumuşak renk paleti."
@@ -1230,10 +1230,10 @@
"No plugins found.": "Eklenti bulunamadı." "No plugins found.": "Eklenti bulunamadı."
}, },
"No printer found": { "No printer found": {
"No printer found": "" "No printer found": "Yazıcı Bulunamadı"
}, },
"None": { "None": {
"None": "" "None": "Hiçbiri"
}, },
"Normal Priority": { "Normal Priority": {
"Normal Priority": "Normal Öncelik" "Normal Priority": "Normal Öncelik"
@@ -1281,7 +1281,7 @@
"Office": "Ofis" "Office": "Ofis"
}, },
"Offline Report": { "Offline Report": {
"Offline Report": "" "Offline Report": "Çevrimdışı Rapor"
}, },
"On-Screen Displays": { "On-Screen Displays": {
"On-Screen Displays": "Ekran Üstü Gösterimler" "On-Screen Displays": "Ekran Üstü Gösterimler"
@@ -1302,16 +1302,16 @@
"Open search bar to find text": "Metin bulmak için arama çubuğunu aç" "Open search bar to find text": "Metin bulmak için arama çubuğunu aç"
}, },
"Other": { "Other": {
"Other": "" "Other": "Diğer"
}, },
"Output Area Almost Full": { "Output Area Almost Full": {
"Output Area Almost Full": "" "Output Area Almost Full": "Çıkış Alanı Neredeyse Dolu"
}, },
"Output Area Full": { "Output Area Full": {
"Output Area Full": "" "Output Area Full": "Çıkış Alanı Dolu"
}, },
"Output Tray Missing": { "Output Tray Missing": {
"Output Tray Missing": "" "Output Tray Missing": "Çıktı Tepsisi Yok"
}, },
"Overview": { "Overview": {
"Overview": "Genel Görünüm" "Overview": "Genel Görünüm"
@@ -1338,10 +1338,10 @@
"Password": "Parola" "Password": "Parola"
}, },
"Pause": { "Pause": {
"Pause": "" "Pause": "Duraklat"
}, },
"Paused": { "Paused": {
"Paused": "" "Paused": "Duraklatıldı"
}, },
"Per-Mode Wallpapers": { "Per-Mode Wallpapers": {
"Per-Mode Wallpapers": "Moda Özel Duvar Kağıtları" "Per-Mode Wallpapers": "Moda Özel Duvar Kağıtları"
@@ -1434,13 +1434,13 @@
"Primary": "Birincil" "Primary": "Birincil"
}, },
"Print Server not available": { "Print Server not available": {
"Print Server not available": "" "Print Server not available": "Yazıcı Sunucusu Kullanılamıyor"
}, },
"Printers": { "Printers": {
"Printers": "" "Printers": "Yazıcılar"
}, },
"Printers: ": { "Printers: ": {
"Printers: ": "" "Printers: ": "Yazıcılar:"
}, },
"Privacy Indicator": { "Privacy Indicator": {
"Privacy Indicator": "Gizlilik Göstergesi" "Privacy Indicator": "Gizlilik Göstergesi"
@@ -1449,7 +1449,7 @@
"Process": "Süreç" "Process": "Süreç"
}, },
"Processing": { "Processing": {
"Processing": "" "Processing": "İşleniyor"
}, },
"Profile Image Error": { "Profile Image Error": {
"Profile Image Error": "Profil Resmi Hatası" "Profile Image Error": "Profil Resmi Hatası"
@@ -1476,7 +1476,7 @@
"Rain Chance": "Yağış İhtimali" "Rain Chance": "Yağış İhtimali"
}, },
"Reason": { "Reason": {
"Reason": "" "Reason": "Sebep"
}, },
"Reboot": { "Reboot": {
"Reboot": "Yeniden Başlat" "Reboot": "Yeniden Başlat"
@@ -1497,7 +1497,7 @@
"Remove": "Kaldır" "Remove": "Kaldır"
}, },
"Report": { "Report": {
"Report": "" "Report": "Rapor"
}, },
"Request confirmation on power off, restart, suspend, hibernate and logout actions": { "Request confirmation on power off, restart, suspend, hibernate and logout actions": {
"Request confirmation on power off, restart, suspend, hibernate and logout actions": "Kapatma, yeniden başlatma, askıya alma, hazırda bekletme ve oturumu kapatma işlemlerinde onay iste" "Request confirmation on power off, restart, suspend, hibernate and logout actions": "Kapatma, yeniden başlatma, askıya alma, hazırda bekletme ve oturumu kapatma işlemlerinde onay iste"
@@ -1509,7 +1509,7 @@
"Resources": "Kaynaklar" "Resources": "Kaynaklar"
}, },
"Resume": { "Resume": {
"Resume": "" "Resume": "Sürdür"
}, },
"Right": { "Right": {
"Right": "Sağ" "Right": "Sağ"
@@ -1692,7 +1692,7 @@
"Shows when microphone, camera, or screen sharing is active": "Mikrofon, kamera veya ekran paylaşımı aktif olduğunda gösterir" "Shows when microphone, camera, or screen sharing is active": "Mikrofon, kamera veya ekran paylaşımı aktif olduğunda gösterir"
}, },
"Shutdown": { "Shutdown": {
"Shutdown": "" "Shutdown": "Kapat"
}, },
"Size": { "Size": {
"Size": "Boyut" "Size": "Boyut"
@@ -1713,7 +1713,7 @@
"Spacing": "Boşluk" "Spacing": "Boşluk"
}, },
"Spool Area Full": { "Spool Area Full": {
"Spool Area Full": "" "Spool Area Full": "Aktarım Alanı Dolu"
}, },
"Square Corners": { "Square Corners": {
"Square Corners": "Kare Köşeler" "Square Corners": "Kare Köşeler"
@@ -1728,13 +1728,13 @@
"Status": "Durum" "Status": "Durum"
}, },
"Stopped": { "Stopped": {
"Stopped": "" "Stopped": "Durduruldu"
}, },
"Stopped Partly": { "Stopped Partly": {
"Stopped Partly": "" "Stopped Partly": "Kısmen Durduruldu"
}, },
"Stopping": { "Stopping": {
"Stopping": "" "Stopping": "Durduruluyor"
}, },
"Storage & Disks": { "Storage & Disks": {
"Storage & Disks": "Depolama & Diskler" "Storage & Disks": "Depolama & Diskler"
@@ -1830,7 +1830,7 @@
"The below settings will modify your GTK and Qt settings. If you wish to preserve your current configurations, please back them up (qt5ct.conf|qt6ct.conf and ~/.config/gtk-3.0|gtk-4.0).": "Aşağıdaki ayarlar GTK ve Qt ayarlarınızı değiştirecektir. Mevcut yapılandırmalarınızı korumak istiyorsanız, lütfen yedekleyin (qt5ct.conf|qt6ct.conf ve ~/.config/gtk-3.0|gtk-4.0)." "The below settings will modify your GTK and Qt settings. If you wish to preserve your current configurations, please back them up (qt5ct.conf|qt6ct.conf and ~/.config/gtk-3.0|gtk-4.0).": "Aşağıdaki ayarlar GTK ve Qt ayarlarınızı değiştirecektir. Mevcut yapılandırmalarınızı korumak istiyorsanız, lütfen yedekleyin (qt5ct.conf|qt6ct.conf ve ~/.config/gtk-3.0|gtk-4.0)."
}, },
"The job queue of this printer is empty": { "The job queue of this printer is empty": {
"The job queue of this printer is empty": "" "The job queue of this printer is empty": "Bu yazıcının iş kuyruğu boş"
}, },
"Theme & Colors": { "Theme & Colors": {
"Theme & Colors": "Tema & Renkler" "Theme & Colors": "Tema & Renkler"
@@ -1854,7 +1854,7 @@
"Time & Weather": "Zaman & Hava Durumu" "Time & Weather": "Zaman & Hava Durumu"
}, },
"Timed Out": { "Timed Out": {
"Timed Out": "" "Timed Out": "Zaman Aşımı"
}, },
"To Full": { "To Full": {
"To Full": "Dolmasına" "To Full": "Dolmasına"
@@ -1875,10 +1875,10 @@
"Tomorrow": "Yarın" "Tomorrow": "Yarın"
}, },
"Toner Empty": { "Toner Empty": {
"Toner Empty": "" "Toner Empty": "Toner Boş"
}, },
"Toner Low": { "Toner Low": {
"Toner Low": "" "Toner Low": "Toner Az"
}, },
"Top": { "Top": {
"Top": "Üst" "Top": "Üst"
@@ -2010,7 +2010,7 @@
"Wallpapers": "Duvar Kağıtları" "Wallpapers": "Duvar Kağıtları"
}, },
"Warning": { "Warning": {
"Warning": "" "Warning": "Uyarı"
}, },
"Wave Progress Bars": { "Wave Progress Bars": {
"Wave Progress Bars": "Dalga İlerleme Çubukları" "Wave Progress Bars": "Dalga İlerleme Çubukları"