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}"/>