diff --git a/.editorconfig b/.editorconfig
index dedc5722..3ad9d05b 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -292,3 +292,8 @@ indent_size = 2
end_of_line = lf
[*.{cmd,bat}]
end_of_line = crlf
+
+# YAML files
+[*.{yml,yaml}]
+indent_size = 2
+end_of_line = lf
diff --git a/.github/workflows/localization-check.yml b/.github/workflows/localization-check.yml
new file mode 100644
index 00000000..cc5201ab
--- /dev/null
+++ b/.github/workflows/localization-check.yml
@@ -0,0 +1,42 @@
+name: Localization Check
+on:
+ push:
+ branches: [ develop ]
+ paths:
+ - 'src/Resources/Locales/**'
+ - 'README.md'
+ workflow_dispatch:
+ workflow_call:
+
+jobs:
+ localization-check:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20.x'
+
+ - name: Install dependencies
+ run: npm install fs-extra@11.2.0 path@0.12.7 xml2js@0.6.2
+
+ - name: Run localization check
+ run: node build/scripts/localization-check.js
+
+ - name: Commit changes
+ run: |
+ git config --global user.name 'github-actions[bot]'
+ git config --global user.email 'github-actions[bot]@users.noreply.github.com'
+ if [ -n "$(git status --porcelain)" ]; then
+ git add README.md TRANSLATION.md
+ git commit -m 'doc: Update translation status and missing keys'
+ git push
+ else
+ echo "No changes to commit"
+ fi
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 95bde92d..0c66b11e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,10 @@ ehthumbs_vista.db
bin/
obj/
+# ignore ci node files
+node_modules/
+package.json
+package-lock.json
build/resources/
build/SourceGit/
@@ -32,4 +36,4 @@ build/*.tar.gz
build/*.deb
build/*.rpm
build/*.AppImage
-SourceGit.app/
\ No newline at end of file
+SourceGit.app/
diff --git a/README.md b/README.md
index 62427db4..95005a5c 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,10 @@
> [!WARNING]
> **Linux** only tested on **Debian 12** on both **X11** & **Wayland**.
+## Translation Status
+
+[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md) [![de__DE](https://img.shields.io/badge/de__DE-98.95%25-yellow)](TRANSLATION.md) [![fr__FR](https://img.shields.io/badge/fr__FR-90.36%25-yellow)](TRANSLATION.md) [![pt__BR](https://img.shields.io/badge/pt__BR-93.52%25-yellow)](TRANSLATION.md) [![ru__RU](https://img.shields.io/badge/ru__RU-98.80%25-yellow)](TRANSLATION.md) [![zh__CN](https://img.shields.io/badge/zh__CN-99.10%25-yellow)](TRANSLATION.md) [![zh__TW](https://img.shields.io/badge/zh__TW-99.70%25-yellow)](TRANSLATION.md)
+
## How to Use
**To use this tool, you need to install Git(>=2.23.0) first.**
diff --git a/SourceGit.sln b/SourceGit.sln
index 10c94e36..abd42aee 100644
--- a/SourceGit.sln
+++ b/SourceGit.sln
@@ -16,6 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
.github\workflows\ci.yml = .github\workflows\ci.yml
.github\workflows\package.yml = .github\workflows\package.yml
.github\workflows\release.yml = .github\workflows\release.yml
+ .github\workflows\localization-check.yml = .github\workflows\localization-check.yml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{49A7C2D6-558C-4FAA-8F5D-EEE81497AED7}"
diff --git a/TRANSLATION.md b/TRANSLATION.md
new file mode 100644
index 00000000..aa9cc7af
--- /dev/null
+++ b/TRANSLATION.md
@@ -0,0 +1,183 @@
+### de_DE.axaml: 98.95%
+
+
+
+Missing Keys
+
+- Text.Configure.Git.EnableSignOff
+- Text.Configure.IssueTracker.AddSampleGitLabIssue
+- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
+- Text.Preference.Advanced
+- Text.Preference.AI.AnalyzeDiffPrompt
+- Text.Preference.AI.GenerateSubjectPrompt
+- Text.WorkingCopy.ConfirmCommitWithoutFiles
+
+
+
+### fr_FR.axaml: 90.36%
+
+
+
+Missing Keys
+
+- Text.About.Chart
+- Text.AIAssistant
+- Text.AIAssistant.Tip
+- Text.CherryPick.AppendSourceToMessage
+- Text.CherryPick.Mainline
+- Text.CherryPick.Mainline.Tips
+- Text.CommitCM.CherryPickMultiple
+- Text.CommitCM.SquashCommitsSinceThis
+- Text.CommitDetail.Info.WebLinks
+- Text.Configure.Git.DefaultRemote
+- Text.Configure.Git.EnableSignOff
+- Text.Configure.IssueTracker.AddSampleGitLabIssue
+- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
+- Text.ConfigureWorkspace
+- Text.ConfigureWorkspace.Color
+- Text.ConfigureWorkspace.Restore
+- Text.ConventionalCommit
+- Text.ConventionalCommit.BreakingChanges
+- Text.ConventionalCommit.ClosedIssue
+- Text.ConventionalCommit.Detail
+- Text.ConventionalCommit.Scope
+- Text.ConventionalCommit.ShortDescription
+- Text.ConventionalCommit.Type
+- Text.Diff.IgnoreWhitespace
+- Text.Discard.IncludeIgnored
+- Text.FileHistory.FileChange
+- Text.GitLFS.Locks.OnlyMine
+- Text.Histories.Header.AuthorTime
+- Text.Histories.Tips
+- Text.Histories.Tips.MacOS
+- Text.Histories.Tips.Prefix
+- Text.Hotkeys.Repo.CommitWithAutoStage
+- Text.Hotkeys.Repo.DiscardSelected
+- Text.MoveRepositoryNode
+- Text.MoveRepositoryNode.Target
+- Text.Preference.Advanced
+- Text.Preference.AI
+- Text.Preference.AI.AnalyzeDiffPrompt
+- Text.Preference.AI.ApiKey
+- Text.Preference.AI.GenerateSubjectPrompt
+- Text.Preference.AI.Model
+- Text.Preference.AI.Server
+- Text.Preference.General.ShowAuthorTime
+- Text.Preference.Integration
+- Text.Preference.Shell
+- Text.Preference.Shell.Type
+- Text.Preference.Shell.Path
+- Text.Repository.AutoFetching
+- Text.Repository.EnableReflog
+- Text.Repository.Search.InCurrentBranch
+- Text.ScanRepositories
+- Text.ScanRepositories.RootDir
+- Text.Squash.Into
+- Text.Stash.OnlyStagedChanges
+- Text.Stash.TipForSelectedFiles
+- Text.Statistics.Overview
+- Text.TagCM.CopyMessage
+- Text.Welcome.Move
+- Text.Welcome.ScanDefaultCloneDir
+- Text.WorkingCopy.CommitTip
+- Text.WorkingCopy.CommitWithAutoStage
+- Text.WorkingCopy.ConfirmCommitWithoutFiles
+- Text.Workspace
+- Text.Workspace.Configure
+
+
+
+### pt_BR.axaml: 93.52%
+
+
+
+Missing Keys
+
+- Text.About.Chart
+- Text.AIAssistant
+- Text.AIAssistant.Tip
+- Text.CherryPick.AppendSourceToMessage
+- Text.CherryPick.Mainline
+- Text.CherryPick.Mainline.Tips
+- Text.CommitCM.CherryPickMultiple
+- Text.CommitCM.SquashCommitsSinceThis
+- Text.CommitDetail.Info.ContainsIn
+- Text.CommitDetail.Info.ContainsIn.Title
+- Text.CommitDetail.Info.WebLinks
+- Text.Configure.Git.DefaultRemote
+- Text.Configure.Git.EnableSignOff
+- Text.Configure.IssueTracker.AddSampleGitLabIssue
+- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
+- Text.ConfigureWorkspace
+- Text.ConfigureWorkspace.Color
+- Text.ConfigureWorkspace.Restore
+- Text.ConventionalCommit
+- Text.ConventionalCommit.BreakingChanges
+- Text.ConventionalCommit.ClosedIssue
+- Text.ConventionalCommit.Detail
+- Text.ConventionalCommit.Scope
+- Text.ConventionalCommit.ShortDescription
+- Text.ConventionalCommit.Type
+- Text.CopyAllText
+- Text.Discard.IncludeIgnored
+- Text.FileHistory.FileContent
+- Text.FileHistory.FileChange
+- Text.GitLFS.Locks.OnlyMine
+- Text.MoveRepositoryNode
+- Text.MoveRepositoryNode.Target
+- Text.Preference.Advanced
+- Text.Push.CheckSubmodules
+- Text.Squash.Into
+- Text.Stash.OnlyStagedChanges
+- Text.Stash.TipForSelectedFiles
+- Text.Statistics.Overview
+- Text.TagCM.CopyMessage
+- Text.WorkingCopy.Staged.UnstageAll
+- Text.WorkingCopy.Unstaged
+- Text.WorkingCopy.Unstaged.Stage
+- Text.WorkingCopy.Unstaged.StageAll
+
+
+
+### ru_RU.axaml: 98.80%
+
+
+
+Missing Keys
+
+- Text.Configure.Git.EnableSignOff
+- Text.Configure.IssueTracker.AddSampleGitLabIssue
+- Text.Configure.IssueTracker.AddSampleGitLabMergeRequest
+- Text.Preference.Advanced
+- Text.Preference.AI.AnalyzeDiffPrompt
+- Text.Preference.AI.GenerateSubjectPrompt
+- Text.Repository.EnableReflog
+- Text.WorkingCopy.ConfirmCommitWithoutFiles
+
+
+
+### zh_CN.axaml: 99.10%
+
+
+
+Missing Keys
+
+- Text.Preference.AI
+- Text.Preference.AI.AnalyzeDiffPrompt
+- Text.Preference.AI.ApiKey
+- Text.Preference.AI.GenerateSubjectPrompt
+- Text.Preference.AI.Model
+- Text.Preference.AI.Server
+
+
+
+### zh_TW.axaml: 99.70%
+
+
+
+Missing Keys
+
+- Text.Preference.AI.AnalyzeDiffPrompt
+- Text.Preference.AI.GenerateSubjectPrompt
+
+
diff --git a/build/scripts/localization-check.js b/build/scripts/localization-check.js
new file mode 100644
index 00000000..45db82be
--- /dev/null
+++ b/build/scripts/localization-check.js
@@ -0,0 +1,59 @@
+const fs = require('fs-extra');
+const path = require('path');
+const xml2js = require('xml2js');
+
+const repoRoot = path.join(__dirname, '../../');
+const localesDir = path.join(repoRoot, 'src/Resources/Locales');
+const enUSFile = path.join(localesDir, 'en_US.axaml');
+const outputFile = path.join(repoRoot, 'TRANSLATION.md');
+const readmeFile = path.join(repoRoot, 'README.md');
+
+const parser = new xml2js.Parser();
+
+async function parseXml(filePath) {
+ const data = await fs.readFile(filePath);
+ return parser.parseStringPromise(data);
+}
+
+async function calculateTranslationRate() {
+ const enUSData = await parseXml(enUSFile);
+ const enUSKeys = new Set(enUSData.ResourceDictionary['x:String'].map(item => item.$['x:Key']));
+
+ const translationRates = [];
+ const badges = [];
+
+ const files = (await fs.readdir(localesDir)).filter(file => file !== 'en_US.axaml' && file.endsWith('.axaml'));
+
+ // Add en_US badge first
+ badges.push(`[![en_US](https://img.shields.io/badge/en__US-100%25-brightgreen)](TRANSLATION.md)`);
+
+ for (const file of files) {
+ const filePath = path.join(localesDir, file);
+ const localeData = await parseXml(filePath);
+ const localeKeys = new Set(localeData.ResourceDictionary['x:String'].map(item => item.$['x:Key']));
+
+ const missingKeys = [...enUSKeys].filter(key => !localeKeys.has(key));
+ const translationRate = ((enUSKeys.size - missingKeys.length) / enUSKeys.size) * 100;
+
+ translationRates.push(`### ${file}: ${translationRate.toFixed(2)}%\n`);
+ translationRates.push(`\nMissing Keys
\n\n${missingKeys.map(key => `- ${key}`).join('\n')}\n\n `);
+
+ // Add badges
+ const locale = file.replace('.axaml', '').replace('_', '__');
+ const badgeColor = translationRate === 100 ? 'brightgreen' : translationRate >= 75 ? 'yellow' : 'red';
+ badges.push(`[![${locale}](https://img.shields.io/badge/${locale}-${translationRate.toFixed(2)}%25-${badgeColor})](TRANSLATION.md)`);
+ }
+
+ console.log(translationRates.join('\n\n'));
+
+ await fs.writeFile(outputFile, translationRates.join('\n\n') + '\n', 'utf8');
+
+ // Update README.md
+ let readmeContent = await fs.readFile(readmeFile, 'utf8');
+ const badgeSection = `## Translation Status\n\n${badges.join(' ')}`;
+ console.log(badgeSection);
+ readmeContent = readmeContent.replace(/## Translation Status\n\n.*\n\n/, badgeSection + '\n\n');
+ await fs.writeFile(readmeFile, readmeContent, 'utf8');
+}
+
+calculateTranslationRate().catch(err => console.error(err));
diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml
index 32d6f3de..e45eecdb 100644
--- a/src/Resources/Locales/de_DE.axaml
+++ b/src/Resources/Locales/de_DE.axaml
@@ -493,7 +493,6 @@
Fetch
Im Browser öffnen
Prune
- Ziel:
Bestätige das entfernen des Worktrees
Aktiviere `--force` Option
Ziel:
diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml
index 4b571648..184837e8 100644
--- a/src/Resources/Locales/en_US.axaml
+++ b/src/Resources/Locales/en_US.axaml
@@ -402,6 +402,7 @@
Last year
{0} years ago
Preference
+ Advanced Options
OPEN AI
Analyze Diff Prompt
API Key
@@ -496,7 +497,6 @@
Fetch
Open In Browser
Prune
- Target:
Confirm to Remove Worktree
Enable `--force` Option
Target:
diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml
index 14512b2f..e096071f 100644
--- a/src/Resources/Locales/fr_FR.axaml
+++ b/src/Resources/Locales/fr_FR.axaml
@@ -453,7 +453,6 @@
Fetch
Open In Browser
Prune
- Target:
Confirm to Remove Worktree
Enable `--force` Option
Target:
diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml
index d5eac91d..c944cea5 100644
--- a/src/Resources/Locales/pt_BR.axaml
+++ b/src/Resources/Locales/pt_BR.axaml
@@ -467,7 +467,6 @@
Editar...
Buscar
Abrir no Navegador
- Alvo:
Podar
Habilitar Opção `--force`
Alvo:
diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml
index cfb86496..668a185a 100644
--- a/src/Resources/Locales/ru_RU.axaml
+++ b/src/Resources/Locales/ru_RU.axaml
@@ -494,7 +494,6 @@
Извлечь
Открыть в браузере
Удалить
- Цель:
Подтвердить удаление рабочего дерева
Включить опцию `--force`
Цель:
diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml
index d99091c2..f4027111 100644
--- a/src/Resources/Locales/zh_CN.axaml
+++ b/src/Resources/Locales/zh_CN.axaml
@@ -404,6 +404,7 @@
一年前
{0}年前
偏好设置
+ 高级设置
外观配置
缺省字体
默认字体大小
diff --git a/src/Resources/Locales/zh_TW.axaml b/src/Resources/Locales/zh_TW.axaml
index 44f74dfc..e49a049e 100644
--- a/src/Resources/Locales/zh_TW.axaml
+++ b/src/Resources/Locales/zh_TW.axaml
@@ -404,6 +404,7 @@
一年前
{0} 年前
偏好設定
+ 進階選項
OpenAI
伺服器
API 金鑰
@@ -496,7 +497,6 @@
提取 (fetch) 更新
在瀏覽器中存取網址
清理遠端已刪除分支
- 目標:
刪除工作區操作確認
啟用 [--force] 選項
目標工作區:
diff --git a/src/Views/Preference.axaml b/src/Views/Preference.axaml
index c977a0e8..b95dbe66 100644
--- a/src/Views/Preference.axaml
+++ b/src/Views/Preference.axaml
@@ -465,7 +465,7 @@
-
+
@@ -499,29 +499,39 @@
PasswordChar="*"
Text="{Binding OpenAIApiKey, Mode=TwoWay}"/>
-
+
+
+
+
-
+
+ TextWrapping="Wrap"
+ IsVisible="{Binding #OpenAIAdvancedOptions.IsChecked}"/>
-
-
+
+ TextWrapping="Wrap"
+ IsVisible="{Binding #OpenAIAdvancedOptions.IsChecked}"/>