├── .config └── dotnet-tools.json ├── .editorconfig ├── .gitattributes ├── .github ├── dependabot.yml ├── release.yml └── workflows │ ├── dotnet-desktop.yml │ ├── dotnet-package.yml │ └── jekyll-gh-pages.yml ├── .gitignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── ColorThief ├── ColorThief.Bench │ ├── ColorThief.Bench.csproj │ └── Program.cs ├── ColorThief.Test │ ├── ColorThief.Test.csproj │ └── ColorThiefTest.cs ├── ColorThief │ ├── CMap.cs │ ├── Color.cs │ ├── ColorThief.cs │ ├── ColorThief.csproj │ ├── Mmcq.cs │ ├── QuantizedColor.cs │ ├── README.md │ ├── Simd.cs │ └── VBox.cs └── images │ └── test2.jpg ├── Composition.WindowsRuntimeHelpers ├── CaptureHelper.cs ├── Composition.WindowsRuntimeHelpers.csproj ├── CompositionHelper.cs ├── CoreMessagingHelper.cs └── Direct3D11Helper.cs ├── Directory.Build.props ├── Directory.Build.targets ├── Directory.Packages.props ├── GitVersion.yml ├── LICENSE ├── Plugins ├── Directory.Build.props ├── Directory.Build.targets ├── WindowTranslator.Plugin.BergamotTranslatorPlugin │ ├── BergamotTranslator.cs │ ├── SystemUtility.cs │ └── WindowTranslator.Plugin.BergamotTranslatorPlugin.csproj ├── WindowTranslator.Plugin.DeepLTranslatePlugin │ ├── DeepLTranslator.cs │ ├── Properties │ │ ├── Resources.Designer.cs │ │ ├── Resources.de.resx │ │ ├── Resources.en.resx │ │ ├── Resources.ko.resx │ │ ├── Resources.resx │ │ ├── Resources.vi.resx │ │ ├── Resources.zh-CN.resx │ │ └── Resources.zh-TW.resx │ └── WindowTranslator.Plugin.DeepLTranslatePlugin.csproj ├── WindowTranslator.Plugin.DummyPlugin │ ├── DummyFilter.cs │ ├── TranslateEmptyModule.cs │ └── WindowTranslator.Plugin.DummyPlugin.csproj ├── WindowTranslator.Plugin.FoMPlugin │ ├── FoMFilterModule.cs │ ├── Properties │ │ ├── Resources.Designer.cs │ │ ├── Resources.de.resx │ │ ├── Resources.en.resx │ │ ├── Resources.ko.resx │ │ ├── Resources.resx │ │ ├── Resources.vi.resx │ │ ├── Resources.zh-CN.resx │ │ └── Resources.zh-TW.resx │ └── WindowTranslator.Plugin.FoMPlugin.csproj ├── WindowTranslator.Plugin.GoogleAIPlugin │ ├── GoogleAIOcr.cs │ ├── GoogleAIOptions.cs │ ├── GoogleAITranslator.cs │ ├── OcrCorrectFilterBase.cs │ ├── OcrCorrectFromImageFilter.cs │ ├── OcrCorrectFromTextFilter.cs │ ├── Properties │ │ ├── Resources.Designer.cs │ │ ├── Resources.de.resx │ │ ├── Resources.en.resx │ │ ├── Resources.ko.resx │ │ ├── Resources.resx │ │ ├── Resources.vi.resx │ │ ├── Resources.zh-CN.resx │ │ └── Resources.zh-TW.resx │ └── WindowTranslator.Plugin.GoogleAIPlugin.csproj ├── WindowTranslator.Plugin.GoogleAppsSctiptPlugin │ ├── GasTranslator.cs │ ├── Properties │ │ ├── Resources.Designer.cs │ │ ├── Resources.de.resx │ │ ├── Resources.en.resx │ │ ├── Resources.ko.resx │ │ ├── Resources.resx │ │ ├── Resources.vi.resx │ │ ├── Resources.zh-CN.resx │ │ └── Resources.zh-TW.resx │ ├── WindowTranslator.Plugin.GoogleAppsSctiptPlugin.csproj │ ├── service │ │ ├── .claspignore │ │ ├── .gitignore │ │ ├── appsscript.json │ │ ├── package-lock.json │ │ ├── package.json │ │ └── translate.js │ └── test.http ├── WindowTranslator.Plugin.LLMPlugin │ ├── LLMOcr.cs │ ├── LLMOptions.cs │ ├── LLMTranslator.cs │ ├── OcrCorrectFilterBase.cs │ ├── OcrCorrectFromImageFilter.cs │ ├── OcrCorrectionTextFilter.cs │ ├── Properties │ │ ├── Resources.Designer.cs │ │ ├── Resources.de.resx │ │ ├── Resources.en.resx │ │ ├── Resources.ko.resx │ │ ├── Resources.resx │ │ ├── Resources.vi.resx │ │ ├── Resources.zh-CN.resx │ │ └── Resources.zh-TW.resx │ └── WindowTranslator.Plugin.LLMPlugin.csproj ├── WindowTranslator.Plugin.OneOcrPlugin │ ├── NativeMethods.cs │ ├── OneOcr.cs │ ├── OneOcrValidator.cs │ ├── Properties │ │ ├── Resources.Designer.cs │ │ ├── Resources.de.resx │ │ ├── Resources.en.resx │ │ ├── Resources.ko.resx │ │ ├── Resources.resx │ │ ├── Resources.vi.resx │ │ ├── Resources.zh-CN.resx │ │ └── Resources.zh-TW.resx │ ├── README.md │ ├── Utility.cs │ └── WindowTranslator.Plugin.OneOcrPlugin.csproj └── copy_plugins.bat ├── Sandbox ├── Program.cs ├── Properties │ └── launchSettings.json └── Sandbox.csproj ├── Terms_of_Use.rtf ├── WindowTranslator.Abstractions ├── BitmapUtility.cs ├── Collections │ └── RemovableQueue.cs ├── ComponentModel │ ├── DefaultModuleAttribute.cs │ ├── LocalizedDescriptionAttribute.cs │ └── LocalizedDisplayNameAttribute.cs ├── Extensions │ ├── AsyncEnumerable.cs │ ├── BitmapExtensions.cs │ └── SystemExtensions.cs ├── IPluginParam.cs ├── ITargetSettingsValidator.cs ├── LanguageOptions.cs ├── Modules │ ├── ICacheModule.cs │ ├── IFilterModule.cs │ ├── IOcrModule.cs │ └── ITranslateModule.cs ├── OcrUtility.cs ├── PathUtility.cs ├── Properties │ ├── Resources.Designer.cs │ ├── Resources.de.resx │ ├── Resources.en.resx │ ├── Resources.ko.resx │ ├── Resources.resx │ ├── Resources.vi.resx │ ├── Resources.zh-CN.resx │ └── Resources.zh-TW.resx ├── ResourceUtility.cs ├── Stores │ └── IProcessInfoStore.cs ├── TextRect.cs ├── UserSettings.cs └── WindowTranslator.Abstractions.csproj ├── WindowTranslator.Wix ├── Program.cs ├── Properties │ └── launchSettings.json ├── WindowTranslator.Wix.csproj ├── installer_back.png └── installer_bunner.png ├── WindowTranslator.sln ├── WindowTranslator ├── App.xaml ├── App.xaml.cs ├── AssemblyInfo.cs ├── ComponentModel │ ├── DisposeAction.cs │ └── ResetableLazy.cs ├── Controls │ ├── LineBreakSplitParser.cs │ ├── NotifyIcon2.cs │ ├── OverlayTextsControl.cs │ ├── ProcessWindowPresenter.cs │ ├── ShortcutBox.cs │ └── WindowCaptureCompositionHost.cs ├── Data │ ├── BoolToDataTemplateConverter.cs │ ├── BoolToDoubleConverter.cs │ ├── DrawingColorToBrushConverter.cs │ ├── SizeToCornerRadiusConverter.cs │ ├── TextOverlayVisibilityConverter.cs │ └── TextOverlayWidthConverter.cs ├── Extensions │ ├── KeyExtensions.cs │ └── LoggerExtensions.cs ├── IVirtualDesktopManager.cs ├── Modules │ ├── Cache │ │ ├── CacheParam.cs │ │ ├── InMemoryCache.cs │ │ ├── LocalCache.cs │ │ └── NoCache.cs │ ├── Capture │ │ ├── ICaptureModule.cs │ │ └── WindowsGraphicsCapture.cs │ ├── Main │ │ ├── CaptureMainWindow.xaml │ │ ├── CaptureMainWindow.xaml.cs │ │ ├── CloseMessage.cs │ │ ├── MainViewModelBase.cs │ │ ├── MainWindowModule.cs │ │ ├── OverlayMainWindow.xaml │ │ └── OverlayMainWindow.xaml.cs │ ├── Ocr │ │ ├── OcrBufferFilter.cs │ │ ├── WindowsMediaOcr.cs │ │ ├── WindowsMediaOcrUtility.cs │ │ └── WindowsMediaOcrValidator.cs │ ├── OverlayColor │ │ ├── ColorThiefModule.cs │ │ └── IColorModule.cs │ ├── Settings │ │ ├── AllSettingsDialog.xaml │ │ ├── AllSettingsDialog.xaml.cs │ │ ├── AllSettingsViewModel.cs │ │ ├── PluginParamConverter.cs │ │ ├── SettingsPropertyGridFactory.cs │ │ └── SettingsPropertyGridOperator.cs │ ├── Startup │ │ ├── StartupDialog.xaml │ │ ├── StartupDialog.xaml.cs │ │ └── StartupViewModel.cs │ └── Translate │ │ └── NoTranslateModule.cs ├── NativeMethods.txt ├── Program.cs ├── Properties │ ├── Resources.Designer.cs │ ├── Resources.de.resx │ ├── Resources.en.resx │ ├── Resources.ko.resx │ ├── Resources.resx │ ├── Resources.vi.resx │ ├── Resources.zh-CN.resx │ ├── Resources.zh-TW.resx │ └── launchSettings.json ├── Stores │ ├── AutoTargetStore.cs │ └── ProcessInfoStore.cs ├── Themes │ ├── DefaultStyles.xaml │ └── Generic.xaml ├── UpdateChecker.cs ├── WindowMonitor.cs ├── WindowTranslator.csproj └── appsettings.json ├── crowdin.yml ├── docs ├── Gemfile ├── Gemfile.lock ├── PrivacyPolicy.de.md ├── PrivacyPolicy.en.md ├── PrivacyPolicy.kr.md ├── PrivacyPolicy.md ├── PrivacyPolicy.vi.md ├── PrivacyPolicy.zh-cn.md ├── PrivacyPolicy.zh-tw.md ├── README.de.md ├── README.en.md ├── README.kr.md ├── README.md ├── README.vi.md ├── README.zh-cn.md ├── README.zh-tw.md ├── _config.yml ├── images │ ├── auth.png │ ├── deepl.png │ ├── language.png │ ├── login.png │ ├── result.png │ ├── select.png │ ├── settings.png │ ├── startup.png │ ├── translate.png │ ├── translate_module.png │ ├── wt.ico │ └── wt.png ├── template.html └── theme.css ├── hwnd-adorner ├── .gitattributes ├── .gitignore ├── Demo │ ├── App.xaml │ ├── App.xaml.cs │ ├── BrowserAdornment.xaml │ ├── BrowserAdornment.xaml.cs │ ├── BrowserPresenter.cs │ ├── Demo.csproj │ ├── MainWindow.xaml │ └── MainWindow.xaml.cs ├── HwndExtensions.sln ├── HwndExtensions │ ├── Adorner │ │ ├── HwndAdorner.cs │ │ ├── HwndAdornerElement.cs │ │ ├── HwndAdornerGroup.cs │ │ ├── HwndAdornerManager.cs │ │ ├── HwndAdornmentRoot.cs │ │ └── IHwndAdornerManager.cs │ ├── Host │ │ ├── ConnectToHostManagerBehavior.cs │ │ ├── ExtendedHwndHost.cs │ │ ├── HwndHostGroup.cs │ │ ├── HwndHostManager.cs │ │ ├── HwndHostPresenter.cs │ │ ├── IHwndHolder.cs │ │ └── IHwndHostManager.cs │ ├── HwndExtensions.cs │ ├── HwndExtensions.csproj │ └── Utils │ │ ├── DispatchUI.cs │ │ ├── HwndSourceConnector.cs │ │ ├── RectUtil.cs │ │ ├── VisualTreeExtensions.cs │ │ └── WindowConnector.cs ├── License.txt └── README.md ├── ignore-packages.json ├── include-projects.json ├── licenses ├── Microsoft-WindowsAPICodePack-Core__1.1.4.txt ├── Microsoft-WindowsAPICodePack-Shell__1.1.4.txt └── Microsoft.Management.Infrastructure.Runtime.Win__3.0.0.txt ├── memo.md ├── package-information.json └── video └── 83A8T890N5M ├── captions.de.sbv ├── captions.en.sbv ├── captions.kr.sbv ├── captions.zh-cn.sbv └── captions.zh-tw.sbv /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "wix": { 6 | "version": "5.0.2", 7 | "commands": [ 8 | "wix" 9 | ], 10 | "rollForward": false 11 | }, 12 | "nuget-license": { 13 | "version": "3.1.4", 14 | "commands": [ 15 | "nuget-license" 16 | ], 17 | "rollForward": false 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "nuget" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | open-pull-requests-limit: 1 8 | reviewers: 9 | - "Freeesia" 10 | - package-ecosystem: "github-actions" 11 | directory: "/" 12 | schedule: 13 | interval: "weekly" 14 | open-pull-requests-limit: 1 15 | reviewers: 16 | - "Freeesia" 17 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | categories: 3 | - title: 🏕 Features 4 | labels: 5 | - '*' 6 | exclude: 7 | labels: 8 | - dependencies 9 | - title: 👒 Dependencies 10 | labels: 11 | - dependencies 12 | -------------------------------------------------------------------------------- /.github/workflows/dotnet-package.yml: -------------------------------------------------------------------------------- 1 | name: .NET Core Package 2 | 3 | on: 4 | push: 5 | tags: [v*] 6 | 7 | jobs: 8 | build: 9 | runs-on: windows-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | - uses: actions/cache@v4 15 | with: 16 | path: ~/.nuget/packages 17 | key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} #hash of project files 18 | restore-keys: | 19 | ${{ runner.os }}-nuget- 20 | - uses: actions/setup-dotnet@v4 21 | with: 22 | dotnet-version: 8.0.x 23 | - uses: gittools/actions/gitversion/setup@v3.2.1 24 | with: 25 | versionSpec: "5.x" 26 | - id: gitversion 27 | uses: gittools/actions/gitversion/execute@v3.2.1 28 | - run: | 29 | dotnet pack WindowTranslator.Abstractions -c Release -o pack ` 30 | -p:Version=${{ steps.gitversion.outputs.fullSemVer }} ` 31 | -p:AssemblyVersion=${{ steps.gitversion.outputs.assemblySemVer }} ` 32 | -p:FileVersion=${{ steps.gitversion.outputs.assemblySemFileVer }} ` 33 | -p:InformationalVersion=${{ steps.gitversion.outputs.informationalVersion }} 34 | dotnet nuget push pack\*.nupkg -k ${{ secrets.NUGET_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate 35 | -------------------------------------------------------------------------------- /.github/workflows/jekyll-gh-pages.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Jekyll site to GitHub Pages 2 | name: Deploy Jekyll with GitHub Pages dependencies preinstalled 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["master"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Build job 26 | build: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v4 31 | - name: Setup Pages 32 | uses: actions/configure-pages@v5 33 | - name: Build with Jekyll 34 | uses: actions/jekyll-build-pages@v1 35 | with: 36 | source: ./docs/ 37 | destination: ./_site 38 | - name: Upload artifact 39 | uses: actions/upload-pages-artifact@v3 40 | 41 | # Deployment job 42 | deploy: 43 | environment: 44 | name: github-pages 45 | url: ${{ steps.deployment.outputs.page_url }} 46 | runs-on: ubuntu-latest 47 | needs: build 48 | steps: 49 | - name: Deploy to GitHub Pages 50 | id: deployment 51 | uses: actions/deploy-pages@v4 52 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // IntelliSense を使用して利用可能な属性を学べます。 3 | // 既存の属性の説明をホバーして表示します。 4 | // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Launch", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | "program": "${workspaceFolder}/WindowTranslator/bin/Debug/WindowTranslator.exe", 13 | "args": [], 14 | "cwd": "${workspaceFolder}", 15 | "stopAtEntry": false, 16 | "console": "internalConsole", 17 | "envFile": "${workspaceFolder}/.env" 18 | }, 19 | { 20 | "name": "C#: WindowTranslator Debug", 21 | "type": "dotnet", 22 | "request": "launch", 23 | "projectPath": "${workspaceFolder}/WindowTranslator/WindowTranslator.csproj", 24 | "preLaunchTask": "build", 25 | }, 26 | { 27 | "name": ".NET Core Attach", 28 | "type": "coreclr", 29 | "request": "attach" 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dotnet.defaultSolution": "WindowTranslator.sln" 3 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/WindowTranslator.sln", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/WindowTranslator/WindowTranslator.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "--project", 36 | "${workspaceFolder}/WindowTranslator/WindowTranslator.csproj" 37 | ], 38 | "problemMatcher": "$msCompile" 39 | }, 40 | { 41 | "label": "docs", 42 | "command": "bundle", 43 | "isBackground": true, 44 | "type": "process", 45 | "args": [ 46 | "exec", 47 | "jekyll", 48 | "serve", 49 | "--incremental", 50 | ], 51 | "options": { 52 | "cwd": "${workspaceFolder}/docs" 53 | }, 54 | "problemMatcher": [] 55 | } 56 | ] 57 | } -------------------------------------------------------------------------------- /ColorThief/ColorThief.Bench/ColorThief.Bench.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0-windows10.0.20348.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | PreserveNewest 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ColorThief/ColorThief.Bench/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using BenchmarkDotNet.Attributes; 3 | using BenchmarkDotNet.Configs; 4 | using BenchmarkDotNet.Jobs; 5 | using BenchmarkDotNet.Running; 6 | using StudioFreesia.ColorThief; 7 | using Windows.Graphics.Imaging; 8 | 9 | var config = DefaultConfig.Instance 10 | .AddJob(Job.InProcess); 11 | 12 | _ = BenchmarkRunner.Run(config); 13 | 14 | [MemoryDiagnoser] 15 | public class ColorThiefTest 16 | { 17 | private readonly ColorThiefDotNet.ColorThief colorThief = new(); 18 | private readonly Bitmap bmp; 19 | private readonly SoftwareBitmap sbmp; 20 | 21 | public ColorThiefTest() 22 | { 23 | using var fileStream = new FileStream(@"images\test2.jpg", FileMode.Open, FileAccess.Read); 24 | var randomAccessStream = fileStream.AsRandomAccessStream(); 25 | 26 | var decoder = BitmapDecoder.CreateAsync(randomAccessStream).AsTask().Result; 27 | this.sbmp = decoder.GetSoftwareBitmapAsync().AsTask().Result; 28 | this.bmp = new Bitmap(randomAccessStream.AsStream()); 29 | } 30 | 31 | [Benchmark(Baseline = true)] 32 | public List Original() => this.colorThief.GetPalette(this.bmp); 33 | 34 | [Benchmark] 35 | public List ColorThiefEx() => ColorThief.GetPalette(this.sbmp).ToList(); 36 | } -------------------------------------------------------------------------------- /ColorThief/ColorThief.Test/ColorThief.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0-windows10.0.20348.0 5 | enable 6 | enable 7 | StudioFreesia.ColorThief.Test 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | PreserveNewest 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ColorThief/ColorThief.Test/ColorThiefTest.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using Windows.Graphics.Imaging; 3 | 4 | namespace StudioFreesia.ColorThief.Test; 5 | 6 | public class ColorThiefTest 7 | { 8 | private readonly ColorThiefDotNet.ColorThief colorThief = new(); 9 | 10 | [Fact] 11 | public async Task Test1() 12 | { 13 | using var fileStream = new FileStream(@"images\test2.jpg", FileMode.Open, FileAccess.Read); 14 | var randomAccessStream = fileStream.AsRandomAccessStream(); 15 | 16 | var decoder = await BitmapDecoder.CreateAsync(randomAccessStream); 17 | using var sbmp = await decoder.GetSoftwareBitmapAsync(); 18 | using var bmp = new Bitmap(randomAccessStream.AsStream()); 19 | var expect = this.colorThief.GetPalette(bmp); 20 | //var actual = ColorThief.GetPalette(sbmp, quality: 10000).ToList(); 21 | var actual = ColorThief.GetPalette(sbmp).ToList(); 22 | Assert.Equal(expect.Count, actual.Count); 23 | for (var i = 0; i < expect.Count; i++) 24 | { 25 | Assert.Equal(expect[i].Color.R, actual[i].Color.R); 26 | Assert.Equal(expect[i].Color.G, actual[i].Color.G); 27 | Assert.Equal(expect[i].Color.B, actual[i].Color.B); 28 | Assert.Equal(expect[i].Population, actual[i].Population); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /ColorThief/ColorThief/CMap.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace StudioFreesia.ColorThief; 4 | 5 | /// 6 | /// Color map 7 | /// 8 | internal class CMap 9 | { 10 | private readonly List vboxes = []; 11 | private List? palette; 12 | 13 | public void Push(VBox box) 14 | { 15 | palette = null; 16 | vboxes.Add(box); 17 | } 18 | 19 | public IEnumerable GeneratePalette() 20 | => palette ??= vboxes.Select(vBox => 21 | { 22 | var rgb = vBox.Avg(false); 23 | return new QuantizedColor(Color.FromArgb(rgb[0], rgb[1], rgb[2]), vBox.Count(false)); 24 | }).ToList(); 25 | } 26 | -------------------------------------------------------------------------------- /ColorThief/ColorThief/Color.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace StudioFreesia.ColorThief; 4 | 5 | /// 6 | /// Defines a color in RGB space. 7 | /// 8 | public static class ColorExtensions 9 | { 10 | /// 11 | /// Get HSL color. 12 | /// 13 | /// 14 | public static HslColor ToHsl(this Color color) 15 | { 16 | const double toDouble = 1.0 / 255; 17 | var r = toDouble * color.R; 18 | var g = toDouble * color.G; 19 | var b = toDouble * color.B; 20 | var max = Math.Max(Math.Max(r, g), b); 21 | var min = Math.Min(Math.Min(r, g), b); 22 | var chroma = max - min; 23 | double h1; 24 | 25 | if (chroma == 0) 26 | { 27 | h1 = 0; 28 | } 29 | else if (max == r) 30 | { 31 | h1 = (g - b) / chroma % 6; 32 | } 33 | else if (max == g) 34 | { 35 | h1 = 2 + (b - r) / chroma; 36 | } 37 | else //if (max == b) 38 | { 39 | h1 = 4 + (r - g) / chroma; 40 | } 41 | 42 | var lightness = 0.5 * (max - min); 43 | var saturation = chroma == 0 ? 0 : chroma / (1 - Math.Abs(2 * lightness - 1)); 44 | return new(toDouble * color.A, 60 * h1, saturation, lightness); 45 | } 46 | 47 | public static string ToHexString(this Color color) 48 | => $"#{color.R:X2}{color.G:X2}{color.B:X2}"; 49 | 50 | public static string ToHexAlphaString(this Color color) 51 | => $"#{color.A:X2}{color.R:X2}{color.G:X2}{color.B:X2}"; 52 | } 53 | 54 | /// 55 | /// Defines a color in Hue/Saturation/Lightness (HSL) space. 56 | /// 57 | /// The Alpha/opacity in 0..1 range. 58 | /// The Hue in 0..360 range. 59 | /// The Lightness in 0..1 range. 60 | /// The Saturation in 0..1 range. 61 | public readonly record struct HslColor(double A, double H, double S, double L); -------------------------------------------------------------------------------- /ColorThief/ColorThief/ColorThief.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0-windows10.0.20348.0 5 | enable 6 | enable 7 | StudioFreesia.ColorThief 8 | True 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /ColorThief/ColorThief/QuantizedColor.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace StudioFreesia.ColorThief; 4 | 5 | public record QuantizedColor(Color Color, int Population) 6 | { 7 | public bool IsDark { get; } = CalculateYiqLuma(Color) < 128; 8 | 9 | private static int CalculateYiqLuma(Color color) => Convert.ToInt32(Math.Round((299 * color.R + 587 * color.G + 114 * color.B) / 1000f)); 10 | } -------------------------------------------------------------------------------- /ColorThief/ColorThief/README.md: -------------------------------------------------------------------------------- 1 | # Color Thief 2 | 3 | 4 | ## 元のライブラリ 5 | 6 | [Color Thief .NET](https://github.com/KSemenenko/ColorThief) 7 | 8 | #### ライセンス 9 | 10 | The MIT License (MIT) 11 | 12 | Copyright (c) 2015 Kos 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all 22 | copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | SOFTWARE. 31 | 32 | ## オリジナル 33 | 34 | [Color Thief](https://github.com/lokesh/color-thief) 35 | 36 | #### ライセンス 37 | 38 | The MIT License (MIT) 39 | 40 | Copyright (c) 2015 Lokesh Dhakar 41 | 42 | Permission is hereby granted, free of charge, to any person obtaining a copy 43 | of this software and associated documentation files (the "Software"), to deal 44 | in the Software without restriction, including without limitation the rights 45 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 46 | copies of the Software, and to permit persons to whom the Software is 47 | furnished to do so, subject to the following conditions: 48 | 49 | The above copyright notice and this permission notice shall be included in all 50 | copies or substantial portions of the Software. 51 | 52 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 53 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 54 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 55 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 56 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 57 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 58 | SOFTWARE. -------------------------------------------------------------------------------- /ColorThief/images/test2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/ColorThief/images/test2.jpg -------------------------------------------------------------------------------- /Composition.WindowsRuntimeHelpers/Composition.WindowsRuntimeHelpers.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0-windows10.0.19041.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Composition.WindowsRuntimeHelpers/CompositionHelper.cs: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------------------------------- 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // 4 | // The MIT License (MIT) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // --------------------------------------------------------------------------------- 24 | 25 | using System; 26 | using System.Runtime.InteropServices; 27 | using Windows.UI.Composition; 28 | using Windows.UI.Composition.Desktop; 29 | using WinRT; 30 | 31 | namespace Composition.WindowsRuntimeHelpers 32 | { 33 | public static class CompositionHelper 34 | { 35 | [ComImport] 36 | [Guid("25297D5C-3AD4-4C9C-B5CF-E36A38512330")] 37 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 38 | [ComVisible(true)] 39 | interface ICompositorInterop 40 | { 41 | void CreateCompositionSurfaceForHandle(IntPtr swapChain, out IntPtr surface); 42 | 43 | void CreateCompositionSurfaceForSwapChain(IntPtr swapChain, out IntPtr surface); 44 | 45 | void CreateGraphicsDevice(IntPtr renderingDevice, out IntPtr device); 46 | } 47 | 48 | [ComImport] 49 | [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")] 50 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 51 | [ComVisible(true)] 52 | interface ICompositorDesktopInterop 53 | { 54 | IntPtr CreateDesktopWindowTarget(IntPtr hwnd, bool isTopmost); 55 | } 56 | 57 | public static CompositionTarget CreateDesktopWindowTarget(this Compositor compositor, IntPtr hwnd, bool isTopmost) 58 | { 59 | var desktopInterop = compositor.As(); 60 | var targetPtr = desktopInterop.CreateDesktopWindowTarget(hwnd, isTopmost); 61 | return MarshalInterface.FromAbi(targetPtr); 62 | } 63 | 64 | public static ICompositionSurface CreateCompositionSurfaceForSwapChain(this Compositor compositor, SharpDX.DXGI.SwapChain1 swapChain) 65 | { 66 | var interop = compositor.As(); 67 | interop.CreateCompositionSurfaceForSwapChain(swapChain.NativePointer, out var surface); 68 | return MarshalInterface.FromAbi(surface); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | latest 4 | enable 5 | true 6 | true 7 | ja-JP 8 | 0.0.0-local 9 | false 10 | 11 | 12 | Freesia 13 | WindowTranslator is a tool to translate the text on the screen. 14 | MIT 15 | https://github.com/Freeesia/WindowTranslator 16 | https://github.com/Freeesia/WindowTranslator 17 | git 18 | WindowTranslator;OCR;Translation 19 | 20 | 21 | embedded 22 | 23 | 24 | 25 | 26 | <_Parameter1>BuildDateTime 27 | <_Parameter2>$([System.DateTime]::UtcNow.ToString("o")) 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/Directory.Build.targets -------------------------------------------------------------------------------- /Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | assembly-versioning-scheme: MajorMinorPatchTag 2 | mode: ContinuousDeployment 3 | tag-prefix: '[vV]' 4 | ignore: 5 | sha: [] 6 | merge-message-formats: {} 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Freeesia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Plugins/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | net8.0 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | false 23 | runtime 24 | 25 | 26 | -------------------------------------------------------------------------------- /Plugins/Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.BergamotTranslatorPlugin/SystemUtility.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowTranslator.Plugin.BergamotTranslatorPlugin; 5 | 6 | public static partial class SystemUtility 7 | { 8 | private const ushort IMAGE_FILE_MACHINE_AMD64 = 0x8664; 9 | 10 | [LibraryImport("kernel32.dll", SetLastError = true)] 11 | [return: MarshalAs(UnmanagedType.Bool)] 12 | private static partial bool IsWow64Process2(nint hProcess, out ushort processMachine, out ushort nativeMachine); 13 | 14 | public static bool IsX64Machine() 15 | { 16 | if (IsWow64Process2(Process.GetCurrentProcess().Handle, out _, out var nativeMachine)) 17 | { 18 | return nativeMachine == IMAGE_FILE_MACHINE_AMD64; 19 | } 20 | return false; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.BergamotTranslatorPlugin/WindowTranslator.Plugin.BergamotTranslatorPlugin.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.DeepLTranslatePlugin/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // このコードはツールによって生成されました。 4 | // ランタイム バージョン:4.0.30319.42000 5 | // 6 | // このファイルへの変更は、以下の状況下で不正な動作の原因になったり、 7 | // コードが再生成されるときに損失したりします。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WindowTranslator.Plugin.DeepLTranslatePlugin.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。 17 | /// 18 | // このクラスは StronglyTypedResourceBuilder クラスが ResGen 19 | // または Visual Studio のようなツールを使用して自動生成されました。 20 | // メンバーを追加または削除するには、.ResX ファイルを編集して、/str オプションと共に 21 | // ResGen を実行し直すか、または VS プロジェクトをビルドし直します。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// このクラスで使用されているキャッシュされた ResourceManager インスタンスを返します。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WindowTranslator.Plugin.DeepLTranslatePlugin.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// すべてについて、現在のスレッドの CurrentUICulture プロパティをオーバーライドします 51 | /// 現在のスレッドの CurrentUICulture プロパティをオーバーライドします。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// APIキー に類似しているローカライズされた文字列を検索します。 65 | /// 66 | internal static string AuthKey { 67 | get { 68 | return ResourceManager.GetString("AuthKey", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// 用語集パス に類似しているローカライズされた文字列を検索します。 74 | /// 75 | internal static string GlossaryPath { 76 | get { 77 | return ResourceManager.GetString("GlossaryPath", resourceCulture); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.DeepLTranslatePlugin/WindowTranslator.Plugin.DeepLTranslatePlugin.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | True 12 | True 13 | Resources.resx 14 | 15 | 16 | 17 | 18 | 19 | ResXFileCodeGenerator 20 | Resources.Designer.cs 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.DummyPlugin/DummyFilter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using WindowTranslator.Modules; 3 | 4 | namespace WindowTranslator.Plugin.DummyPlugin; 5 | public class DummyFilter(ILogger logger) : IFilterModule 6 | { 7 | private readonly ILogger logger = logger; 8 | 9 | public IAsyncEnumerable ExecutePostTranslate(IAsyncEnumerable texts, FilterContext context) 10 | { 11 | this.logger.LogDebug("ExecutePostTranslate"); 12 | return texts; 13 | } 14 | 15 | public IAsyncEnumerable ExecutePreTranslate(IAsyncEnumerable texts, FilterContext context) 16 | { 17 | this.logger.LogDebug("ExecutePreTranslate"); 18 | return texts; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.DummyPlugin/TranslateEmptyModule.cs: -------------------------------------------------------------------------------- 1 | using WindowTranslator.Modules; 2 | using DisplayNameAttribute = System.ComponentModel.DisplayNameAttribute; 3 | 4 | namespace WindowTranslator.Plugin.DummyPlugin; 5 | 6 | [DisplayName("空文字化")] 7 | public class TranslateEmptyModule : ITranslateModule 8 | { 9 | public ValueTask TranslateAsync(TextInfo[] srcTexts) 10 | => ValueTask.FromResult((string[])Array.CreateInstance(typeof(string), srcTexts.Length)); 11 | } 12 | 13 | 14 | [DisplayName("完了しない")] 15 | public class TranslateInfinityModule : ITranslateModule 16 | { 17 | public ValueTask TranslateAsync(TextInfo[] srcTexts) 18 | => new(Task.Delay(-1).ContinueWith(_ => (string[])Array.CreateInstance(typeof(string), srcTexts.Length))); 19 | } 20 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.DummyPlugin/WindowTranslator.Plugin.DummyPlugin.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.FoMPlugin/WindowTranslator.Plugin.FoMPlugin.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | True 11 | True 12 | Resources.resx 13 | 14 | 15 | 16 | 17 | 18 | ResXFileCodeGenerator 19 | Resources.Designer.cs 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.GoogleAIPlugin/GoogleAIOptions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using GenerativeAI; 3 | using PropertyTools.DataAnnotations; 4 | using WindowTranslator.ComponentModel; 5 | using WindowTranslator.Modules; 6 | using WindowTranslator.Plugin.GoogleAIPlugin.Properties; 7 | 8 | namespace WindowTranslator.Plugin.GoogleAIPlugin; 9 | 10 | public partial class GoogleAIOptions : IPluginParam 11 | { 12 | [SelectorStyle(SelectorStyle.ComboBox)] 13 | public CorrectMode CorrectMode { get; set; } 14 | 15 | [SelectorStyle(SelectorStyle.ComboBox)] 16 | public GoogleAIModel Model { get; set; } = GoogleAIModel.Gemini15Flash; 17 | 18 | [LocalizedDescription(typeof(Resources), $"{nameof(PreviewModel)}_Desc")] 19 | public string? PreviewModel { get; set; } 20 | 21 | [DataType(DataType.Password)] 22 | public string? ApiKey { get; set; } 23 | 24 | [Height(120)] 25 | [DataType(DataType.MultilineText)] 26 | public string? CorrectSample { get; set; } 27 | 28 | [Height(120)] 29 | [DataType(DataType.MultilineText)] 30 | public string? TranslateContext { get; set; } 31 | 32 | [FileExtensions(Extensions = ".csv")] 33 | [InputFilePath(".csv", "CSV (.csv)|*.csv")] 34 | public string? GlossaryPath { get; set; } 35 | } 36 | 37 | public enum GoogleAIModel 38 | { 39 | Gemini15Flash, 40 | Gemini15Pro, 41 | Gemini20FlashLite, 42 | Gemini20Flash, 43 | } 44 | 45 | public enum CorrectMode 46 | { 47 | [LocalizedDescription(typeof(Resources), $"{nameof(CorrectMode)}_{nameof(None)}")] 48 | None, 49 | [LocalizedDescription(typeof(Resources), $"{nameof(CorrectMode)}_{nameof(Text)}")] 50 | Text, 51 | [LocalizedDescription(typeof(Resources), $"{nameof(CorrectMode)}_{nameof(Image)}")] 52 | Image, 53 | } 54 | 55 | public static class GoogleAIModelExtensions 56 | { 57 | public static string GetName(this GoogleAIModel model) => model switch 58 | { 59 | GoogleAIModel.Gemini15Flash => GoogleAIModels.Gemini15Flash, 60 | GoogleAIModel.Gemini15Pro => GoogleAIModels.Gemini15Pro, 61 | GoogleAIModel.Gemini20FlashLite => "models/gemini-2.0-flash-lite", 62 | GoogleAIModel.Gemini20Flash => "models/gemini-2.0-flash", 63 | _ => throw new ArgumentOutOfRangeException(nameof(model)), 64 | }; 65 | } 66 | 67 | public class GoogleAIValidator : ITargetSettingsValidator 68 | { 69 | public ValueTask Validate(TargetSettings settings) 70 | { 71 | var op = settings.PluginParams.GetValueOrDefault(nameof(GoogleAIOptions)) as GoogleAIOptions; 72 | // APIキーが設定されている場合は有効 73 | if (!string.IsNullOrEmpty(op?.ApiKey)) 74 | { 75 | return ValueTask.FromResult(ValidateResult.Valid); 76 | } 77 | 78 | // 翻訳モジュールでも補正も利用しない場合は無条件で有効 79 | if (settings.SelectedPlugins[nameof(ITranslateModule)] != nameof(GoogleAITranslator) && (op?.CorrectMode ?? CorrectMode.None) == CorrectMode.None) 80 | { 81 | return ValueTask.FromResult(ValidateResult.Valid); 82 | } 83 | 84 | return ValueTask.FromResult(ValidateResult.Invalid("GoogleAI", """ 85 | 翻訳モジュールにGoogleAIが選択もしくは認識補正が有効化されています。 86 | 87 | GoogleAIの利用にはAPIキーが必要です。 88 | 「対象ごとの設定」→「GoogleAIOptions」タブのAPIキーを設定してください。 89 | 90 | APIキーはGoogleAIの[APIキーページ](https://aistudio.google.com/app/apikey)から取得できます。 91 | """)); 92 | } 93 | } -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.GoogleAIPlugin/OcrCorrectFromTextFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Text.Json; 3 | using GenerativeAI; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using WindowTranslator.Modules; 7 | 8 | namespace WindowTranslator.Plugin.GoogleAIPlugin; 9 | 10 | public sealed class OcrCorrectFromTextFilter( 11 | IOptionsSnapshot langOptions, 12 | IOptionsSnapshot googleAiOptions, 13 | ILogger logger) 14 | : OcrCorrectFilterBase(langOptions, googleAiOptions, logger) 15 | { 16 | protected override CorrectMode TargetMode => CorrectMode.Text; 17 | 18 | protected override string GetSystem(LanguageOptions languageOptions, GoogleAIOptions googleAIOptions) 19 | => $""" 20 | あなたは{CultureInfo.GetCultureInfo(languageOptions.Source).DisplayName}の専門家です。 21 | これから渡される文字列はOCRによって認識されたものであり、誤字や脱字が含まれています。 22 | 次の指示に従って、渡された文字列を修正してください。 23 | 24 | 1. 誤字や脱字を、文字形の類似性を考慮して修正してください。 25 | 2. 単語や文章の意味が崩れないように、文字数の変更は最小限にしてください。 26 | 3. 正しい単語や文章に修正した結果のみを出力してください。 27 | 4. 誤字や脱字がない場合は、そのままの文字列を出力してください。 28 | 5. 単語や文章として認識できない場合は、空文字を出力してください。 29 | 30 | <誤字修正の例> 31 | {googleAIOptions.CorrectSample} 32 | 33 | 34 | 修正する文字列は以下のJsonフォーマットになっています。出力文字列も同じJsonフォーマットで、入力文字列の順序を維持してください。 35 | <入力テキストのJsonフォーマット> 36 | ["誤字修正するテキスト1","誤字修正するテキスト2"] 37 | 38 | """; 39 | 40 | protected override ValueTask> GetQueueData(IEnumerable targets, FilterContext context) 41 | => new([.. targets.Select(t => t.Text)]); 42 | 43 | protected override async Task CorrectCore(IReadOnlyList texts, CancellationToken cancellationToken) 44 | { 45 | foreach (var text in texts) 46 | { 47 | this.Cache.TryAdd(text, null); 48 | } 49 | try 50 | { 51 | var json = JsonSerializer.Serialize(texts, DefaultSerializerOptions.GenerateObjectJsonOptions); 52 | var corrected = await this.Client.GenerateObjectAsync(json, cancellationToken) 53 | .ConfigureAwait(false) ?? []; 54 | cancellationToken.ThrowIfCancellationRequested(); 55 | for (var i = 0; i < texts.Count; i++) 56 | { 57 | this.Cache[texts[i]] = corrected[i]; 58 | } 59 | } 60 | catch (Exception e) 61 | { 62 | this.Logger.LogError(e, $"Failed to correct `{texts}`"); 63 | } 64 | } 65 | 66 | protected override void Dropped(IReadOnlyList texts) 67 | => this.Logger.LogDebug($"Dropped texts: {string.Join(", ", texts)}"); 68 | } 69 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.GoogleAIPlugin/WindowTranslator.Plugin.GoogleAIPlugin.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0-windows10.0.20348.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | True 13 | True 14 | Resources.resx 15 | 16 | 17 | 18 | 19 | ResXFileCodeGenerator 20 | Resources.Designer.cs 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.GoogleAppsSctiptPlugin/WindowTranslator.Plugin.GoogleAppsSctiptPlugin.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <_Parameter1>DecryptKey 10 | <_Parameter2>$(DecryptKey) 11 | 12 | 13 | 14 | 15 | 16 | True 17 | True 18 | Resources.resx 19 | 20 | 21 | 22 | 23 | 24 | ResXFileCodeGenerator 25 | Resources.Designer.cs 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.GoogleAppsSctiptPlugin/service/.claspignore: -------------------------------------------------------------------------------- 1 | **/** 2 | !translate.js 3 | !appsscript.json -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.GoogleAppsSctiptPlugin/service/.gitignore: -------------------------------------------------------------------------------- 1 | .clasp.json 2 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.GoogleAppsSctiptPlugin/service/appsscript.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeZone": "Asia/Tokyo", 3 | "dependencies": {}, 4 | "exceptionLogging": "STACKDRIVER", 5 | "runtimeVersion": "V8", 6 | "webapp": { 7 | "executeAs": "USER_ACCESSING", 8 | "access": "ANYONE" 9 | } 10 | } -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.GoogleAppsSctiptPlugin/service/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "service", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "service", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@types/google-apps-script": "^1.0.92" 13 | } 14 | }, 15 | "node_modules/@types/google-apps-script": { 16 | "version": "1.0.92", 17 | "resolved": "https://registry.npmjs.org/@types/google-apps-script/-/google-apps-script-1.0.92.tgz", 18 | "integrity": "sha512-QWkcGH0Ll0NjPXfnMuMT1rH3ics/6s9DKZiAB8pQIZNfSrRaJIqoChOuR7Sr+n5oe5XF1NPJ4RVPE3Bvfh9heQ==", 19 | "license": "MIT" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.GoogleAppsSctiptPlugin/service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tw_gas_translate", 3 | "version": "1.0.0", 4 | "main": "translate.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "Freeesia", 10 | "license": "MIT", 11 | "description": "", 12 | "dependencies": { 13 | "@types/google-apps-script": "^1.0.92" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.GoogleAppsSctiptPlugin/service/translate.js: -------------------------------------------------------------------------------- 1 | function doPost(e) { 2 | // リクエストボディをパースして文字列配列を取得 3 | var requestBody = JSON.parse(e.postData.contents); 4 | var texts = requestBody.texts; 5 | var sourceLanguage = requestBody.sourceLanguage 6 | var targetLanguage = requestBody.targetLanguage; 7 | 8 | // 翻訳結果を格納する配列 9 | var translatedTexts = []; 10 | 11 | // 各文字列を翻訳 12 | for (var i = 0; i < texts.length; i++) { 13 | var translatedText = LanguageApp.translate(texts[i], sourceLanguage, targetLanguage); 14 | translatedTexts.push(translatedText); 15 | } 16 | 17 | // 翻訳結果をjsonとして返す 18 | var jsonResponse = JSON.stringify(translatedTexts); 19 | return ContentService.createTextOutput(jsonResponse) 20 | .setMimeType(ContentService.MimeType.JSON); 21 | } 22 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.GoogleAppsSctiptPlugin/test.http: -------------------------------------------------------------------------------- 1 | @deployId = AKfycbxe_E9XjeWckgkkbe9mDoc5GyIQX1CaxFD5bBT6J7Y6JmMrG0U7JaQv-D2Nc0NaXI_APQ 2 | 3 | ### 4 | GET https://script.google.com/macros/s/{{deployId}}/exec HTTP/1.1 5 | Authorization: Bearer {{wt_oauth_token}} 6 | 7 | ### 8 | POST https://script.google.com/macros/s/{{deployId}}/exec HTTP/1.1 9 | Authorization: Bearer {{wt_oauth_token}} 10 | Content-Type: application/json 11 | 12 | { 13 | "sourceLanguage": "en", 14 | "targetLanguage": "ja", 15 | "texts": ["Hello, World!"] 16 | } 17 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.LLMPlugin/LLMOptions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using PropertyTools.DataAnnotations; 3 | using WindowTranslator.ComponentModel; 4 | using WindowTranslator.Modules; 5 | using WindowTranslator.Plugin.LLMPlugin.Properties; 6 | 7 | namespace WindowTranslator.Plugin.LLMPlugin; 8 | 9 | public class LLMOptions : IPluginParam 10 | { 11 | [SelectorStyle(SelectorStyle.ComboBox)] 12 | public CorrectMode CorrectMode { get; set; } 13 | 14 | public string? Model { get; set; } = "gpt-4o-mini"; 15 | 16 | [DataType(DataType.Password)] 17 | public string? ApiKey { get; set; } 18 | 19 | [LocalizedDescription(typeof(Resources), $"{nameof(Endpoint)}_Desc")] 20 | public string? Endpoint { get; set; } 21 | 22 | [Height(120)] 23 | [DataType(DataType.MultilineText)] 24 | public string? CorrectSample { get; set; } 25 | 26 | [Height(120)] 27 | [DataType(DataType.MultilineText)] 28 | public string? TranslateContext { get; set; } 29 | 30 | [FileExtensions(Extensions = ".csv")] 31 | [InputFilePath(".csv", "CSV (.csv)|*.csv")] 32 | public string? GlossaryPath { get; set; } 33 | } 34 | 35 | public enum CorrectMode 36 | { 37 | [LocalizedDescription(typeof(Resources), $"{nameof(CorrectMode)}_{nameof(None)}")] 38 | None, 39 | [LocalizedDescription(typeof(Resources), $"{nameof(CorrectMode)}_{nameof(Text)}")] 40 | Text, 41 | [LocalizedDescription(typeof(Resources), $"{nameof(CorrectMode)}_{nameof(Image)}")] 42 | Image, 43 | } 44 | 45 | public class LLMOptionsValidator : ITargetSettingsValidator 46 | { 47 | public ValueTask Validate(TargetSettings settings) 48 | { 49 | var op = settings.PluginParams.GetValueOrDefault(nameof(LLMOptions)) as LLMOptions; 50 | // APIキーが設定されている場合は有効 51 | if (!string.IsNullOrEmpty(op?.ApiKey)) 52 | { 53 | return ValueTask.FromResult(ValidateResult.Valid); 54 | } 55 | 56 | // 翻訳モジュールでも補正も利用しない場合は無条件で有効 57 | if (settings.SelectedPlugins[nameof(ITranslateModule)] != nameof(LLMTranslator) && (op?.CorrectMode ?? CorrectMode.None) == CorrectMode.None) 58 | { 59 | return ValueTask.FromResult(ValidateResult.Valid); 60 | } 61 | 62 | return ValueTask.FromResult(ValidateResult.Invalid("LLM", """ 63 | 翻訳モジュールにLLMが選択もしくは認識補正が有効化されています。 64 | 65 | LLMの利用にはAPIキーが必要です。 66 | 「対象ごとの設定」→「LLMOptions」タブのAPIキーを設定してください。 67 | 68 | ※ローカルLLMを利用する場合もライブラリの制約のためAPIキーが必要です。 69 | """)); 70 | } 71 | } -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.LLMPlugin/WindowTranslator.Plugin.LLMPlugin.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0-windows10.0.20348.0 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | True 12 | True 13 | Resources.resx 14 | 15 | 16 | 17 | 18 | ResXFileCodeGenerator 19 | Resources.Designer.cs 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.OneOcrPlugin/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace WindowTranslator.Plugin.OneOcrPlugin; 5 | 6 | static partial class NativeMethods 7 | { 8 | [LibraryImport("oneocr.dll")] 9 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 10 | public static partial long CreateOcrInitOptions(out long ctx); 11 | 12 | [LibraryImport("oneocr.dll")] 13 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 14 | public static partial long GetOcrLineCount(long instance, out long count); 15 | 16 | [LibraryImport("oneocr.dll")] 17 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 18 | public static partial long GetOcrLine(long instance, long index, out long line); 19 | 20 | [LibraryImport("oneocr.dll", StringMarshalling = StringMarshalling.Utf8)] 21 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 22 | public static partial long GetOcrLineContent(long line, out string content); 23 | 24 | [LibraryImport("oneocr.dll")] 25 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 26 | public static partial long GetOcrLineBoundingBox(long line, out IntPtr boundingBox); 27 | 28 | [LibraryImport("oneocr.dll")] 29 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 30 | public static partial long GetOcrLineWordCount(long instance, out long count); 31 | 32 | [LibraryImport("oneocr.dll")] 33 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 34 | public static partial long GetOcrWord(long instance, long index, out long line); 35 | 36 | [LibraryImport("oneocr.dll", StringMarshalling = StringMarshalling.Utf8)] 37 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 38 | public static partial long GetOcrWordContent(long line, out string content); 39 | 40 | [LibraryImport("oneocr.dll")] 41 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 42 | public static partial long GetOcrWordBoundingBox(long line, out IntPtr boundingBox); 43 | 44 | [LibraryImport("oneocr.dll")] 45 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 46 | public static partial long OcrProcessOptionsSetMaxRecognitionLineCount(long opt, long count); 47 | 48 | [LibraryImport("oneocr.dll")] 49 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 50 | public static partial long RunOcrPipeline(long pipeline, ref Img img, long opt, out long instance); 51 | 52 | [LibraryImport("oneocr.dll")] 53 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 54 | public static partial long CreateOcrProcessOptions(out long opt); 55 | 56 | [LibraryImport("oneocr.dll")] 57 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 58 | public static partial long OcrInitOptionsSetUseModelDelayLoad(long ctx, byte flag); 59 | 60 | [LibraryImport("oneocr.dll", StringMarshalling = StringMarshalling.Utf8)] 61 | [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] 62 | public static partial long CreateOcrPipeline(string modelPath, string key, long ctx, out long pipeline); 63 | } 64 | 65 | [StructLayout(LayoutKind.Sequential)] 66 | readonly record struct Img(int T, int Col, int Row, int Unk, long Step, IntPtr Data); 67 | 68 | [StructLayout(LayoutKind.Sequential)] 69 | struct BoundingBox 70 | { 71 | public float x1; 72 | public float y1; 73 | public float x2; 74 | public float y2; 75 | public float x3; 76 | public float y3; 77 | public float x4; 78 | public float y4; 79 | } 80 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.OneOcrPlugin/OneOcrValidator.cs: -------------------------------------------------------------------------------- 1 | using WindowTranslator.Modules; 2 | 3 | namespace WindowTranslator.Plugin.OneOcrPlugin; 4 | 5 | public class OneOcrValidator : ITargetSettingsValidator 6 | { 7 | public async ValueTask Validate(TargetSettings settings) 8 | { 9 | // 翻訳モジュールで利用しない場合は無条件で有効 10 | if (settings.SelectedPlugins[nameof(IOcrModule)] != nameof(OneOcr)) 11 | { 12 | return ValidateResult.Valid; 13 | } 14 | 15 | if (!Utility.NeedCopyDll()) 16 | { 17 | return ValidateResult.Valid; 18 | } 19 | 20 | // OneOcrのインストール先を取得 21 | var oneOcrPath = await Utility.FindOneOcrPath().ConfigureAwait(false); 22 | if (oneOcrPath == null) 23 | { 24 | return ValidateResult.Invalid("OneOcr", "依存モジュールが見つかりません。この環境では利用できません。"); 25 | } 26 | 27 | // DLLをコピー 28 | try 29 | { 30 | Utility.CopyDll(oneOcrPath); 31 | } 32 | catch (Exception ex) 33 | { 34 | return ValidateResult.Invalid("OneOcr", $"OneOcrのDLLのコピーに失敗しました。{ex.Message}"); 35 | } 36 | 37 | return ValidateResult.Valid; 38 | } 39 | } -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.OneOcrPlugin/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // このコードはツールによって生成されました。 4 | // ランタイム バージョン:4.0.30319.42000 5 | // 6 | // このファイルへの変更は、以下の状況下で不正な動作の原因になったり、 7 | // コードが再生成されるときに損失したりします。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace WindowTranslator.Plugin.OneOcrPlugin.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。 17 | /// 18 | // このクラスは StronglyTypedResourceBuilder クラスが ResGen 19 | // または Visual Studio のようなツールを使用して自動生成されました。 20 | // メンバーを追加または削除するには、.ResX ファイルを編集して、/str オプションと共に 21 | // ResGen を実行し直すか、または VS プロジェクトをビルドし直します。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// このクラスで使用されているキャッシュされた ResourceManager インスタンスを返します。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WindowTranslator.Plugin.OneOcrPlugin.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// すべてについて、現在のスレッドの CurrentUICulture プロパティをオーバーライドします 51 | /// 現在のスレッドの CurrentUICulture プロパティをオーバーライドします。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// 新Windows文字認識(ベータ) に類似しているローカライズされた文字列を検索します。 65 | /// 66 | internal static string OneOcr { 67 | get { 68 | return ResourceManager.GetString("OneOcr", resourceCulture); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.OneOcrPlugin/README.md: -------------------------------------------------------------------------------- 1 | # OneOcr 2 | 3 | ## 参考 4 | 5 | https://github.com/ksasao/SnippingToolOcrSharp -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.OneOcrPlugin/Utility.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace WindowTranslator.Plugin.OneOcrPlugin; 4 | 5 | static class Utility 6 | { 7 | private const string OneOcrDll = "oneocr.dll"; 8 | public const string OneOcrModel = "oneocr.onemodel"; 9 | private const string OnnxRuntimeDll = "onnxruntime.dll"; 10 | public static string OneOcrPath { get; } = Path.Combine(PathUtility.UserDir, "OneOcr"); 11 | 12 | private static async ValueTask GetInstallLocation(string appName) 13 | { 14 | var info = new ProcessStartInfo("powershell.exe", $"-Command \"(Get-AppxPackage -Name {appName}).InstallLocation\"") 15 | { 16 | UseShellExecute = false, 17 | CreateNoWindow = true, 18 | RedirectStandardOutput = true, 19 | }; 20 | using var p = Process.Start(info); 21 | await p!.WaitForExitAsync().ConfigureAwait(false); 22 | if (p.ExitCode != 0) 23 | { 24 | return null; 25 | } 26 | var path = await p.StandardOutput.ReadToEndAsync(); 27 | return path.Trim(); 28 | } 29 | 30 | public static async ValueTask FindOneOcrPath() 31 | { 32 | var scketch = await GetInstallLocation("Microsoft.ScreenSketch").ConfigureAwait(false); 33 | if (!string.IsNullOrEmpty(scketch)) 34 | { 35 | return Path.Combine(scketch, "SnippingTool"); 36 | } 37 | return await GetInstallLocation("Microsoft.Photos").ConfigureAwait(false); 38 | } 39 | 40 | public static bool NeedCopyDll() 41 | { 42 | if (!File.Exists(Path.Combine(OneOcrPath, OneOcrDll))) 43 | { 44 | return true; 45 | } 46 | if (!File.Exists(Path.Combine(OneOcrPath, OneOcrModel))) 47 | { 48 | return true; 49 | } 50 | if (!File.Exists(Path.Combine(OneOcrPath, OnnxRuntimeDll))) 51 | { 52 | return true; 53 | } 54 | return false; 55 | } 56 | 57 | public static void CopyDll(string path) 58 | { 59 | Directory.CreateDirectory(OneOcrPath); 60 | File.Copy(Path.Combine(path, OneOcrDll), Path.Combine(OneOcrPath, OneOcrDll), true); 61 | File.Copy(Path.Combine(path, OneOcrModel), Path.Combine(OneOcrPath, OneOcrModel), true); 62 | File.Copy(Path.Combine(path, OnnxRuntimeDll), Path.Combine(OneOcrPath, OnnxRuntimeDll), true); 63 | } 64 | } -------------------------------------------------------------------------------- /Plugins/WindowTranslator.Plugin.OneOcrPlugin/WindowTranslator.Plugin.OneOcrPlugin.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0-windows10.0.20348.0 4 | true 5 | 6 | 7 | 8 | True 9 | True 10 | Resources.resx 11 | 12 | 13 | 14 | 15 | ResXFileCodeGenerator 16 | Resources.Designer.cs 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Plugins/copy_plugins.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set BASE_DIR=%~dp0 3 | set PLUGIN_DIR=%1 4 | set PLUGIN_NAME=%2 5 | set CONFIGURATION=%3 6 | 7 | xcopy /S /Y /I %PLUGIN_DIR% %BASE_DIR%..\WindowTranslator\bin\%CONFIGURATION%\plugins\%PLUGIN_NAME%\ -------------------------------------------------------------------------------- /Sandbox/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "DeepL": { 4 | "commandName": "Project", 5 | "commandLineArgs": "DeepL" 6 | }, 7 | "GoogleAI": { 8 | "commandName": "Project", 9 | "commandLineArgs": "GoogleAI" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Sandbox/Sandbox.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 1cda64be-8678-4eb9-a533-77744fa3438c 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/Collections/RemovableQueue.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Diagnostics.CodeAnalysis; 3 | 4 | namespace WindowTranslator.Collections; 5 | 6 | /// 7 | /// 削除可能なキュー 8 | /// 9 | /// 要素の型 10 | #pragma warning disable CA1711 // ちゃんとdotnetのQueueとして動くのでOK 11 | public class RemovableQueue : ICollection, IReadOnlyCollection 12 | #pragma warning restore CA1711 13 | where T : notnull 14 | { 15 | private readonly LinkedList inner; 16 | 17 | /// 18 | public int Count => inner.Count; 19 | 20 | /// 21 | public bool IsReadOnly => false; 22 | 23 | /// 24 | /// 初期化します。 25 | /// 26 | public RemovableQueue() 27 | => inner = new(); 28 | 29 | /// 30 | /// 初期化します。 31 | /// 32 | /// 初期キュー 33 | public RemovableQueue(IEnumerable list) 34 | => inner = new(list); 35 | 36 | /// 37 | /// 要素を取り出します。 38 | /// 取り出した要素はキューから削除されます。 39 | /// 40 | /// 一番古い要素 41 | /// 取り出せたかどうか 42 | public bool TryDequeue([NotNullWhen(true)] out T? item) 43 | { 44 | if (inner.First is { } first) 45 | { 46 | item = first.Value; 47 | inner.RemoveFirst(); 48 | return true; 49 | } 50 | else 51 | { 52 | item = default; 53 | return false; 54 | } 55 | } 56 | 57 | /// 58 | /// 要素を取り出します。 59 | /// 60 | /// 一番古い要素 61 | /// 62 | public T Dequeue() 63 | => TryDequeue(out var item) ? item : throw new InvalidOperationException(); 64 | 65 | /// 66 | public bool Remove(T item) 67 | => inner.Remove(item); 68 | 69 | /// 70 | public void Add(T item) 71 | => inner.AddLast(item); 72 | 73 | /// 74 | public void Clear() 75 | => inner.Clear(); 76 | 77 | /// 78 | public bool Contains(T item) 79 | => inner.Contains(item); 80 | 81 | /// 82 | public void CopyTo(T[] array, int arrayIndex) 83 | => inner.CopyTo(array, arrayIndex); 84 | 85 | /// 86 | public IEnumerator GetEnumerator() 87 | => inner.GetEnumerator(); 88 | 89 | IEnumerator IEnumerable.GetEnumerator() 90 | => inner.GetEnumerator(); 91 | } 92 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/ComponentModel/DefaultModuleAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator.ComponentModel; 2 | 3 | /// 4 | /// デフォルトで使用するモジュールを示す属性 5 | /// 6 | [AttributeUsage(AttributeTargets.Class)] 7 | public class DefaultModuleAttribute : Attribute; -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/ComponentModel/LocalizedDescriptionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.ComponentModel; 3 | using System.Globalization; 4 | using System.Resources; 5 | 6 | namespace WindowTranslator.ComponentModel; 7 | 8 | /// 9 | /// ローカライズされた説明を提供します。 10 | /// 11 | /// リソースの型 12 | /// キー 13 | public class LocalizedDescriptionAttribute(Type resourceType, string description) : DescriptionAttribute(description) 14 | { 15 | private static readonly ConcurrentDictionary resourceCache = new(); 16 | 17 | /// 18 | /// リソースの型を取得または設定します。 19 | /// 20 | public Type ResourceType { get; set; } = resourceType; 21 | 22 | /// 23 | public override string Description 24 | { 25 | get 26 | { 27 | var resourceManager = resourceCache.GetOrAdd(ResourceType, type => new ResourceManager(type)); 28 | return resourceManager.GetString(this.DescriptionValue, CultureInfo.CurrentUICulture) ?? this.DescriptionValue; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/ComponentModel/LocalizedDisplayNameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.ComponentModel; 3 | using System.Globalization; 4 | using System.Resources; 5 | 6 | namespace WindowTranslator.ComponentModel; 7 | 8 | /// 9 | /// ローカライズされた表示名を提供します。 10 | /// 11 | /// リソースの型 12 | /// キー 13 | public class LocalizedDisplayNameAttribute(Type resourceType, string displayName) : DisplayNameAttribute(displayName) 14 | { 15 | private static readonly ConcurrentDictionary resourceCache = new(); 16 | 17 | /// 18 | /// リソースの型を取得または設定します。 19 | /// 20 | public Type ResourceType { get; set; } = resourceType; 21 | 22 | /// 23 | public override string DisplayName 24 | { 25 | get 26 | { 27 | var resourceManager = resourceCache.GetOrAdd(ResourceType, type => new ResourceManager(type)); 28 | return resourceManager.GetString(this.DisplayNameValue, CultureInfo.CurrentUICulture) ?? this.DisplayNameValue; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/Extensions/AsyncEnumerable.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Threading.Channels; 3 | 4 | namespace WindowTranslator.Extensions; 5 | 6 | /// 7 | /// の拡張メソッドを提供します。 8 | /// 9 | public static class AsyncEnumerable 10 | { 11 | /// 12 | /// 並列に処理して終わった順に結果を返します。 13 | /// 14 | /// 処理の対象の型 15 | /// 処理の結果の型 16 | /// 処理の対象のリスト 17 | /// 並列で行う処理 18 | /// キャンセルトークン 19 | /// 結果 20 | public static async IAsyncEnumerable WhenEach(this IAsyncEnumerable source, Func> func, [EnumeratorCancellation] CancellationToken cancellationToken = default) 21 | { 22 | var channel = Channel.CreateUnbounded(); 23 | using var completionCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); 24 | Task? task = null; 25 | try 26 | { 27 | task = Parallel.ForEachAsync(source, completionCts.Token, async (s, ct) => 28 | { 29 | try 30 | { 31 | var result = await func(s, ct).ConfigureAwait(false); 32 | await channel.Writer.WriteAsync(result, ct).ConfigureAwait(false); 33 | } 34 | catch (Exception e) when (e is not OperationCanceledException) 35 | { 36 | channel.Writer.Complete(e); 37 | } 38 | }).ContinueWith(static (_, s) => ((ChannelWriter)s!).Complete(), channel.Writer, cancellationToken); 39 | 40 | await foreach (var result in channel.Reader.ReadAllAsync(cancellationToken).ConfigureAwait(false)) 41 | { 42 | yield return result; 43 | cancellationToken.ThrowIfCancellationRequested(); 44 | } 45 | } 46 | finally 47 | { 48 | completionCts.Cancel(); 49 | try 50 | { 51 | if (task is { } t) 52 | { 53 | await t.ConfigureAwait(false); 54 | } 55 | } 56 | catch (OperationCanceledException) { } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/Extensions/BitmapExtensions.cs: -------------------------------------------------------------------------------- 1 | #if WINDOWS 2 | using System.Buffers; 3 | using System.Drawing; 4 | using Windows.Graphics.Imaging; 5 | using Windows.Storage.Streams; 6 | 7 | namespace WindowTranslator.Extensions; 8 | 9 | /// 10 | /// 画像関連の拡張メソッド 11 | /// 12 | public static class BitmapExtensions 13 | { 14 | private static readonly ThreadLocal stream = new(() => new()); 15 | 16 | /// 17 | /// ソフトウェアビットマップをJPEG形式でエンコードし、Base64文字列に変換します。 18 | /// 19 | /// エンコードするソフトウェアビットマップ。 20 | /// エンコードする領域。 21 | /// Base64文字列。 22 | public static async Task EncodeToJpegBase64(this SoftwareBitmap bitmap, Rectangle rect = default) 23 | { 24 | var (mem, size) = await bitmap.EncodeToJpeg(rect).ConfigureAwait(false); 25 | using var _ = mem; 26 | var buffer = mem.Memory[..size]; 27 | return Convert.ToBase64String(buffer.Span); 28 | } 29 | 30 | /// 31 | /// ソフトウェアビットマップをJPEG形式でエンコードし、バイト配列に変換します。 32 | /// 33 | /// エンコードするソフトウェアビットマップ。 34 | /// エンコードする領域。 35 | /// Jpeg形式のバイト配列。 36 | public static async Task EncodeToJpegBytes(this SoftwareBitmap bitmap, Rectangle rect = default) 37 | { 38 | var (mem, size) = await bitmap.EncodeToJpeg(rect).ConfigureAwait(false); 39 | using var _ = mem; 40 | var buffer = mem.Memory[..size]; 41 | return buffer.ToArray(); 42 | } 43 | 44 | /// 45 | /// ソフトウェアビットマップをJPEG形式でエンコードします。 46 | /// 47 | /// エンコードするソフトウェアビットマップ。 48 | /// エンコードする領域。 49 | /// エンコードしたデータとサイズ 50 | public static async Task<(IMemoryOwner memory, int size)> EncodeToJpeg(this SoftwareBitmap bitmap, Rectangle rect = default) 51 | { 52 | var s = stream.Value!; 53 | s.Seek(0); 54 | var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, s); 55 | encoder.SetSoftwareBitmap(bitmap); 56 | if (rect != default) 57 | { 58 | encoder.BitmapTransform.Bounds = new((uint)rect.X, (uint)rect.Y, (uint)rect.Width, (uint)rect.Height); 59 | } 60 | await encoder.FlushAsync(); 61 | s.Seek(0); 62 | var mem = MemoryPool.Shared.Rent((int)s.Size); 63 | var buffer = mem.Memory[..(int)s.Size]; 64 | await s.AsStreamForRead().ReadExactlyAsync(buffer).ConfigureAwait(false); 65 | return (mem, (int)s.Size); 66 | } 67 | 68 | /// 69 | /// TextRect オブジェクトを System.Drawing.Rectangle に変換します。 70 | /// 71 | /// 変換する TextRect オブジェクト。 72 | /// 変換された System.Drawing.Rectangle オブジェクト。 73 | public static Rectangle ToRect(this TextRect rect) 74 | => new((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height); 75 | } 76 | #endif 77 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/Extensions/SystemExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator.Extensions; 2 | 3 | /// 4 | /// 汎用的な拡張メソッド 5 | /// 6 | public static class SystemExtensions 7 | { 8 | /// 9 | /// 指定した値が指定した範囲内にあるかどうかを判定します。 10 | /// 11 | /// 12 | /// 判定する対象 13 | /// 判定する範囲 14 | /// 含まれているかどうか 15 | public static bool Or(this T target, params T[] values) 16 | where T : Enum 17 | => values.Contains(target); 18 | } 19 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/IPluginParam.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator; 2 | 3 | /// 4 | /// プラグインのパラメータを表します。 5 | /// 6 | public interface IPluginParam 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/ITargetSettingsValidator.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator; 2 | 3 | /// 4 | /// 設定の検証を行うインターフェース 5 | /// 6 | public interface ITargetSettingsValidator 7 | { 8 | /// 9 | /// 設定の検証を行う 10 | /// 11 | /// 検証結果 12 | ValueTask Validate(TargetSettings settings); 13 | } 14 | 15 | /// 16 | /// 検証結果 17 | /// 18 | /// タイトル 19 | /// 検証結果 20 | /// 検証エラーのメッセージ 21 | public record ValidateResult(bool IsValid, string Title, string Message) 22 | { 23 | /// 24 | /// 検証結果が有効であることを示す 25 | /// 26 | public static ValidateResult Valid { get; } = new(true, string.Empty, string.Empty); 27 | 28 | /// 29 | /// 検証結果が無効であることを示す 30 | /// 31 | /// タイトル 32 | /// 検証エラーのメッセージ 33 | /// 検証結果 34 | public static ValidateResult Invalid(string title, string message) => new(false, title, message); 35 | } -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/LanguageOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | 3 | namespace WindowTranslator; 4 | 5 | /// 6 | /// 翻訳言語のオプションを表します。 7 | /// 8 | public class LanguageOptions 9 | { 10 | /// 11 | /// 翻訳元言語を取得または設定します。 12 | /// 13 | public string Source { get; set; } = "en-US"; 14 | 15 | /// 16 | /// 翻訳先言語を取得または設定します。 17 | /// 18 | public string Target { get; set; } = CultureInfo.CurrentUICulture.Name; 19 | } 20 | 21 | /// 22 | /// 言語に関連するユーティリティメソッドを提供します。 23 | /// 24 | public static class LanguageUtility 25 | { 26 | /// 27 | /// 指定された言語がスペースを含むかどうかを判断します。 28 | /// 29 | /// 言語コード 30 | /// スペースを含む場合は true、それ以外の場合は false 31 | public static bool IsSpaceLang(string lang) 32 | => lang[..2] is not "ja" or "zh"; 33 | 34 | /// 35 | /// 指定された言語が特殊なグリフの言語かどうかを判断します。 36 | /// 37 | /// 言語コード 38 | /// 特殊な言語の場合は true、それ以外の場合は false 39 | public static bool IsSpecialLang(string lang) 40 | => lang[..2] is "ja" or "zh" or "ko" or "ru"; 41 | 42 | /// 43 | /// 指定されたテキストの単語数をカウントします。 44 | /// 45 | /// カウントするテキスト 46 | /// 単語数 47 | public static int WordCount(string text) 48 | { 49 | var span = text.AsSpan(); 50 | var count = 0; 51 | while (!span.IsEmpty) 52 | { 53 | count++; 54 | var index = span.IndexOf(' '); 55 | if (index == -1) 56 | { 57 | break; 58 | } 59 | span = span[(index + 1)..]; 60 | } 61 | return count; 62 | } 63 | } -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/Modules/ICacheModule.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator.Modules; 2 | 3 | /// 4 | /// キャッシュモジュールのインターフェースです。 5 | /// 6 | public interface ICacheModule 7 | { 8 | /// 9 | /// テキストがキャッシュに存在するかどうかを返します。 10 | /// 11 | bool Contains(string src); 12 | 13 | /// 14 | /// 翻訳結果をキャッシュに追加します。 15 | /// 16 | void AddRange(IEnumerable<(string src, string dst)> pairs); 17 | 18 | /// 19 | /// キャッシュから翻訳結果を取得します。 20 | /// 21 | string Get(string src); 22 | } 23 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/Modules/IFilterModule.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace WindowTranslator.Modules; 4 | 5 | /// 6 | /// 翻訳前後のテキストに対してフィルター処理を行うモジュールのインターフェース 7 | /// 8 | public interface IFilterModule 9 | { 10 | /// 11 | /// 優先度 12 | /// 13 | /// 14 | /// 数値が大きい順に翻訳前処理が行われ、小さい順に翻訳後処理が行われる 15 | /// 16 | double Priority => 0; 17 | 18 | /// 19 | /// 翻訳前に行うフィルター処理 20 | /// 21 | /// 処理対象のテキスト 22 | /// コンテキスト 23 | /// フィンルター後のテキスト 24 | IAsyncEnumerable ExecutePreTranslate(IAsyncEnumerable texts, FilterContext context); 25 | 26 | /// 27 | /// 翻訳後に行うフィルター処理 28 | /// 29 | /// 処理対象のテキスト 30 | /// コンテキスト 31 | /// フィンルター後のテキスト 32 | IAsyncEnumerable ExecutePostTranslate(IAsyncEnumerable texts, FilterContext context); 33 | } 34 | 35 | /// 36 | /// フィルター処理の文脈 37 | /// 38 | public record FilterContext 39 | { 40 | #if WINDOWS 41 | /// 42 | /// 認識した画像 43 | /// 44 | public required Windows.Graphics.Imaging.SoftwareBitmap SoftwareBitmap { get; init; } 45 | #endif 46 | 47 | /// 48 | /// 認識した画像のサイズ 49 | /// 50 | public Size ImageSize { get; init; } 51 | } 52 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/Modules/IOcrModule.cs: -------------------------------------------------------------------------------- 1 | using PropertyTools.DataAnnotations; 2 | using CategoryAttribute = System.ComponentModel.CategoryAttribute; 3 | 4 | namespace WindowTranslator.Modules; 5 | 6 | /// 7 | /// テキスト認識モジュールのインターフェース 8 | /// 9 | public interface IOcrModule 10 | { 11 | #if WINDOWS 12 | /// 13 | /// 画像からテキストを認識する 14 | /// 15 | ValueTask> RecognizeAsync(Windows.Graphics.Imaging.SoftwareBitmap bitmap); 16 | #endif 17 | } 18 | 19 | /// 20 | /// 基本的なOCRパラメータ 21 | /// 22 | public class BasicOcrParam : IPluginParam 23 | { 24 | /// 25 | /// 認識スケール 26 | /// 27 | [Category("Recognize")] 28 | [Slidable(0.5, 4, 0.1, 0.5, true, 0.1)] 29 | [FormatString("F2")] 30 | public double Scale { get; set; } = 1.0; 31 | 32 | /// 33 | /// X位置のしきい値 34 | /// 35 | [Category("MergeThrethold")] 36 | [FormatString("P2")] 37 | [Slidable(0, 0.2, .001, .01, true, .001)] 38 | public double XPosThrethold { get; set; } = .005; 39 | 40 | /// 41 | /// Y位置のしきい値 42 | /// 43 | [Category("MergeThrethold")] 44 | [FormatString("P2")] 45 | [Slidable(0, 0.2, .001, .01, true, .001)] 46 | public double YPosThrethold { get; set; } = .005; 47 | 48 | /// 49 | /// 行間のしきい値 50 | /// 51 | [Category("MergeThrethold")] 52 | [Slidable(0, 1, .01, .1, true, .01)] 53 | [FormatString("P2")] 54 | public double LeadingThrethold { get; set; } = .80; 55 | 56 | /// 57 | /// 文字間のしきい値 58 | /// 59 | [Category("MergeThrethold")] 60 | [Slidable(0, 3, .01, .1, true, .01)] 61 | [FormatString("P2")] 62 | public double SpacingThreshold { get; set; } = 1.1; 63 | 64 | /// 65 | /// フォントサイズのしきい値 66 | /// 67 | [Category("MergeThrethold")] 68 | [Slidable(0, 1, .01, .1, true, .01)] 69 | [FormatString("P2")] 70 | public double FontSizeThrethold { get; set; } = .25; 71 | 72 | /// 73 | /// リストのマージを避けるかどうか 74 | /// 75 | [Category("MergeThrethold")] 76 | public bool IsAvoidMergeList { get; set; } = false; 77 | 78 | /// 79 | /// バッファサイズ 80 | /// 81 | [Category("Misc")] 82 | [Spinnable] 83 | public int BufferSize { get; set; } = 3; 84 | } 85 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/Modules/ITranslateModule.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator.Modules; 2 | 3 | /// 4 | /// Interface for translation modules. 5 | /// 6 | public interface ITranslateModule 7 | { 8 | /// 9 | /// モジュール名 10 | /// 11 | public string Name => GetType().Name; 12 | 13 | /// 14 | /// 渡されたテキストを翻訳します。 15 | /// 16 | /// 翻訳するテキストの配列。 17 | /// 翻訳されたテキストの配列。 18 | ValueTask TranslateAsync(TextInfo[] srcTexts); 19 | 20 | /// 21 | /// 翻訳モジュールに用語を登録します。 22 | /// 23 | /// 用語 24 | /// 非同期処理 25 | ValueTask RegisterGlossaryAsync(IReadOnlyDictionary glossary) 26 | => default; 27 | 28 | /// 29 | /// 翻訳するテキストの文脈を登録します。 30 | /// 31 | /// 文脈 32 | void RegisterContext(string context) 33 | { 34 | } 35 | } -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/OcrUtility.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace WindowTranslator; 4 | 5 | /// 6 | /// OCRに関連するユーティリティメソッドを提供します。 7 | /// 8 | public static partial class OcrUtility 9 | { 10 | 11 | [GeneratedRegex(@"^[\s\p{S}\p{P}\d]+$")] 12 | public static partial Regex AllSymbolOrSpace(); 13 | } -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/PathUtility.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator; 2 | 3 | /// 4 | /// パスのユーティリティクラスです。 5 | /// 6 | public static class PathUtility 7 | { 8 | /// 9 | /// ユーザー設定ディレクトリのパスを取得します。 10 | /// 11 | #if DEBUG 12 | public static readonly string UserDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".wt.debug"); 13 | #else 14 | public static readonly string UserDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".wt"); 15 | #endif 16 | 17 | /// 18 | /// ユーザー設定ファイルのパスを取得します。 19 | /// 20 | public static readonly string UserSettings = Path.Combine(UserDir, "settings.json"); 21 | } 22 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/ResourceUtility.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | 4 | namespace WindowTranslator; 5 | 6 | /// 7 | /// リソースに関連するユーティリティメソッドを提供します。 8 | /// 9 | public static class ResourceUtility 10 | { 11 | private static readonly Dictionary ResManagerCache = new(); 12 | 13 | /// 14 | /// 型からリソースマネージャを取得します。 15 | /// 16 | /// 型 17 | /// リソースマネージャー 18 | public static ResourceManager? GetResourceManager(this Type declaringType) 19 | { 20 | var assembly = declaringType.Assembly; 21 | if (ResManagerCache.TryGetValue(assembly, out var resManager)) 22 | { 23 | return resManager; 24 | } 25 | var ns = declaringType.Namespace; 26 | Type? resType = null; 27 | while (resType is null && ns is not null) 28 | { 29 | resType = assembly.GetType($"{ns}.Properties.Resources"); 30 | int lastDotIndex = ns.LastIndexOf('.'); 31 | ns = lastDotIndex > 0 ? ns[..lastDotIndex] : null; 32 | } 33 | if (resType is null) 34 | { 35 | return null; 36 | } 37 | return ResManagerCache[assembly] = new(resType); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/Stores/IProcessInfoStore.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator.Stores; 2 | 3 | /// 4 | /// 翻訳対象のプロセス情報を保持するインターフェース 5 | /// 6 | public interface IProcessInfoStore 7 | { 8 | /// 9 | /// 対象のウィンドウハンドル 10 | /// 11 | IntPtr MainWindowHandle { get; } 12 | 13 | /// 14 | /// 対象のプロセスの名前 15 | /// 16 | string Name { get; } 17 | } 18 | -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/TextRect.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace WindowTranslator; 4 | 5 | /// 6 | /// 翻訳テキストの矩形情報 7 | /// 8 | /// 9 | /// テキスト 10 | /// が true の場合は翻訳後のテキスト 11 | /// 12 | /// X位置 13 | /// Y位置 14 | /// 幅 15 | /// 高さ 16 | /// フォントサイズ 17 | /// 複数行かどうか 18 | /// 文字色 19 | /// 背景色 20 | /// が翻訳後のテキストかどうか 21 | public record TextRect(string Text, double X, double Y, double Width, double Height, double FontSize, bool MultiLine, Color Foreground, Color Background, bool IsTranslated = false) 22 | : TextInfo(Text, IsTranslated) 23 | { 24 | /// 25 | /// 空 26 | /// 27 | public static TextRect Empty { get; } = new TextRect(string.Empty, 0, 0, 0, 0, 0, false); 28 | 29 | /// 30 | /// コンストラクタ 31 | /// 32 | /// テキスト 33 | /// X位置 34 | /// Y位置 35 | /// 幅 36 | /// 高さ 37 | /// フォントサイズ 38 | /// 複数行かどうか 39 | public TextRect(string text, double x, double y, double width, double height, double fontSize, bool multiLine) 40 | : this(text, x, y, width, height, fontSize, multiLine, Color.Red, Color.WhiteSmoke) 41 | { 42 | } 43 | }; 44 | 45 | /// 46 | /// 翻訳テキストの矩形情報 47 | /// 48 | /// 49 | /// テキスト 50 | /// が true の場合は翻訳後のテキスト 51 | /// 52 | /// が翻訳後のテキストかどうか 53 | public record TextInfo(string Text, bool IsTranslated = false) 54 | { 55 | /// 56 | /// このテキストの文脈 57 | /// 58 | public string Context { get; init; } = string.Empty; 59 | }; -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/UserSettings.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator; 2 | 3 | /// 4 | /// ユーザー設定 5 | /// 6 | public class UserSettings 7 | { 8 | /// 9 | /// 共通設定 10 | /// 11 | public CommonSettings Common { get; init; } = new(); 12 | 13 | /// 14 | /// 翻訳対象ごとの設定 15 | /// 16 | public Dictionary Targets { get; init; } = new(StringComparer.OrdinalIgnoreCase); 17 | } 18 | 19 | /// 20 | /// 共通設定 21 | /// 22 | public class CommonSettings 23 | { 24 | /// 25 | /// 翻訳結果の表示モード 26 | /// 27 | public ViewMode ViewMode { get; set; } = ViewMode.Overlay; 28 | 29 | /// 30 | /// キャプチャー可能にするか 31 | /// 32 | public bool IsEnableCaptureOverlay { get; set; } 33 | 34 | /// 35 | /// オーバーレイの切り替え方法 36 | /// 37 | public OverlaySwitch OverlaySwitch { get; set; } = OverlaySwitch.Hold; 38 | 39 | /// 40 | /// 自動的に翻訳を発動するか 41 | /// 42 | public bool IsEnableAutoTarget { get; set; } 43 | } 44 | 45 | /// 46 | /// 翻訳対象ごとの設定 47 | /// 48 | public class TargetSettings 49 | { 50 | /// 51 | /// 翻訳言語のオプション 52 | /// 53 | public LanguageOptions Language { get; init; } = new(); 54 | 55 | /// 56 | /// 翻訳結果を表示するためのフォント 57 | /// 58 | public string Font { get; set; } = "Yu Gothic UI"; 59 | 60 | /// 61 | /// フォントの拡大率 62 | /// 63 | public double FontScale { get; set; } = 1.1; 64 | 65 | /// 66 | /// オーバーレイ表示の切り替えショートカットキー 67 | /// 68 | public string OverlayShortcut { get; set; } = "Ctrl + Alt + O"; 69 | 70 | /// 71 | /// プラグインの選択 72 | /// 73 | public Dictionary SelectedPlugins { get; init; } = new(StringComparer.OrdinalIgnoreCase); 74 | 75 | /// 76 | /// プラグインのパラメータ 77 | /// 78 | public Dictionary PluginParams { get; init; } = new(StringComparer.OrdinalIgnoreCase); 79 | } 80 | 81 | /// 82 | /// 翻訳結果の表示モード 83 | /// 84 | public enum ViewMode 85 | { 86 | /// 87 | /// キャプチャー 88 | /// 89 | Capture, 90 | 91 | /// 92 | /// オーバーレイ 93 | /// 94 | Overlay, 95 | } 96 | 97 | /// 98 | /// オーバレイ表示の切り替え方法 99 | /// 100 | public enum OverlaySwitch 101 | { 102 | /// 103 | /// ホールド 104 | /// 105 | Hold, 106 | 107 | /// 108 | /// トグル 109 | /// 110 | Toggle, 111 | } -------------------------------------------------------------------------------- /WindowTranslator.Abstractions/WindowTranslator.Abstractions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0;net8.0-windows10.0.20348.0 5 | WindowTranslator 6 | true 7 | wt.png 8 | README.md 9 | true 10 | true 11 | true 12 | true 13 | Recommended 14 | true 15 | 16 | 17 | 18 | all 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | 21 | 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | all 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | True 34 | True 35 | Resources.resx 36 | 37 | 38 | 39 | 40 | ResXFileCodeGenerator 41 | Resources.Designer.cs 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /WindowTranslator.Wix/Program.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/WindowTranslator.Wix/Program.cs -------------------------------------------------------------------------------- /WindowTranslator.Wix/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "WindowTranslator.Wix": { 4 | "commandName": "Project", 5 | "workingDirectory": "$(ProjectDir)" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /WindowTranslator.Wix/WindowTranslator.Wix.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | VdLavel.Wix 6 | net472 7 | latest 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /WindowTranslator.Wix/installer_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/WindowTranslator.Wix/installer_back.png -------------------------------------------------------------------------------- /WindowTranslator.Wix/installer_bunner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/WindowTranslator.Wix/installer_bunner.png -------------------------------------------------------------------------------- /WindowTranslator/App.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /WindowTranslator/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Composition.WindowsRuntimeHelpers; 2 | using System.Windows; 3 | using Windows.System; 4 | 5 | namespace WindowTranslator; 6 | 7 | /// 8 | /// Interaction logic for App.xaml 9 | /// 10 | public partial class App : Application 11 | { 12 | #pragma warning disable IDE0052 // WinUIのコントロール使うために初期化する必要がある 13 | private readonly DispatcherQueueController? controller; 14 | #pragma warning restore IDE0052 15 | private readonly TaskCompletionSource tcs = new(); 16 | 17 | public App() 18 | { 19 | this.controller = CoreMessagingHelper.CreateDispatcherQueueControllerForCurrentThread(); 20 | InitializeComponent(); 21 | } 22 | protected override void OnStartup(StartupEventArgs e) 23 | { 24 | base.OnStartup(e); 25 | this.tcs.SetResult(); 26 | } 27 | 28 | public Task WaitForStartupAsync() 29 | #pragma warning disable VSTHRD003 // Avoid awaiting foreign Tasks 30 | => this.tcs.Task; 31 | #pragma warning restore VSTHRD003 // Avoid awaiting foreign Tasks 32 | } 33 | -------------------------------------------------------------------------------- /WindowTranslator/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Versioning; 2 | using System.Windows; 3 | 4 | [assembly: ThemeInfo( 5 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 6 | //(used if a resource is not found in the page, 7 | // or application resource dictionaries) 8 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 9 | //(used if a resource is not found in the page, 10 | // app, or any theme specific resource dictionaries) 11 | )] 12 | 13 | [assembly: SupportedOSPlatform("windows10.0.19041")] -------------------------------------------------------------------------------- /WindowTranslator/ComponentModel/DisposeAction.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator.ComponentModel; 2 | public readonly struct DisposeAction(Action action) : IDisposable 3 | { 4 | private readonly Action action = action; 5 | 6 | public void Dispose() => action(); 7 | } 8 | -------------------------------------------------------------------------------- /WindowTranslator/ComponentModel/ResetableLazy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | namespace WindowTranslator.ComponentModel; 8 | 9 | public class ResetableLazy 10 | { 11 | 12 | private Lazy lazy; 13 | private readonly Func valueFactory; 14 | private readonly LazyThreadSafetyMode mode; 15 | 16 | public bool IsValueCreated => this.lazy.IsValueCreated; 17 | 18 | public T Value => this.lazy.Value; 19 | 20 | public ResetableLazy(Func valueFactory) 21 | : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication) 22 | { 23 | } 24 | 25 | public ResetableLazy(Func valueFactory, bool isThreadSafe) 26 | : this(valueFactory, isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None) 27 | { 28 | } 29 | 30 | public ResetableLazy(Func valueFactory, LazyThreadSafetyMode mode) 31 | { 32 | this.valueFactory = valueFactory; 33 | this.mode = mode; 34 | this.lazy = CreateLazy(); 35 | } 36 | 37 | private Lazy CreateLazy() 38 | => new(this.valueFactory, this.mode); 39 | 40 | 41 | public void Reset() 42 | { 43 | if (this.lazy.IsValueCreated) 44 | { 45 | Interlocked.Exchange(ref this.lazy, CreateLazy()); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /WindowTranslator/Controls/LineBreakSplitParser.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Windows.Documents; 3 | using MdXaml.Plugins; 4 | 5 | namespace WindowTranslator.Controls; 6 | 7 | public partial class LineBreakSplitPluginSetup : IPluginSetup 8 | { 9 | public void Setup(MdXamlPlugins plugins) 10 | => plugins.Inline.Add(LineBreakSplitParser.Instance); 11 | 12 | private partial class LineBreakSplitParser : IInlineParser 13 | { 14 | public static LineBreakSplitParser Instance { get; } = new(); 15 | public Regex FirstMatchPattern { get; } = LineBreakRegex(); 16 | 17 | [GeneratedRegex(@"(.*)\n", RegexOptions.Compiled)] 18 | private static partial Regex LineBreakRegex(); 19 | 20 | public IEnumerable Parse(string text, Match firstMatch, IMarkdown engine, out int parseTextBegin, out int parseTextEnd) 21 | { 22 | parseTextBegin = firstMatch.Index; 23 | parseTextEnd = firstMatch.Index + firstMatch.Length; 24 | 25 | return [new Run(firstMatch.Groups[1].Value), new LineBreak()]; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /WindowTranslator/Controls/NotifyIcon2.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using System.Windows.Input; 4 | using Wpf.Ui.Tray.Controls; 5 | 6 | namespace WindowTranslator.Controls; 7 | 8 | public class NotifyIcon2 : NotifyIcon, ICommandSource 9 | { 10 | /// Identifies the dependency property. 11 | public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(nameof(Command), typeof(ICommand), typeof(NotifyIcon2), new PropertyMetadata(null)); 12 | /// Identifies the dependency property. 13 | public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(nameof(CommandParameter), typeof(object), typeof(NotifyIcon2), new PropertyMetadata(null)); 14 | /// Identifies the dependency property. 15 | public static readonly DependencyProperty CommandTargetProperty = DependencyProperty.Register(nameof(CommandTarget), typeof(IInputElement), typeof(NotifyIcon2), new PropertyMetadata(null)); 16 | 17 | public IInputElement? CommandTarget 18 | { 19 | get => (IInputElement?)GetValue(CommandTargetProperty); 20 | set => SetValue(CommandTargetProperty, value); 21 | } 22 | 23 | public ICommand? Command 24 | { 25 | get => (ICommand?)GetValue(CommandProperty); 26 | set => SetValue(CommandProperty, value); 27 | } 28 | 29 | public object? CommandParameter 30 | { 31 | get => GetValue(CommandParameterProperty); 32 | set => SetValue(CommandParameterProperty, value); 33 | } 34 | 35 | public NotifyIcon2() 36 | { 37 | this.DataContextChanged += NotifyIcon2_DataContextChanged; 38 | } 39 | 40 | private void NotifyIcon2_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) 41 | { 42 | if (this.Menu != null) 43 | { 44 | this.Menu.DataContext = this.DataContext; 45 | } 46 | } 47 | 48 | protected override void OnMenuChanged(ContextMenu contextMenu) 49 | { 50 | contextMenu.DataContext = this.DataContext; 51 | base.OnMenuChanged(contextMenu); 52 | } 53 | 54 | override protected void Dispose(bool disposing) 55 | { 56 | if (disposing) 57 | { 58 | this.DataContextChanged -= NotifyIcon2_DataContextChanged; 59 | } 60 | base.Dispose(disposing); 61 | } 62 | 63 | protected override void OnLeftClick() 64 | { 65 | if (Command is { } command) 66 | { 67 | if (command is RoutedCommand routed) 68 | { 69 | var target = CommandTarget ?? this; 70 | if (routed.CanExecute(CommandParameter, target)) 71 | { 72 | routed.Execute(CommandParameter, target); 73 | } 74 | } 75 | else if (command.CanExecute(CommandParameter)) 76 | { 77 | command.Execute(CommandParameter); 78 | } 79 | } 80 | else 81 | { 82 | base.OnLeftClick(); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /WindowTranslator/Controls/OverlayTextsControl.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace WindowTranslator.Controls; 5 | 6 | public class OverlayTextsControl : Control 7 | { 8 | static OverlayTextsControl() => DefaultStyleKeyProperty.OverrideMetadata(typeof(OverlayTextsControl), new FrameworkPropertyMetadata(typeof(OverlayTextsControl))); 9 | 10 | public IEnumerable Texts 11 | { 12 | get => (IEnumerable)GetValue(TextsProperty); 13 | set => SetValue(TextsProperty, value); 14 | } 15 | 16 | /// Identifies the dependency property. 17 | public static readonly DependencyProperty TextsProperty = DependencyProperty.Register(nameof(Texts), typeof(IEnumerable), typeof(OverlayTextsControl), new PropertyMetadata(Enumerable.Empty())); 18 | 19 | public double RectWidth 20 | { 21 | get => (double)GetValue(RectWidthProperty); 22 | set => SetValue(RectWidthProperty, value); 23 | } 24 | 25 | /// Identifies the dependency property. 26 | public static readonly DependencyProperty RectWidthProperty = DependencyProperty.Register(nameof(RectWidth), typeof(double), typeof(OverlayTextsControl), new PropertyMetadata(double.NaN)); 27 | 28 | public double RectHeight 29 | { 30 | get => (double)GetValue(RectHeightProperty); 31 | set => SetValue(RectHeightProperty, value); 32 | } 33 | 34 | /// Identifies the dependency property. 35 | public static readonly DependencyProperty RectHeightProperty = DependencyProperty.Register(nameof(RectHeight), typeof(double), typeof(OverlayTextsControl), new PropertyMetadata(double.NaN)); 36 | 37 | public Point MousePos 38 | { 39 | get => (Point)GetValue(MousePosProperty); 40 | set => SetValue(MousePosProperty, value); 41 | } 42 | 43 | /// Identifies the dependency property. 44 | public static readonly DependencyProperty MousePosProperty = 45 | DependencyProperty.Register(nameof(MousePos), typeof(Point), typeof(OverlayTextsControl), new PropertyMetadata(new Point(double.NaN, double.NaN))); 46 | 47 | public double Scale 48 | { 49 | get => (double)GetValue(ScaleProperty); 50 | set => SetValue(ScaleProperty, value); 51 | } 52 | 53 | /// Identifies the dependency property. 54 | public static readonly DependencyProperty ScaleProperty = 55 | DependencyProperty.Register(nameof(Scale), typeof(double), typeof(OverlayTextsControl), new PropertyMetadata(1.0)); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /WindowTranslator/Controls/ProcessWindowPresenter.cs: -------------------------------------------------------------------------------- 1 | using HwndExtensions.Host; 2 | using PInvoke; 3 | using System.Runtime.InteropServices; 4 | using System.Windows; 5 | using System.Windows.Interop; 6 | using WindowTranslator.Stores; 7 | 8 | namespace WindowTranslator.Controls; 9 | public class ProcessWindowPresenter : HwndHostPresenter 10 | { 11 | public IProcessInfoStore? TargetProcess 12 | { 13 | get => (IProcessInfoStore?)GetValue(TargetProcessProperty); 14 | set => SetValue(TargetProcessProperty, value); 15 | } 16 | 17 | /// Identifies the dependency property. 18 | public static readonly DependencyProperty TargetProcessProperty = 19 | DependencyProperty.Register( 20 | nameof(TargetProcess), 21 | typeof(IProcessInfoStore), 22 | typeof(ProcessWindowPresenter), 23 | new PropertyMetadata(null, (d, e) => ((ProcessWindowPresenter)d).OnTargetProcessChanged((IProcessInfoStore?)e.NewValue))); 24 | 25 | private void OnTargetProcessChanged(IProcessInfoStore? newValue) 26 | { 27 | if (newValue is not null) 28 | { 29 | this.HwndHost = new ProcessWindowHost(newValue); 30 | } 31 | } 32 | 33 | private class ProcessWindowHost(IProcessInfoStore process) : HwndHost 34 | { 35 | private readonly IProcessInfoStore process = process; 36 | private IntPtr beforeStyle; 37 | 38 | protected override HandleRef BuildWindowCore(HandleRef hwndParent) 39 | { 40 | var childStyle = (IntPtr)(User32.WindowStyles.WS_CHILD | 41 | // Child window should be have a thin-line border 42 | User32.WindowStyles.WS_BORDER | 43 | // the parent cannot draw over the child's area. this is needed to avoid refresh issues 44 | User32.WindowStyles.WS_CLIPCHILDREN | 45 | User32.WindowStyles.WS_VISIBLE | 46 | User32.WindowStyles.WS_MAXIMIZE); 47 | this.beforeStyle = User32.GetWindowLongPtr_IntPtr(this.process.MainWindowHandle, User32.WindowLongIndexFlags.GWL_STYLE); 48 | User32.SetWindowLongPtr(this.process.MainWindowHandle, User32.WindowLongIndexFlags.GWL_STYLE, childStyle); 49 | User32.SetParent(this.process.MainWindowHandle, hwndParent.Handle); 50 | return new HandleRef(this, this.process.MainWindowHandle); 51 | } 52 | 53 | protected override void DestroyWindowCore(HandleRef hwnd) 54 | { 55 | User32.SetParent(this.process.MainWindowHandle, IntPtr.Zero); 56 | User32.SetWindowLongPtr(this.process.MainWindowHandle, User32.WindowLongIndexFlags.GWL_STYLE, this.beforeStyle); 57 | User32.DestroyWindow(hwnd.Handle); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /WindowTranslator/Controls/ShortcutBox.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Input; 3 | using WindowTranslator.Extensions; 4 | using Wpf.Ui.Controls; 5 | 6 | namespace WindowTranslator.Controls; 7 | 8 | /// 9 | /// ショートカット入力を受け付けるテキストボックス 10 | /// 11 | public class ShortcutBox : TextBox 12 | { 13 | static ShortcutBox() 14 | { 15 | DefaultStyleKeyProperty.OverrideMetadata(typeof(ShortcutBox), new FrameworkPropertyMetadata(typeof(ShortcutBox))); 16 | ClearButtonEnabledProperty.OverrideMetadata(typeof(ShortcutBox), new FrameworkPropertyMetadata(true)); 17 | ShowClearButtonProperty.OverrideMetadata(typeof(ShortcutBox), new FrameworkPropertyMetadata(true)); 18 | ContextMenuProperty.OverrideMetadata(typeof(ShortcutBox), new FrameworkPropertyMetadata(null)); 19 | } 20 | 21 | protected override void OnPreviewKeyDown(KeyEventArgs e) 22 | { 23 | SetCurrentValue(TextProperty, (e.KeyboardDevice.Modifiers, e.Key).ToShortcutString()); 24 | e.Handled = true; 25 | base.OnPreviewKeyDown(e); 26 | } 27 | 28 | protected override void OnClearButtonClick() 29 | { 30 | SetCurrentValue(TextProperty, (ModifierKeys.Control | ModifierKeys.Alt, Key.O).ToShortcutString()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /WindowTranslator/Data/BoolToDataTemplateConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows.Data; 3 | using System.Windows; 4 | 5 | namespace WindowTranslator.Data; 6 | 7 | [ValueConversion(typeof(bool), typeof(DataTemplate))] 8 | public class BoolToDataTemplateConverter : IValueConverter 9 | { 10 | public DataTemplate? TrueContent { get; set; } 11 | public DataTemplate? FalseContent { get; set; } 12 | 13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | if (value is bool boolValue) 16 | { 17 | return (boolValue ? TrueContent : FalseContent) ?? DependencyProperty.UnsetValue; 18 | } 19 | return DependencyProperty.UnsetValue; 20 | } 21 | 22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException(); 23 | } -------------------------------------------------------------------------------- /WindowTranslator/Data/BoolToDoubleConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows.Data; 3 | 4 | namespace WindowTranslator.Data; 5 | 6 | [ValueConversion(typeof(bool), typeof(double))] 7 | public class BoolToDoubleConverter : IValueConverter 8 | { 9 | public double TrueValue { get; set; } 10 | public double FalseValue { get; set; } 11 | 12 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 13 | { 14 | if (value is bool boolValue) 15 | { 16 | return boolValue ? TrueValue : FalseValue; 17 | } 18 | 19 | return FalseValue; 20 | } 21 | 22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 23 | => throw new NotSupportedException(); 24 | } -------------------------------------------------------------------------------- /WindowTranslator/Data/DrawingColorToBrushConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows.Data; 3 | using Color = System.Drawing.Color; 4 | using System.Windows; 5 | using System.Windows.Media; 6 | 7 | namespace WindowTranslator.Data; 8 | 9 | [ValueConversion(typeof(Color), typeof(Brush))] 10 | public sealed class DrawingColorToBrushConverter : IValueConverter 11 | { 12 | /// Gets the default instance 13 | public static DrawingColorToBrushConverter Default { get; } = new DrawingColorToBrushConverter(); 14 | 15 | private readonly Dictionary cache = new(); 16 | 17 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 18 | { 19 | if (value is not Color color) 20 | { 21 | return DependencyProperty.UnsetValue; 22 | } 23 | else if (this.cache.TryGetValue(color, out var brush)) 24 | { 25 | return brush; 26 | } 27 | else 28 | { 29 | brush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(color.A, color.R, color.G, color.B)); 30 | cache.Add(color, brush); 31 | return brush; 32 | } 33 | } 34 | 35 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 36 | => throw new NotImplementedException(); 37 | } 38 | -------------------------------------------------------------------------------- /WindowTranslator/Data/SizeToCornerRadiusConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows; 3 | using System.Windows.Data; 4 | 5 | namespace WindowTranslator.Data; 6 | 7 | [ValueConversion(typeof(FrameworkElement), typeof(double))] 8 | public sealed class SizeToCornerRadiusConverter : IValueConverter 9 | { 10 | /// デフォルトインスタンスを取得 11 | public static SizeToCornerRadiusConverter Default { get; } = new SizeToCornerRadiusConverter(); 12 | 13 | /// 角丸の最大値 14 | public double MaxValue { get; set; } = 10; 15 | 16 | /// 角丸の最小値 17 | public double MinValue { get; set; } = 2; 18 | 19 | /// 比率感度を調整するためのスケール係数 20 | public double ScaleFactor { get; set; } = 0.15; 21 | 22 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 23 | { 24 | if (value is not FrameworkElement element || double.IsNaN(element.MinWidth) || double.IsNaN(element.MinHeight)) 25 | { 26 | return DependencyProperty.UnsetValue; 27 | } 28 | 29 | // 計算のために小さい方の寸法を使用 30 | // TextRectの情報はMinWidthとMinHeightに格納されている 31 | double smallerDimension = Math.Min(element.MinWidth, element.MinHeight); 32 | 33 | // スケール係数を使用して小さい方の寸法に基づいて半径を計算 34 | double radius = smallerDimension * ScaleFactor; 35 | 36 | // 制約を適用 37 | radius = Math.Max(MinValue, Math.Min(MaxValue, radius)); 38 | 39 | return radius; 40 | } 41 | 42 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 43 | => throw new NotImplementedException(); 44 | } 45 | -------------------------------------------------------------------------------- /WindowTranslator/Data/TextOverlayVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows; 3 | using System.Windows.Data; 4 | 5 | namespace WindowTranslator.Data; 6 | 7 | public sealed class TextOverlayVisibilityConverter : IMultiValueConverter 8 | { 9 | /// Gets the default instance 10 | public static readonly TextOverlayVisibilityConverter Default = new TextOverlayVisibilityConverter(); 11 | 12 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 13 | { 14 | if (values is not [TextRect rect, Point pos, double scale]) 15 | { 16 | return Visibility.Visible; 17 | } 18 | var r = new Rect(rect.X * scale, rect.Y * scale, rect.Width * scale, rect.Height * scale); 19 | return r.Contains(pos) ? Visibility.Collapsed : Visibility.Visible; 20 | } 21 | 22 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 23 | => throw new NotSupportedException(); 24 | } 25 | -------------------------------------------------------------------------------- /WindowTranslator/Data/TextOverlayWidthConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Windows; 3 | using System.Windows.Data; 4 | 5 | namespace WindowTranslator.Data; 6 | 7 | [ValueConversion(typeof(TextRect), typeof(double))] 8 | public sealed class TextOverlayWidthConverter : IValueConverter 9 | { 10 | /// Gets the default instance 11 | public static TextOverlayWidthConverter Default { get; } = new TextOverlayWidthConverter(); 12 | 13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | if (value is not TextRect rect) 16 | { 17 | return DependencyProperty.UnsetValue; 18 | } 19 | // 複数行の時は改行して収まるようにする 20 | if (rect.MultiLine) 21 | { 22 | return rect.Width * 1.01; 23 | } 24 | // 1行の時ははみ出ても良いことにする 25 | return double.NaN; 26 | } 27 | 28 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 29 | => throw new NotImplementedException(); 30 | } 31 | -------------------------------------------------------------------------------- /WindowTranslator/Extensions/LoggerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Microsoft.Extensions.Logging; 3 | using WindowTranslator.ComponentModel; 4 | 5 | namespace WindowTranslator.Extensions; 6 | 7 | public static class LoggerExtensions 8 | { 9 | public static IDisposable LogDebugTime(this ILogger logger, string message) 10 | { 11 | var sw = Stopwatch.StartNew(); 12 | return new DisposeAction(() => logger.LogDebug($"{message}: {sw.Elapsed}")); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /WindowTranslator/IVirtualDesktopManager.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace WindowTranslator; 4 | 5 | [InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("a5cd92ff-29be-454c-8d04-d82879fb3f1b")] 6 | public interface IVirtualDesktopManager 7 | { 8 | bool IsWindowOnCurrentVirtualDesktop(IntPtr topLevelWindow); 9 | Guid GetWindowDesktopId(IntPtr hWnd); 10 | int MoveWindowToDesktop(IntPtr hWnd, ref Guid desktop); 11 | } 12 | -------------------------------------------------------------------------------- /WindowTranslator/Modules/Cache/CacheParam.cs: -------------------------------------------------------------------------------- 1 | using PropertyTools.DataAnnotations; 2 | 3 | namespace WindowTranslator.Modules.Cache; 4 | /// 5 | /// キャッシュモジュールのパラメータを表します。 6 | /// 7 | public class CacheParam : IPluginParam 8 | { 9 | /// 10 | /// 同じテキストと判定するための閾値 11 | /// 12 | [Slidable(0, 1, .01, .1, true, .01)] 13 | [FormatString("P2")] 14 | public double FuzzyMatchThreshold { get; set; } = 0.9; 15 | } -------------------------------------------------------------------------------- /WindowTranslator/Modules/Cache/InMemoryCache.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Options; 4 | using Quickenshtein; 5 | 6 | namespace WindowTranslator.Modules.Cache; 7 | 8 | public class InMemoryCache(ILogger logger, IOptionsSnapshot options) : ICacheModule 9 | { 10 | private readonly ConcurrentDictionary cache = new(); 11 | private readonly ConcurrentDictionary nearCache = new(); 12 | private readonly ILogger logger = logger; 13 | private readonly CacheParam options = options.Value; 14 | 15 | public bool Contains(string src) 16 | { 17 | if (this.cache.ContainsKey(src) || this.nearCache.ContainsKey(src)) 18 | { 19 | return true; 20 | } 21 | if (this.cache.IsEmpty || Math.Abs(this.options.FuzzyMatchThreshold - 1.0) < double.Epsilon) 22 | { 23 | return false; 24 | } 25 | var t = DateTime.UtcNow; 26 | var (cacheSrc, dst, distance) = this.cache 27 | .Select(p => (src: p.Key, dst: p.Value, distance: Levenshtein.GetDistance(src, p.Key, CalculationOptions.DefaultWithThreading))) 28 | .MinBy(p => p.distance); 29 | // 一致率の計算 30 | var p = 1 - ((float)distance / Math.Max(src.Length, cacheSrc.Length)); 31 | this.logger.LogDebug($"LevenshteinDistance: {src} -> {cacheSrc} ({p:p2}%) [{DateTime.UtcNow - t}]"); 32 | if (p < this.options.FuzzyMatchThreshold) 33 | { 34 | return false; 35 | } 36 | this.nearCache.TryAdd(src, dst); 37 | return true; 38 | } 39 | 40 | public void AddRange(IEnumerable<(string src, string dst)> pairs) 41 | { 42 | foreach (var (src, dst) in pairs) 43 | { 44 | this.cache.AddOrUpdate(src, dst, (_, _) => dst); 45 | } 46 | } 47 | public string Get(string src) 48 | => this.cache.TryGetValue(src, out var dst) ? dst 49 | : this.nearCache.TryGetValue(src, out dst) ? dst 50 | : string.Empty; 51 | } -------------------------------------------------------------------------------- /WindowTranslator/Modules/Cache/NoCache.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator.Modules.Cache; 2 | 3 | /// 4 | /// キャッシュを使用しないキャッシュモジュールです。 5 | /// 6 | public class NoCache : ICacheModule 7 | { 8 | /// 9 | public bool Contains(string src) => false; 10 | 11 | /// 12 | public void AddRange(IEnumerable<(string src, string dst)> pairs) { } 13 | 14 | /// 15 | public string Get(string src) => string.Empty; 16 | } -------------------------------------------------------------------------------- /WindowTranslator/Modules/Capture/ICaptureModule.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Threading; 2 | using Windows.Graphics.Capture; 3 | 4 | namespace WindowTranslator.Modules.Capture; 5 | public interface ICaptureModule 6 | { 7 | event AsyncEventHandler? Captured; 8 | void StartCapture(IntPtr targetWindow); 9 | void StopCapture(); 10 | } 11 | 12 | public record CapturedEventArgs(Direct3D11CaptureFrame Frame); 13 | -------------------------------------------------------------------------------- /WindowTranslator/Modules/Main/CaptureMainWindow.xaml: -------------------------------------------------------------------------------- 1 |  18 | 19 | 20 | 21 | 22 | 27 | 28 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /WindowTranslator/Modules/Main/CaptureMainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Messaging; 2 | using System.Windows; 3 | using System.Windows.Threading; 4 | using WindowTranslator.Stores; 5 | using static PInvoke.User32; 6 | 7 | namespace WindowTranslator.Modules.Main; 8 | 9 | /// 10 | /// Interaction logic for MainWindow.xaml 11 | /// 12 | public partial class CaptureMainWindow 13 | { 14 | private readonly IProcessInfoStore processInfo; 15 | private readonly DispatcherTimer timer = new(); 16 | 17 | public CaptureMainWindow(IProcessInfoStore processInfo) 18 | { 19 | InitializeComponent(); 20 | this.processInfo = processInfo; 21 | this.timer.Interval = TimeSpan.FromMilliseconds(10); 22 | this.timer.Tick += (s, e) => CheckTargetWindow(); 23 | } 24 | 25 | private void Window_Loaded(object sender, RoutedEventArgs e) 26 | { 27 | this.timer.Start(); 28 | StrongReferenceMessenger.Default.Register(this, CloseIfViewModel); 29 | } 30 | 31 | private void CheckTargetWindow() 32 | { 33 | var windowInfo = WINDOWINFO.Create(); 34 | if (!GetWindowInfo(this.processInfo.MainWindowHandle, ref windowInfo)) 35 | { 36 | this.Close(); 37 | return; 38 | } 39 | } 40 | 41 | protected override void OnClosed(EventArgs e) 42 | { 43 | base.OnClosed(e); 44 | this.timer.Stop(); 45 | StrongReferenceMessenger.Default.Unregister(this); 46 | } 47 | 48 | private static void CloseIfViewModel(CaptureMainWindow w, CloseMessage m) 49 | { 50 | if (w.DataContext == m.ViewModel) 51 | { 52 | w.Close(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /WindowTranslator/Modules/Main/CloseMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace WindowTranslator.Modules.Main; 8 | internal record CloseMessage(object ViewModel); 9 | -------------------------------------------------------------------------------- /WindowTranslator/Modules/Main/MainWindowModule.cs: -------------------------------------------------------------------------------- 1 | using Kamishibai; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.Options; 4 | using Microsoft.VisualStudio.Threading; 5 | using System.Collections.ObjectModel; 6 | using WindowTranslator.Stores; 7 | 8 | namespace WindowTranslator.Modules.Main; 9 | public sealed class MainWindowModule(App app, IServiceProvider provider) : IMainWindowModule, IDisposable 10 | { 11 | private readonly App app = app; 12 | private readonly IServiceProvider provider = provider; 13 | private readonly AsyncSemaphore asyncLock = new(1); 14 | 15 | public ObservableCollection OpenedWindows { get; } = new(); 16 | 17 | public Task OpenTargetAsync(IntPtr targetWindowHandle, string name) 18 | => this.app.Dispatcher.Invoke(() => OpenTargetWindowCoreAsync(targetWindowHandle, name)); 19 | 20 | private async Task OpenTargetWindowCoreAsync(IntPtr targetWindowHandle, string name) 21 | { 22 | using var l = await this.asyncLock.EnterAsync(); 23 | var scope = provider.CreateScope(); 24 | var options = scope.ServiceProvider.GetRequiredService>(); 25 | var presentationService = scope.ServiceProvider.GetRequiredService(); 26 | var processInfo = scope.ServiceProvider.GetRequiredService(); 27 | processInfo.SetTargetProcess(targetWindowHandle, name); 28 | 29 | if (!options.Value.Targets.ContainsKey(name)) 30 | { 31 | await presentationService.OpenAllSettingsDialogAsync(name); 32 | } 33 | 34 | var window = options.Value.Common.ViewMode switch 35 | { 36 | ViewMode.Capture => await presentationService.OpenCaptureMainWindowAsync(), 37 | ViewMode.Overlay => await presentationService.OpenOverlayMainWindowAsync(), 38 | _ => throw new NotSupportedException(), 39 | }; 40 | var info = new WindowInfo(name, targetWindowHandle, window); 41 | window.Closed += (_, _) => 42 | { 43 | scope.Dispose(); 44 | this.OpenedWindows.Remove(info); 45 | }; 46 | this.OpenedWindows.Add(info); 47 | } 48 | 49 | public void Dispose() 50 | => this.asyncLock.Dispose(); 51 | } 52 | 53 | public interface IMainWindowModule 54 | { 55 | ObservableCollection OpenedWindows { get; } 56 | 57 | Task OpenTargetAsync(IntPtr targetWindowHandle, string name); 58 | } 59 | 60 | public record WindowInfo(string Name, IntPtr Target, IWindow Window); 61 | -------------------------------------------------------------------------------- /WindowTranslator/Modules/Main/OverlayMainWindow.xaml: -------------------------------------------------------------------------------- 1 |  21 | 22 | 23 | 24 | 31 | 32 | 33 | 34 | 42 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /WindowTranslator/Modules/Ocr/WindowsMediaOcrUtility.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO; 3 | 4 | namespace WindowTranslator.Modules.Ocr; 5 | 6 | public static class WindowsMediaOcrUtility 7 | { 8 | public static string ConvertLanguage(string lang) => lang switch 9 | { 10 | "zh-Hant" => "zh-TW", 11 | "zh-Hans" => "zh-CN", 12 | _ => lang, 13 | }; 14 | 15 | public static bool IsInstalledLanguage(string lang) 16 | => Directory.Exists(@$"C:\Windows\OCR\{ConvertLanguage(lang)}"); 17 | 18 | public static async Task InstallLanguageAsync(string language, CancellationToken cancellationToken = default) 19 | { 20 | var info = new ProcessStartInfo("powershell.exe", $"-Command \"Install-Language -Language {ConvertLanguage(language)} -ExcludeFeatures -AsJob\"") 21 | { 22 | Verb = "runas", // 管理者権限で実行 23 | UseShellExecute = true, 24 | CreateNoWindow = true, 25 | }; 26 | var p = Process.Start(info); 27 | p!.WaitForExit(); 28 | while (!IsInstalledLanguage(language)) 29 | { 30 | cancellationToken.ThrowIfCancellationRequested(); 31 | await Task.Delay(1000, cancellationToken).ConfigureAwait(false); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /WindowTranslator/Modules/OverlayColor/ColorThiefModule.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System.ComponentModel; 3 | using Windows.Graphics.Imaging; 4 | using WindowTranslator.ComponentModel; 5 | using ColorConverter = ColorHelper.ColorConverter; 6 | using StudioFreesia.ColorThief; 7 | using System.Drawing; 8 | 9 | namespace WindowTranslator.Modules.OverlayColor; 10 | 11 | [DefaultModule] 12 | [DisplayName("近似カラー")] 13 | public class ColorThiefModule(ILogger logger) : IColorModule 14 | { 15 | private readonly ILogger logger = logger; 16 | 17 | public ValueTask> ConvertColorAsync(SoftwareBitmap bitmap, IEnumerable texts) 18 | { 19 | var results = new List(texts.Count()); 20 | var palette = TimeSpan.Zero; 21 | // テキスト数が少ない時に並列化すると逆に遅くなり、総合すると並列化しないほうがよさそう 22 | foreach (var text in texts) 23 | { 24 | var now = DateTime.UtcNow; 25 | var colors = ColorThief.GetPalette(bitmap, new Rectangle((int)text.X, (int)text.Y, (int)text.Width, (int)text.Height), ignoreWhite: false) 26 | .OrderByDescending(c => c.Population) 27 | .Select(c => c.Color) 28 | .ToArray(); 29 | palette += DateTime.UtcNow - now; 30 | var back = colors[0]; 31 | 32 | // 文字影が文字色より大きくなることがあるので、背景色とのBrightness距離が大きい方を文字色とする 33 | var backB = ColorConverter.RgbToHsv(new(back.R, back.G, back.B)).V; 34 | var front = GetDistance(backB, colors[1]) > GetDistance(backB, colors[2]) ? colors[1] : colors[2]; 35 | results.Add(text with { Foreground = Color.FromArgb(front.R, front.G, front.B), Background = Color.FromArgb(0xF0, back.R, back.G, back.B) }); 36 | } 37 | 38 | this.logger.LogDebug($"Palette:{palette}"); 39 | return new(results); 40 | } 41 | 42 | private static double GetDistance(double h1, Color h2) 43 | => Math.Abs(h1 - ColorConverter.RgbToHsv(new(h2.R, h2.G, h2.B)).V); 44 | } 45 | -------------------------------------------------------------------------------- /WindowTranslator/Modules/OverlayColor/IColorModule.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using Windows.Graphics.Imaging; 3 | 4 | namespace WindowTranslator.Modules.OverlayColor; 5 | public interface IColorModule 6 | { 7 | ValueTask> ConvertColorAsync(SoftwareBitmap bitmap, IEnumerable texts); 8 | } 9 | 10 | public class EmptyColorModule : IColorModule 11 | { 12 | public ValueTask> ConvertColorAsync(SoftwareBitmap bitmap, IEnumerable texts) 13 | => new(texts); 14 | } 15 | 16 | public class TransparentColorModule : IColorModule 17 | { 18 | public ValueTask> ConvertColorAsync(SoftwareBitmap bitmap, IEnumerable texts) 19 | => new(texts.Select(t => t with { Background = Color.Transparent, Foreground = Color.Transparent })); 20 | } -------------------------------------------------------------------------------- /WindowTranslator/Modules/Settings/AllSettingsDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Data; 2 | using System.Windows; 3 | using Wpf.Ui.Controls; 4 | using System.Globalization; 5 | using Wpf.Ui; 6 | using Wpf.Ui.Appearance; 7 | 8 | namespace WindowTranslator.Modules.Settings; 9 | 10 | /// 11 | /// AllSettingsDialog.xaml の相互作用ロジック 12 | /// 13 | public partial class AllSettingsDialog : FluentWindow 14 | { 15 | public AllSettingsDialog(IContentDialogService contentDialogService) 16 | { 17 | SystemThemeWatcher.Watch(this); 18 | InitializeComponent(); 19 | contentDialogService.SetDialogHost(this.RootContentDialog); 20 | } 21 | } 22 | 23 | [ValueConversion(typeof(bool), typeof(Visibility))] 24 | public sealed class FalseToVisibilityConverter : IValueConverter 25 | { 26 | public static FalseToVisibilityConverter Default { get; } = new FalseToVisibilityConverter(); 27 | 28 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 29 | => value is bool b && b ? Visibility.Collapsed : Visibility.Visible; 30 | 31 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 32 | => value is Visibility v && v != Visibility.Visible; 33 | } 34 | 35 | [ValueConversion(typeof(string), typeof(string))] 36 | public sealed class TargetNameConverter : IValueConverter 37 | { 38 | public static TargetNameConverter Default { get; } = new TargetNameConverter(); 39 | 40 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 41 | => value is string { Length: > 0 } name ? name : "Default"; 42 | 43 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 44 | => throw new NotSupportedException(); 45 | } -------------------------------------------------------------------------------- /WindowTranslator/Modules/Settings/PluginParamConverter.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace WindowTranslator.Modules.Settings; 5 | internal class PluginParamConverter : JsonConverter 6 | { 7 | public override IPluginParam? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 8 | => throw new NotImplementedException(); 9 | 10 | public override void Write(Utf8JsonWriter writer, IPluginParam value, JsonSerializerOptions options) 11 | => JsonSerializer.Serialize(writer, value, value.GetType(), options); 12 | } 13 | -------------------------------------------------------------------------------- /WindowTranslator/Modules/Startup/StartupDialog.xaml: -------------------------------------------------------------------------------- 1 |  26 | 27 | 32 | 33 | 34 | 39 | 40 | 44 | 45 | 46 | 47 | 52 | 53 | 54 | 55 | 56 | 60 | 64 | 65 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /WindowTranslator/Modules/Startup/StartupDialog.xaml.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using System.Windows; 3 | using Wpf.Ui.Appearance; 4 | using Wpf.Ui.Controls; 5 | 6 | namespace WindowTranslator.Modules.Startup; 7 | /// 8 | /// StartupDialog.xaml の相互作用ロジック 9 | /// 10 | public partial class StartupDialog : FluentWindow 11 | { 12 | private readonly LaunchMode mode; 13 | 14 | public StartupDialog(IConfiguration configuration) 15 | { 16 | SystemThemeWatcher.Watch(this); 17 | InitializeComponent(); 18 | this.mode = configuration.GetValue(nameof(LaunchMode), LaunchMode.Direct); 19 | } 20 | 21 | private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) 22 | { 23 | e.Cancel = true; 24 | Hide(); 25 | } 26 | 27 | private void Window_Loaded(object sender, RoutedEventArgs e) 28 | { 29 | if (this.mode == LaunchMode.Startup) 30 | { 31 | this.SetCurrentValue(VisibilityProperty, Visibility.Hidden); 32 | } 33 | } 34 | } 35 | 36 | public enum LaunchMode 37 | { 38 | Direct, 39 | Startup, 40 | } -------------------------------------------------------------------------------- /WindowTranslator/Modules/Translate/NoTranslateModule.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator.Modules.Translate; 2 | 3 | public class NoTranslateModule : ITranslateModule 4 | { 5 | public ValueTask TranslateAsync(TextInfo[] srcTexts) 6 | => ValueTask.FromResult(srcTexts.Select(s => s.Text).ToArray()); 7 | } 8 | -------------------------------------------------------------------------------- /WindowTranslator/NativeMethods.txt: -------------------------------------------------------------------------------- 1 | RegisterHotKey 2 | UnregisterHotKey 3 | WM_HOTKEY 4 | -------------------------------------------------------------------------------- /WindowTranslator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "WindowTranslator": { 4 | "commandName": "Project", 5 | "workingDirectory": "$(ProjectDir)", 6 | "environmentVariables": { 7 | "SELECTEDPLUGINS__ITRANSLATEMODULE": "DeepLTranslator", 8 | "LOGGING__LOGLEVEL__WindowTranslator.Plugin.LLMPlugin": "Debug" 9 | } 10 | }, 11 | "StartupMode": { 12 | "commandName": "Project", 13 | "commandLineArgs": "--LaunchMode Startup", 14 | "environmentVariables": { 15 | "LOGGING__LOGLEVEL__DEFAULT": "Debug" 16 | } 17 | }, 18 | "RectDebug": { 19 | "commandName": "Project", 20 | "environmentVariables": { 21 | "SELECTEDPLUGINS__ICOLORMODULE": "TransparentColorModule", 22 | "SELECTEDPLUGINS__ICACHEMODULE": "InMemoryCache" 23 | } 24 | }, 25 | "Empty": { 26 | "commandName": "Project", 27 | "environmentVariables": { 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /WindowTranslator/Stores/AutoTargetStore.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Options; 2 | using System.IO; 3 | using System.Text.Json; 4 | 5 | namespace WindowTranslator.Stores; 6 | sealed class AutoTargetStore : IAutoTargetStore, IDisposable 7 | { 8 | private readonly static string targetsPath = Path.Combine(PathUtility.UserDir, "autoTargets.json"); 9 | private readonly IOptionsMonitor settings; 10 | private readonly List activeWindows = []; 11 | 12 | public ISet AutoTargets { get; } 13 | 14 | public AutoTargetStore(IOptionsMonitor userSettings) 15 | { 16 | this.settings = userSettings; 17 | if (File.Exists(targetsPath)) 18 | { 19 | using var fs = File.OpenRead(targetsPath); 20 | this.AutoTargets = JsonSerializer.Deserialize>(fs) ?? []; 21 | } 22 | else 23 | { 24 | this.AutoTargets = new HashSet(); 25 | } 26 | } 27 | 28 | public void Dispose() 29 | { 30 | var settings = this.settings.CurrentValue; 31 | if (settings.IsEnableAutoTarget) 32 | { 33 | Save(); 34 | } 35 | } 36 | 37 | public void Save() 38 | { 39 | Directory.CreateDirectory(PathUtility.UserDir); 40 | using var fs = File.Create(targetsPath); 41 | JsonSerializer.Serialize(fs, this.AutoTargets.Distinct()); 42 | } 43 | 44 | public bool IsAutoTarget(IntPtr windowHandle, string name) 45 | { 46 | if (this.activeWindows.Contains(windowHandle)) 47 | { 48 | return false; 49 | } 50 | var settings = this.settings.CurrentValue; 51 | return settings.IsEnableAutoTarget && this.AutoTargets.Contains(name); 52 | } 53 | 54 | public void AddTarget(IntPtr windowHandle, string name) 55 | { 56 | this.AutoTargets.Add(name); 57 | this.activeWindows.Add(windowHandle); 58 | } 59 | 60 | public void RemoveTarget(IntPtr windowHandle) 61 | => this.activeWindows.Remove(windowHandle); 62 | } 63 | 64 | public interface IAutoTargetStore 65 | { 66 | ISet AutoTargets { get; } 67 | void AddTarget(IntPtr windowHandle, string name); 68 | bool IsAutoTarget(IntPtr windowHandle, string name); 69 | void RemoveTarget(IntPtr windowHandle); 70 | void Save(); 71 | } -------------------------------------------------------------------------------- /WindowTranslator/Stores/ProcessInfoStore.cs: -------------------------------------------------------------------------------- 1 | namespace WindowTranslator.Stores; 2 | 3 | public sealed class ProcessInfoStore(IAutoTargetStore targetStore) : IProcessInfoStoreInternal, IDisposable 4 | { 5 | private readonly IAutoTargetStore targetStore = targetStore; 6 | 7 | public IntPtr MainWindowHandle { get; private set; } 8 | public string Name { get; private set; } = string.Empty; 9 | 10 | public void SetTargetProcess(IntPtr mainWindowHandle, string name) 11 | { 12 | this.MainWindowHandle = mainWindowHandle; 13 | this.Name = name; 14 | this.targetStore.AddTarget(mainWindowHandle, name); 15 | } 16 | 17 | public void Dispose() 18 | => this.targetStore.RemoveTarget(this.MainWindowHandle); 19 | } 20 | 21 | interface IProcessInfoStoreInternal : IProcessInfoStore 22 | { 23 | void SetTargetProcess(IntPtr mainWindowHandle, string name); 24 | } 25 | -------------------------------------------------------------------------------- /WindowTranslator/WindowMonitor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Hosting; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.VisualStudio.Threading; 4 | using PInvoke; 5 | using System.Diagnostics; 6 | using WindowTranslator.Modules.Main; 7 | using WindowTranslator.Stores; 8 | 9 | namespace WindowTranslator; 10 | public class WindowMonitor(IMainWindowModule mainWindowModule, IAutoTargetStore autoTargetStore, IVirtualDesktopManager desktopManager, ILogger logger) : BackgroundService 11 | { 12 | private readonly IMainWindowModule mainWindowModule = mainWindowModule; 13 | private readonly IAutoTargetStore autoTargetStore = autoTargetStore; 14 | private readonly IVirtualDesktopManager desktopManager = desktopManager; 15 | private readonly ILogger logger = logger; 16 | private readonly HashSet checkedWindows = []; 17 | 18 | protected override async Task ExecuteAsync(CancellationToken stoppingToken) 19 | { 20 | while (!stoppingToken.IsCancellationRequested) 21 | { 22 | CheckProcesses(); 23 | stoppingToken.ThrowIfCancellationRequested(); 24 | await Task.Delay(5000, stoppingToken); 25 | stoppingToken.ThrowIfCancellationRequested(); 26 | } 27 | } 28 | 29 | private void CheckProcesses() 30 | { 31 | this.logger.LogDebug("プロセスチェック開始"); 32 | var windows = new HashSet(); 33 | User32.EnumWindows((hWnd, lParam) => 34 | { 35 | if (!User32.IsWindowVisible(hWnd) || !this.desktopManager.IsWindowOnCurrentVirtualDesktop(hWnd) || this.checkedWindows.Contains(hWnd)) 36 | { 37 | return true; 38 | } 39 | 40 | var windowTitle = User32.GetWindowText(hWnd); 41 | if (User32.GetWindowThreadProcessId(hWnd, out var processId) == 0) 42 | { 43 | return true; 44 | } 45 | Process p; 46 | try 47 | { 48 | p = Process.GetProcessById(processId); 49 | } 50 | catch (ArgumentException) 51 | { 52 | return true; 53 | } 54 | if (this.autoTargetStore.IsAutoTarget(hWnd, p.ProcessName)) 55 | { 56 | this.logger.LogInformation($"`{p.ProcessName}`の翻訳を開始"); 57 | this.checkedWindows.Add(hWnd); 58 | this.mainWindowModule.OpenTargetAsync(hWnd, p.ProcessName).Forget(); 59 | } 60 | else 61 | { 62 | windows.Add(hWnd); 63 | } 64 | return true; 65 | }, IntPtr.Zero); 66 | this.checkedWindows.ExceptWith(windows); 67 | this.logger.LogDebug("プロセスチェック終了"); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /WindowTranslator/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ViewMode": 1, 3 | "Sentry": { 4 | "Dsn": "https://5301a4e1d7cd4c45877493fd27303166@o351180.ingest.sentry.io/6471678" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /**/Properties/Resources.resx 3 | translation: /**/Properties/Resources.%two_letters_code%.resx 4 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | # Hello! This is where you manage which Jekyll version is used to run. 3 | # When you want to use a different version, change it below, save the 4 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: 5 | # 6 | # bundle exec jekyll serve 7 | # 8 | # This will help ensure the proper Jekyll version is running. 9 | # Happy Jekylling! 10 | gem "jekyll" 11 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and 12 | # uncomment the line below. To upgrade, run `bundle update github-pages`. 13 | gem "github-pages", group: :jekyll_plugins 14 | # If you have any plugins, put them here! 15 | group :jekyll_plugins do 16 | end 17 | 18 | # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem 19 | # and associated library. 20 | platforms :mingw, :x64_mingw, :mswin, :jruby do 21 | gem "tzinfo", ">= 1", "< 3" 22 | gem "tzinfo-data" 23 | end 24 | 25 | # Performance-booster for watching directories on Windows 26 | gem "wdm", "~> 0.1", :platforms => [:mingw, :x64_mingw, :mswin] 27 | 28 | # Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem 29 | # do not have a Java counterpart. 30 | gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby] 31 | -------------------------------------------------------------------------------- /docs/PrivacyPolicy.de.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Datenschutzerklärung 3 | description: Datenschutzerklärung für WindowTranslator 4 | --- 5 | 6 | Diese Datenschutzerklärung regelt die Nutzung von "WindowTranslator" (im Folgenden "die Anwendung" genannt). 7 | Die Anwendung ist Open Source und auf GitHub ( https://github.com/Freeesia/WindowTranslator ) verfügbar. 8 | Die Anwendung selbst sammelt keine personenbezogenen Daten oder Nutzungsdaten der Benutzer und erhebt auch keine Fehlerprotokolle oder Absturzberichte. 9 | 10 | ## Erhebung personenbezogener Daten 11 | 12 | ### Von der Anwendung erhobene Informationen 13 | Die Anwendung sammelt keine personenbezogenen Daten wie Benutzereingaben, Interaktionshistorien oder Geräteinformationen. 14 | 15 | ### Fehlerprotokolle und Absturzberichte 16 | Die Anwendung sammelt nicht automatisch Fehlerprotokolle oder Absturzberichte. 17 | 18 | ## Umgang mit Google-Nutzerdaten 19 | Beim Zugriff auf Google Apps Script-Dienste erhält die Anwendung nur die minimal erforderlichen Authentifizierungs-Tokens. 20 | Weitere persönliche Daten der Benutzer werden nicht verwendet. 21 | 22 | ### Verwendung der erhobenen Daten 23 | Die erhaltenen Authentifizierungsinformationen werden ausschließlich zur Ausführung von Google Apps Script innerhalb der Anwendung verwendet. 24 | Diese Authentifizierung dient ausschließlich dem Zweck, die Nutzung von Google-Diensten zu ermöglichen. 25 | 26 | ### Weitergabe oder Übertragung von Daten 27 | Der Anbieter der Anwendung sammelt, speichert, teilt oder überträgt keine Authentifizierungsinformationen oder sonstige Google-Nutzerdaten. 28 | Alle Authentifizierungsdaten werden auf dem PC des Benutzers gespeichert und ausschließlich für den Zugriff auf Google-Dienste verwendet. 29 | 30 | ### Datensicherheit 31 | Authentifizierungsdaten werden sicher auf dem PC des Benutzers gespeichert, und der Anbieter der Anwendung hat keinerlei Zugriffsrechte darauf. 32 | Zusätzliche Sicherheitsmaßnahmen hängen von der eigenen PC-Sicherheit des Benutzers ab. 33 | 34 | ### Aufbewahrung und Löschung von Daten 35 | Authentifizierungstokens werden ausschließlich auf dem PC des Benutzers gespeichert. 36 | Die Löschung dieser Tokens erfolgt durch den Benutzer, indem er den Ordner `%APPDATA%\StudioFreesia\WindowTranslator\GoogleAppsScriptPlugin` entfernt. 37 | 38 | ## Nutzung von Übersetzungsdiensten und Drittanbieterbibliotheken 39 | Die Anwendung unterstützt mehrere Übersetzungsdienste, darunter Google Translate und DeepL. 40 | Diese Übersetzungsdienste sowie zugehörige Bibliotheken können gemäß ihrer eigenen Datenschutzerklärungen Nutzerdaten oder Nutzungsinformationen erheben. 41 | Benutzer werden dringend gebeten, auch die Datenschutzerklärungen der verwendeten Übersetzungsdienste zu prüfen. 42 | 43 | ## Kontakt 44 | Bei Fragen oder Anmerkungen zu dieser Datenschutzerklärung kontaktieren Sie bitte: 45 | GitHub-Konto: [Freeesia](https://github.com/Freeesia) 46 | 47 | ## Änderungen dieser Datenschutzerklärung 48 | Diese Datenschutzerklärung kann aufgrund von Gesetzesänderungen oder Änderungen der Anwendung ohne Vorankündigung geändert werden. 49 | Etwaige Änderungen werden über die Anwendung oder das GitHub-Repository zeitnah mitgeteilt. 50 | 51 | > [!WARNING] 52 | > Hinweis: Diese Datei wurde maschinell aus der Originalfassung in japanischer Sprache übersetzt, welche als maßgeblich gilt. 53 | -------------------------------------------------------------------------------- /docs/PrivacyPolicy.en.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Privacy Policy 3 | description: WindowTranslator Privacy Policy 4 | --- 5 | 6 | This Privacy Policy governs the use of "WindowTranslator" (hereinafter referred to as "the Application"). 7 | 8 | ## Introduction 9 | The Application is open source and available on GitHub ( https://github.com/Freeesia/WindowTranslator ). 10 | The Application itself does not collect any personal information or usage data from its users, nor does it collect error logs or crash reports. 11 | 12 | ## Collection of Personal Information 13 | 14 | ### Information Collected by the Application 15 | The Application does not directly collect personal data such as user input, interaction history, or device information. 16 | 17 | ### Error Logs and Crash Reports 18 | The Application does not automatically collect any error logs or crash reports. 19 | 20 | ## Handling of Google User Data 21 | When accessing Google Apps Script services, the Application obtains only the minimum required authentication tokens. 22 | No personal information of the user is used beyond this authentication. 23 | 24 | ### Use of Collected Data 25 | The obtained authentication information is used solely to execute Google Apps Script within the Application. 26 | This authentication is strictly limited to the purpose of enabling the use of Google services. 27 | 28 | ### Sharing or Transfer of Data 29 | The Application provider does not collect, store, share, or transfer any authentication information or other Google user data. 30 | All authentication data is stored on the user’s PC and used solely for accessing Google services. 31 | 32 | ### Data Protection 33 | Authentication data is securely stored on the user’s PC, and the Application provider does not have any access rights to this data. 34 | Additional security measures depend on the user’s own PC security management. 35 | 36 | ### Data Retention and Deletion 37 | Authentication tokens are stored only on the user’s PC. 38 | Deletion of these tokens is completed by the user removing the folder located at `%APPDATA%\StudioFreesia\WindowTranslator\GoogleAppsScriptPlugin`. 39 | 40 | ## Use of Translation Engines and Third-Party Libraries 41 | The Application is designed to support multiple translation engines, including Google Translate and DeepL. 42 | These translation engines and associated libraries may collect user data or usage information in accordance with their respective privacy policies. 43 | Users are strongly advised to review the privacy policies of the translation services they use. 44 | 45 | ## Contact Information 46 | For any questions or feedback regarding this Privacy Policy, please contact: 47 | GitHub Account: [Freeesia](https://github.com/Freeesia) 48 | 49 | ## Changes to This Privacy Policy 50 | This Privacy Policy may be amended without prior notice due to changes in law or application functionality. 51 | Any changes will be promptly communicated via the Application or the GitHub repository. 52 | 53 | > [!WARNING] 54 | > Note: This file is machine translated from the original Japanese version, which is the master version. 55 | -------------------------------------------------------------------------------- /docs/PrivacyPolicy.kr.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 개인정보 처리방침 3 | description: WindowTranslator 개인정보 처리방침 4 | --- 5 | 6 | 본 개인정보 처리방침은 “WindowTranslator”(이하 “본 애플리케이션”)의 이용과 관련하여 사용자 개인정보 및 데이터 처리 방식에 대해 규정합니다. 7 | 본 애플리케이션은 오픈 소스로 GitHub(https://github.com/Freeesia/WindowTranslator)에서 공개되며, 8 | 애플리케이션 자체에서는 사용자의 개인정보나 이용 데이터를 수집하지 않으며, 에러 로그나 크래시 리포트도 수집하지 않습니다. 9 | 10 | ## 개인정보 수집에 관하여 11 | 12 | ### 애플리케이션이 수집하는 정보 13 | 본 애플리케이션은 사용자의 입력 정보, 이용 기록, 기기 정보 등과 같은 개인정보를 직접 수집하지 않습니다. 14 | 15 | ### 에러 로그 및 크래시 리포트 16 | 본 애플리케이션은 에러 로그나 크래시 리포트를 자동으로 수집하지 않습니다. 17 | 18 | ## Google 사용자 데이터 처리 방식 19 | 본 애플리케이션은 Google Apps Script 서비스를 이용할 때, 최소한의 인증 토큰만을 획득합니다. 20 | 이 인증을 제외하고는 사용자의 개인정보를 사용하지 않습니다. 21 | 22 | ### 수집된 데이터의 사용 방법 23 | 획득된 인증 정보는 본 애플리케이션 내에서 Google Apps Script를 실행하는 데에만 사용됩니다. 24 | 이 인증은 오직 Google 서비스를 이용하기 위한 목적으로만 제한됩니다. 25 | 26 | ### 데이터의 공유 및 전송 27 | 본 애플리케이션 제공자는 인증 정보 또는 기타 Google 사용자 데이터를 수집, 저장, 공유하거나 제3자에게 전송하지 않습니다. 28 | 모든 인증 정보는 사용자의 PC에 저장되며, Google 서비스 이용을 위해서만 사용됩니다. 29 | 30 | ### 데이터 보호 31 | 인증 정보는 사용자의 PC에 안전하게 저장되며, 애플리케이션 제공자는 이에 대해 아무런 접근 권한을 갖지 않습니다. 32 | 추가적인 보안 대책은 사용자가 자신의 PC 보안을 관리하는 방식에 따라 달라집니다. 33 | 34 | ### 데이터의 보유 및 삭제 35 | 인증 토큰은 사용자의 PC에만 저장됩니다. 36 | 인증 토큰의 삭제는 사용자가 PC 내의 `%APPDATA%\StudioFreesia\WindowTranslator\GoogleAppsScriptPlugin` 폴더를 삭제함으로써 완료됩니다. 37 | 38 | ## 번역 엔진 및 제3자 라이브러리 사용에 관하여 39 | 본 애플리케이션은 Google 번역, DeepL 등 여러 번역 엔진을 지원하도록 설계되었습니다. 40 | 이들 번역 엔진 및 관련 라이브러리는 각 서비스의 개인정보 처리방침에 따라 사용자 데이터 또는 이용 정보를 수집할 수 있습니다. 41 | 사용자께서는 해당 번역 서비스의 개인정보 처리방침을 함께 검토하시기 바랍니다. 42 | 43 | ## 문의 44 | 본 개인정보 처리방침에 관한 문의나 의견은 아래 GitHub 계정으로 연락해 주시기 바랍니다. 45 | GitHub 계정: [Freeesia](https://github.com/Freeesia) 46 | 47 | ## 개인정보 처리방침 변경 48 | 법령 개정 또는 애플리케이션 기능 변경에 따라 본 개인정보 처리방침은 예고 없이 변경될 수 있습니다. 49 | 변경 시 본 애플리케이션 또는 GitHub 리포지토리를 통해 신속하게 공지됩니다. 50 | 51 | > [!WARNING] 52 | > 주의: 이 파일은 일본어 원문을 기계 번역한 것으로, 일본어 원문이 공식 버전입니다. 53 | -------------------------------------------------------------------------------- /docs/PrivacyPolicy.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: プライバシーポリシー 3 | description: WindowTranslator プライバシーポリシー 4 | --- 5 | 6 | 本プライバシーポリシーは、「WindowTranslator」(以下「本アプリ」といいます)の利用に際し、ユーザーの個人情報およびデータの取り扱いについて定めたものです。 7 | 8 | ## はじめに 9 | 本アプリはオープンソースとしてGitHub( https://github.com/Freeesia/WindowTranslator )にて公開されており、アプリ自体にはユーザーの個人情報や利用状況を収集する仕組みはありません。また、エラーログやクラッシュレポート等の収集も行っておりません。 10 | 11 | ## 個人情報の収集について 12 | ### アプリ自体による情報収集 13 | 本アプリは、ユーザーの入力情報、操作履歴、端末情報などの個人情報を直接収集しません。 14 | 15 | ### エラーログ・クラッシュレポート 16 | エラーログやクラッシュレポート等、ユーザーに関するデータの自動収集は実施しておりません。 17 | 18 | ## Googleユーザーデータに関する取り扱い 19 | 本アプリはGoogle Apps Scriptサービスへアクセスする際のみ、以下の通りGoogleユーザーデータに関連する権限を取得します。 20 | 21 | ### アクセスするデータ 22 | 本アプリは、Google Apps Scriptを利用するために必要な最小限の認証トークンのみを取得します。 23 | ユーザーの個人情報自体は一切利用しません。 24 | 25 | ### データの利用方法 26 | 取得した認証情報は、アプリ内でGoogle Apps Scriptを実行するためにのみ使用されます。 27 | この認証は、ユーザーがGoogleサービスを利用するための認証目的に限定され、その他の目的では使用されません。 28 | 29 | ### データの共有・転送 30 | 認証情報やその他のGoogleユーザーデータは、本アプリ提供者によって一切収集、保存、共有、または第三者への転送は行われません。 31 | すべての認証情報はユーザーのPC上に保存され、Googleのサービス利用のためにのみ利用されます。 32 | 33 | ### データ保護の仕組み 34 | 認証情報はユーザーのPC上に安全に保存され、アプリ提供者はこれらの情報へのアクセス権限を一切有しておりません。 35 | したがって、データ保護に関する追加のセキュリティ対策は、ユーザー自身のPCのセキュリティ管理に依存します。 36 | 37 | ### データの保持・削除 38 | 認証情報(Googleの認証トークン等)は、ユーザーのPC上にのみ保存されます。 39 | 認証情報の削除は、ユーザーがPC上の `%APPDATA%\StudioFreesia\WindowTranslator\GoogleAppsScriptPlugin` フォルダを削除することで完結し、アプリ提供者側で管理・削除することはありません。 40 | 41 | ## 翻訳エンジンおよび第三者ライブラリの利用について 42 | 本アプリは、Google翻訳、DeepLなど、複数の翻訳エンジンを利用可能な設計となっております。これらの翻訳エンジンおよび連携ライブラリは、各サービスの独自のプライバシーポリシーに基づいてユーザー情報や利用状況の収集を行う可能性があります。 43 | ユーザーの皆様には、本アプリ利用に際してご利用の翻訳エンジン各社のプライバシーポリシーも併せてご確認いただくことを強く推奨いたします。 44 | 45 | ## お問い合わせ先 46 | 本プライバシーポリシーに関するご質問・ご意見は、下記GitHubアカウントまでご連絡ください。 47 | 48 | GitHubアカウント: [Freeesia](https://github.com/Freeesia) 49 | 50 | ## 本プライバシーポリシーの変更について 51 | 法令の改正や本アプリの機能変更等に伴い、本プライバシーポリシーを予告なく変更する場合があります。変更が行われた場合は、本アプリ内またはGitHubリポジトリ等を通じて速やかに周知いたします。 52 | 53 | 以上 54 | -------------------------------------------------------------------------------- /docs/PrivacyPolicy.vi.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Chính sách riêng tư 3 | description: Chính sách riêng tư của WindowTranslator 4 | --- 5 | 6 | Chính sách riêng tư này quy định về việc xử lý thông tin cá nhân và dữ liệu của người dùng khi sử dụng "WindowTranslator" (sau đây gọi là "Ứng dụng"). 7 | 8 | ## Giới thiệu 9 | Ứng dụng này được công khai dưới dạng mã nguồn mở trên GitHub (https://github.com/Freeesia/WindowTranslator) và bản thân ứng dụng không có cơ chế thu thập thông tin cá nhân hoặc trạng thái sử dụng của người dùng. Ngoài ra, chúng tôi không thu thập nhật ký lỗi hoặc báo cáo sự cố. 10 | 11 | ## Về việc thu thập thông tin cá nhân 12 | ### Thu thập thông tin từ chính ứng dụng 13 | Ứng dụng này không trực tiếp thu thập thông tin cá nhân như thông tin đầu vào của người dùng, lịch sử thao tác, thông tin thiết bị. 14 | 15 | ### Nhật ký lỗi và báo cáo sự cố 16 | Chúng tôi không thực hiện việc tự động thu thập dữ liệu liên quan đến người dùng như nhật ký lỗi hoặc báo cáo sự cố. 17 | 18 | ## Xử lý dữ liệu người dùng Google 19 | Ứng dụng này chỉ lấy quyền truy cập liên quan đến dữ liệu người dùng Google như sau khi truy cập vào dịch vụ Google Apps Script. 20 | 21 | ### Dữ liệu được truy cập 22 | Ứng dụng này chỉ lấy token xác thực tối thiểu cần thiết để sử dụng Google Apps Script. 23 | Chúng tôi không sử dụng bất kỳ thông tin cá nhân nào của người dùng. 24 | 25 | ### Cách sử dụng dữ liệu 26 | Thông tin xác thực thu được chỉ được sử dụng để chạy Google Apps Script trong ứng dụng. 27 | Việc xác thực này chỉ giới hạn cho mục đích xác thực để người dùng sử dụng dịch vụ Google, và không được sử dụng cho bất kỳ mục đích nào khác. 28 | 29 | ### Chia sẻ và chuyển giao dữ liệu 30 | Thông tin xác thực hoặc dữ liệu người dùng Google khác không được nhà cung cấp ứng dụng này thu thập, lưu trữ, chia sẻ hoặc chuyển giao cho bên thứ ba. 31 | Tất cả thông tin xác thực được lưu trữ trên PC của người dùng và chỉ được sử dụng để truy cập dịch vụ của Google. 32 | 33 | ### Cơ chế bảo vệ dữ liệu 34 | Thông tin xác thực được lưu trữ an toàn trên PC của người dùng, và nhà cung cấp ứng dụng không có quyền truy cập vào những thông tin này. 35 | Do đó, các biện pháp bảo mật bổ sung liên quan đến bảo vệ dữ liệu phụ thuộc vào việc quản lý bảo mật của chính PC người dùng. 36 | 37 | ### Lưu trữ và xóa dữ liệu 38 | Thông tin xác thực (như token xác thực của Google) chỉ được lưu trữ trên PC của người dùng. 39 | Việc xóa thông tin xác thực hoàn toàn phụ thuộc vào việc người dùng xóa thư mục `%APPDATA%\StudioFreesia\WindowTranslator\GoogleAppsScriptPlugin` trên PC, và nhà cung cấp ứng dụng không quản lý hoặc xóa những thông tin này. 40 | 41 | ## Về việc sử dụng công cụ dịch và thư viện của bên thứ ba 42 | Ứng dụng này được thiết kế để có thể sử dụng nhiều công cụ dịch khác nhau như Google Dịch, DeepL, v.v. Các công cụ dịch và thư viện kết nối này có thể thu thập thông tin người dùng và tình trạng sử dụng dựa trên chính sách riêng tư riêng của từng dịch vụ. 43 | Chúng tôi mạnh mẽ khuyến nghị người dùng nên đồng thời kiểm tra chính sách riêng tư của mỗi công ty cung cấp công cụ dịch mà bạn sử dụng khi dùng ứng dụng này. 44 | 45 | ## Thông tin liên hệ 46 | Nếu có câu hỏi hoặc ý kiến liên quan đến chính sách riêng tư này, vui lòng liên hệ với tài khoản GitHub dưới đây. 47 | 48 | Tài khoản GitHub: [Freeesia](https://github.com/Freeesia) 49 | 50 | ## Về việc thay đổi chính sách riêng tư này 51 | Chính sách riêng tư này có thể được thay đổi mà không cần thông báo trước do sửa đổi luật pháp hoặc thay đổi chức năng của ứng dụng. Khi có thay đổi, chúng tôi sẽ thông báo ngay lập tức thông qua ứng dụng hoặc kho lưu trữ GitHub. 52 | 53 | Trên đây là toàn bộ chính sách. 54 | 55 | Tài liệu này được dịch từ tiếng Nhật bằng công cụ dịch máy. 56 | -------------------------------------------------------------------------------- /docs/PrivacyPolicy.zh-cn.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 隐私政策 3 | description: WindowTranslator隐私政策 4 | --- 5 | 6 | 本隐私政策规定了“WindowTranslator”(以下简称“本应用”)在使用过程中对用户个人信息及数据的处理方式。 7 | 本应用是开源的,已在GitHub( https://github.com/Freeesia/WindowTranslator )公开, 8 | 本应用本身不收集用户的个人信息或使用数据,也不收集错误日志或崩溃报告。 9 | 10 | ## 关于个人信息的收集 11 | 12 | ### 应用收集的信息 13 | 本应用不直接收集用户的输入信息、操作记录或设备信息等个人数据。 14 | 15 | ### 错误日志及崩溃报告 16 | 本应用不会自动收集任何错误日志或崩溃报告。 17 | 18 | ## 关于Google用户数据的处理 19 | 在访问Google Apps Script服务时,本应用仅获取执行所需的最小认证令牌。 20 | 除该认证外,不使用用户的其他个人信息。 21 | 22 | ### 收集数据的使用方式 23 | 所获得的认证信息仅用于在本应用中执行Google Apps Script。 24 | 此认证仅限于使用户能够使用Google服务之目的,不会用于其他用途。 25 | 26 | ### 数据的共享或传输 27 | 本应用提供者不会收集、保存、共享或向第三方传输认证信息或其他Google用户数据。 28 | 所有认证信息均存储在用户的电脑上,仅用于访问Google服务。 29 | 30 | ### 数据保护 31 | 认证信息在用户电脑上被安全存储,且本应用提供者对此无任何访问权限。 32 | 额外的安全措施依赖于用户自身电脑的安全管理。 33 | 34 | ### 数据的保留与删除 35 | 认证令牌仅存储在用户电脑中。 36 | 用户可通过删除位于 `%APPDATA%\StudioFreesia\WindowTranslator\GoogleAppsScriptPlugin` 的文件夹来删除认证令牌。 37 | 38 | ## 关于翻译引擎及第三方库的使用 39 | 本应用设计上支持包括Google翻译、DeepL等多种翻译引擎。 40 | 这些翻译引擎及相关库可能会依据各自的隐私政策收集用户数据或使用信息。 41 | 建议用户同时查阅所使用翻译服务的隐私政策。 42 | 43 | ## 联系方式 44 | 如对本隐私政策有任何疑问或意见,请联系: 45 | GitHub账号: [Freeesia](https://github.com/Freeesia) 46 | 47 | ## 本隐私政策的变更 48 | 鉴于法律法规的变化或本应用功能的调整,本隐私政策可能会在不提前通知的情况下进行变更。 49 | 变更内容将通过本应用或GitHub仓库等方式及时通知用户。 50 | 51 | > [!WARNING] 52 | > 注意:此文件為机器翻译版本,以日文原版為准。 53 | -------------------------------------------------------------------------------- /docs/PrivacyPolicy.zh-tw.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 隱私政策 3 | description: WindowTranslator隱私政策 4 | --- 5 | 6 | 本隱私政策規範「WindowTranslator」(以下簡稱「本應用」)在使用過程中對用戶個人資訊及數據的處理方式。 7 | 本應用為開源軟體,並已在GitHub( https://github.com/Freeesia/WindowTranslator )公開, 8 | 本應用本身不會收集用戶的個人資訊或使用數據,也不會收集錯誤日誌或崩潰報告。 9 | 10 | ## 關於個人資訊的收集 11 | 12 | ### 應用收集的資訊 13 | 本應用不會直接收集用戶的輸入資訊、操作記錄或設備資訊等個人數據。 14 | 15 | ### 錯誤日誌及崩潰報告 16 | 本應用不會自動收集任何錯誤日誌或崩潰報告。 17 | 18 | ## 關於Google用戶數據的處理 19 | 在訪問Google Apps Script服務時,本應用僅取得執行所需的最小認證令牌。 20 | 除該認證外,本應用不會使用用戶的其他個人資訊。 21 | 22 | ### 收集數據的使用方式 23 | 所獲得的認證資訊僅用於在本應用中執行Google Apps Script。 24 | 此認證僅限於用戶能夠使用Google服務之目的,不會用於其他用途。 25 | 26 | ### 數據的共享或傳輸 27 | 本應用提供者不會收集、儲存、共享或向第三方傳輸認證資訊或其他Google用戶數據。 28 | 所有認證資訊均存放於用戶的電腦中,僅用於訪問Google服務。 29 | 30 | ### 數據保護 31 | 認證資訊在用戶電腦上將被安全保存,且本應用提供者無權訪問這些資訊。 32 | 額外的安全措施依賴於用戶自身電腦的安全管理。 33 | 34 | ### 數據的保留與刪除 35 | 認證令牌僅存放於用戶電腦中。 36 | 使用者可通過刪除位於 `%APPDATA%\StudioFreesia\WindowTranslator\GoogleAppsScriptPlugin` 的資料夾來完成令牌的刪除。 37 | 38 | ## 關於翻譯引擎及第三方庫的使用 39 | 本應用設計上支援包括Google翻譯、DeepL等多種翻譯引擎。 40 | 這些翻譯引擎及相關庫可能會依各自的隱私政策收集用戶數據或使用資訊。 41 | 建議使用者同時查閱所使用翻譯服務的隱私政策。 42 | 43 | ## 聯繫方式 44 | 如對本隱私政策有任何疑問或意見,請聯繫: 45 | GitHub帳戶: [Freeesia](https://github.com/Freeesia) 46 | 47 | ## 本隱私政策的變更 48 | 因應法律或本應用功能的調整,本隱私政策可能會在不事先通知下予以修改。 49 | 變更內容將通過本應用或GitHub倉庫等方式及時通知使用者。 50 | 51 | > [!WARNING] 52 | > 注意:此文件為機器翻譯版本,以日文原版為準。 53 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | title: "WindowTranslator" 2 | description: >- 3 | Windowsのアプリケーションのウィンドウを翻訳するためのツール 4 | 5 | # Build settings 6 | remote_theme: pages-themes/cayman@v0.2.0 7 | plugins: 8 | - jekyll-remote-theme # add this line to the plugins list if you already have one 9 | 10 | markdown: GFM -------------------------------------------------------------------------------- /docs/images/auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/docs/images/auth.png -------------------------------------------------------------------------------- /docs/images/deepl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/docs/images/deepl.png -------------------------------------------------------------------------------- /docs/images/language.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/docs/images/language.png -------------------------------------------------------------------------------- /docs/images/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/docs/images/login.png -------------------------------------------------------------------------------- /docs/images/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/docs/images/result.png -------------------------------------------------------------------------------- /docs/images/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/docs/images/select.png -------------------------------------------------------------------------------- /docs/images/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/docs/images/settings.png -------------------------------------------------------------------------------- /docs/images/startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/docs/images/startup.png -------------------------------------------------------------------------------- /docs/images/translate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/docs/images/translate.png -------------------------------------------------------------------------------- /docs/images/translate_module.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/docs/images/translate_module.png -------------------------------------------------------------------------------- /docs/images/wt.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/docs/images/wt.ico -------------------------------------------------------------------------------- /docs/images/wt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Freeesia/WindowTranslator/2c80209dd7ac2b426e19b4ff620751a6dd3b6536/docs/images/wt.png -------------------------------------------------------------------------------- /docs/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{{title}}} 6 | 10 | 11 | 12 | 13 |
{{{content}}}
14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/theme.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "M PLUS 1p", sans-serif; 3 | } 4 | -------------------------------------------------------------------------------- /hwnd-adorner/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /hwnd-adorner/.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.pdb 3 | *.dll 4 | Demo/bin/Debug/System.Windows.Interactivity.xml 5 | Demo/obj/Debug/App.g.cs 6 | Demo/obj/Debug/App.g.i.cs 7 | Demo/obj/Debug/Demo.csproj.FileListAbsolute.txt 8 | Demo/obj/Debug/Demo.csproj.GenerateResource.Cache 9 | Demo/obj/Debug/Demo.csprojResolveAssemblyReference.cache 10 | Demo/obj/Debug/Demo.g.resources 11 | Demo/obj/Debug/Demo.Properties.Resources.resources 12 | *.cache 13 | *.baml 14 | Demo/obj/Debug/MainWindow.g.cs 15 | Demo/obj/Debug/MainWindow.g.i.cs 16 | *.suo 17 | HwndExtensions/bin/Debug/System.Windows.Interactivity.xml 18 | HwndExtensions/obj/Debug/GeneratedInternalTypeHelper.g.i.cs 19 | HwndExtensions/obj/Debug/HwndExtensions.csproj.FileListAbsolute.txt 20 | HwndExtensions/obj/Debug/HwndExtensions.Properties.Resources.resources 21 | HwndExtensions/obj/Debug/HwndExtensions_MarkupCompile.i.lref 22 | Demo/bin/Debug/Demo.vshost.exe.manifest 23 | Demo/obj/Debug/BrowserAdornment.g.cs 24 | Demo/obj/Debug/BrowserAdornment.g.i.cs 25 | Demo/obj/Debug/Demo_MarkupCompile.lref 26 | Demo/obj/Debug/GeneratedInternalTypeHelper.g.cs 27 | Demo/obj/Debug/GeneratedInternalTypeHelper.g.i.cs -------------------------------------------------------------------------------- /hwnd-adorner/Demo/App.xaml: -------------------------------------------------------------------------------- 1 |  5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /hwnd-adorner/Demo/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Windows; 7 | 8 | namespace Demo; 9 | 10 | /// 11 | /// Interaction logic for App.xaml 12 | /// 13 | public partial class App : Application 14 | { 15 | } 16 | -------------------------------------------------------------------------------- /hwnd-adorner/Demo/BrowserAdornment.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /hwnd-adorner/Demo/BrowserAdornment.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Data; 8 | using System.Windows.Documents; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | using System.Windows.Media.Imaging; 12 | using System.Windows.Navigation; 13 | using System.Windows.Shapes; 14 | 15 | namespace Demo; 16 | 17 | /// 18 | /// Interaction logic for BrowserAdornment.xaml 19 | /// 20 | public partial class BrowserAdornment : UserControl 21 | { 22 | public BrowserAdornment() 23 | { 24 | InitializeComponent(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /hwnd-adorner/Demo/BrowserPresenter.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using HwndExtensions.Host; 4 | 5 | namespace Demo; 6 | 7 | public class BrowserPresenter : HwndHostPresenter 8 | { 9 | public BrowserPresenter() 10 | { 11 | var browser = new WebBrowser(); 12 | browser.Source = new Uri("https://www.google.com/"); 13 | 14 | HwndHost = browser; 15 | RegisterToAppShutdown(); 16 | } 17 | 18 | private void RegisterToAppShutdown() 19 | => Application.Current.Dispatcher.ShutdownStarted += OnShutdownStarted; 20 | 21 | private void OnShutdownStarted(object? sender, EventArgs e) 22 | { 23 | Dispose(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /hwnd-adorner/Demo/Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0-windows7.0 5 | WinExe 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /hwnd-adorner/Demo/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /hwnd-adorner/Demo/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Data; 8 | using System.Windows.Documents; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | using System.Windows.Media.Imaging; 12 | using System.Windows.Navigation; 13 | using System.Windows.Shapes; 14 | 15 | namespace Demo; 16 | 17 | /// 18 | /// Interaction logic for MainWindow.xaml 19 | /// 20 | public partial class MainWindow : Window 21 | { 22 | public MainWindow() 23 | { 24 | InitializeComponent(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HwndExtensions", "HwndExtensions\HwndExtensions.csproj", "{529FE5B9-FAC5-4222-8170-C8C0B911A588}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo", "Demo\Demo.csproj", "{85137B31-43B5-4DF6-B374-A14BAA1A874D}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {529FE5B9-FAC5-4222-8170-C8C0B911A588}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {529FE5B9-FAC5-4222-8170-C8C0B911A588}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {529FE5B9-FAC5-4222-8170-C8C0B911A588}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {529FE5B9-FAC5-4222-8170-C8C0B911A588}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {85137B31-43B5-4DF6-B374-A14BAA1A874D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {85137B31-43B5-4DF6-B374-A14BAA1A874D}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {85137B31-43B5-4DF6-B374-A14BAA1A874D}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {85137B31-43B5-4DF6-B374-A14BAA1A874D}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/Adorner/HwndAdornerElement.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Windows; 3 | 4 | namespace HwndExtensions.Adorner; 5 | 6 | class HwndAdornerElement : FrameworkElement 7 | { 8 | private readonly HwndAdorner m_hwndAdorner; 9 | 10 | public HwndAdornerElement() 11 | { 12 | m_hwndAdorner = new HwndAdorner(this); 13 | 14 | // This helps dependency property inheritance and resource search cross the visual tree boundary 15 | // (between the tree containing this object and the one containing the adorner root) 16 | AddLogicalChild(m_hwndAdorner.Root); 17 | } 18 | 19 | public UIElement? Adornment 20 | { 21 | get => m_hwndAdorner.Adornment; 22 | set => m_hwndAdorner.Adornment = value; 23 | } 24 | 25 | protected override IEnumerator LogicalChildren 26 | { 27 | get 28 | { 29 | yield return m_hwndAdorner.Root; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/Adorner/HwndAdornerManager.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace HwndExtensions.Adorner; 4 | 5 | public sealed class HwndAdornerManager : Decorator, IHwndAdornerManager, IDisposable 6 | { 7 | private readonly HwndAdornerGroup m_hwndAdornerGroup; 8 | private bool m_disposed; 9 | 10 | HwndAdornerGroup IHwndAdornerManager.AdornerGroup 11 | { 12 | get 13 | { 14 | if (m_disposed) 15 | throw new ObjectDisposedException("this"); 16 | return m_hwndAdornerGroup; 17 | } 18 | } 19 | 20 | public HwndAdornerManager() 21 | => m_hwndAdornerGroup = new HwndAdornerGroup(this); 22 | 23 | public void Dispose() 24 | { 25 | m_disposed = true; 26 | m_hwndAdornerGroup.Dispose(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/Adorner/HwndAdornmentRoot.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | 4 | namespace HwndExtensions.Adorner; 5 | 6 | internal class HwndAdornmentRoot : ContentControl 7 | { 8 | public DependencyObject? UIParentCore { get; set; } 9 | 10 | protected override DependencyObject GetUIParentCore() 11 | => UIParentCore ?? base.GetUIParentCore(); 12 | } 13 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/Adorner/IHwndAdornerManager.cs: -------------------------------------------------------------------------------- 1 | namespace HwndExtensions.Adorner; 2 | 3 | internal interface IHwndAdornerManager 4 | { 5 | HwndAdornerGroup AdornerGroup { get; } 6 | } 7 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/Host/ConnectToHostManagerBehavior.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using HwndExtensions.Utils; 3 | using Microsoft.Xaml.Behaviors; 4 | 5 | namespace HwndExtensions.Host; 6 | 7 | public class ConnectToHostManagerBehavior : Behavior where T : FrameworkElement, IHwndHolder 8 | { 9 | private IHwndHostManager? m_hostManager; 10 | 11 | protected override void OnAttached() 12 | { 13 | base.OnAttached(); 14 | 15 | AssociatedObject.Loaded += OnLoaded; 16 | AssociatedObject.Unloaded += OnUnloaded; 17 | } 18 | 19 | private void ConnectToManager(IHwndHostManager? manager) 20 | { 21 | m_hostManager = manager; 22 | m_hostManager?.HwndHostGroup.AddHost(AssociatedObject); 23 | } 24 | 25 | private void DisconnectFromManager() 26 | { 27 | m_hostManager?.HwndHostGroup.RemoveHost(AssociatedObject); 28 | m_hostManager = null; 29 | } 30 | 31 | private void OnLoaded(object sender, RoutedEventArgs routedEventArgs) 32 | { 33 | var manager = AssociatedObject.TryFindVisualAncestor(); 34 | if (m_hostManager != manager) 35 | { 36 | DisconnectFromManager(); 37 | ConnectToManager(manager); 38 | } 39 | } 40 | 41 | private void OnUnloaded(object sender, RoutedEventArgs routedEventArgs) 42 | { 43 | DisconnectFromManager(); 44 | } 45 | 46 | protected override void OnDetaching() 47 | { 48 | DisconnectFromManager(); 49 | 50 | AssociatedObject.Loaded -= OnLoaded; 51 | AssociatedObject.Unloaded -= OnUnloaded; 52 | 53 | base.OnDetaching(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/Host/HwndHostManager.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace HwndExtensions.Host; 4 | 5 | public class HwndHostManager : Decorator, IHwndHostManager 6 | { 7 | private readonly HwndHostGroup m_hostGroup; 8 | private bool m_disposed; 9 | 10 | public HwndHostGroup HwndHostGroup 11 | { 12 | get 13 | { 14 | if (m_disposed) 15 | throw new ObjectDisposedException("this"); 16 | return m_hostGroup; 17 | } 18 | } 19 | 20 | public HwndHostManager() => m_hostGroup = new HwndHostGroup(this); 21 | 22 | public void Dispose() 23 | { 24 | m_disposed = true; 25 | m_hostGroup.Dispose(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/Host/IHwndHolder.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace HwndExtensions.Host; 4 | 5 | public interface IHwndHolder 6 | { 7 | void CollapseHwnd(bool freezeBounds = false); 8 | void FreezeHwndBounds(); 9 | void ExpandHwnd(); 10 | 11 | Rect LatestHwndBounds { get; } 12 | Rect FreezedHwndBounds { get; } 13 | } 14 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/Host/IHwndHostManager.cs: -------------------------------------------------------------------------------- 1 | namespace HwndExtensions.Host; 2 | 3 | public interface IHwndHostManager 4 | { 5 | HwndHostGroup HwndHostGroup { get; } 6 | } 7 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/HwndExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Input; 3 | using HwndExtensions.Adorner; 4 | 5 | namespace HwndExtensions; 6 | 7 | public static class HwndExtensions 8 | { 9 | public static readonly RoutedEvent HwndMouseEnterEvent = EventManager.RegisterRoutedEvent( 10 | "HwndMouseEnter", 11 | RoutingStrategy.Bubble, 12 | typeof(MouseEventHandler), 13 | typeof(HwndExtensions)); 14 | 15 | public static readonly RoutedEvent HwndMouseLeaveEvent = EventManager.RegisterRoutedEvent( 16 | "HwndMouseLeave", 17 | RoutingStrategy.Bubble, 18 | typeof(MouseEventHandler), 19 | typeof(HwndExtensions)); 20 | 21 | 22 | // Manages an adornment over any FrameworkElement through a private attached property 23 | // containning the HwndAdorner instance which will present the adornment over hwnd's 24 | private static readonly DependencyProperty HwndAdornerProperty = DependencyProperty.RegisterAttached( 25 | "HwndAdorner", typeof(HwndAdorner), typeof(HwndExtensions), new PropertyMetadata(null)); 26 | 27 | private static void SetHwndAdorner(DependencyObject element, HwndAdorner? value) 28 | => element.SetValue(HwndAdornerProperty, value); 29 | 30 | [AttachedPropertyBrowsableForType(typeof(DependencyObject))] 31 | private static HwndAdorner? GetHwndAdorner(DependencyObject element) 32 | => (HwndAdorner?)element.GetValue(HwndAdornerProperty); 33 | 34 | public static readonly DependencyProperty HwndAdornmentProperty = DependencyProperty.RegisterAttached( 35 | "HwndAdornment", typeof(UIElement), typeof(HwndExtensions), new UIPropertyMetadata(null, OnHwndAdornmentChanged)); 36 | 37 | /// Helper for setting on . 38 | /// to set on. 39 | /// HwndAdornment property value. 40 | public static void SetHwndAdornment(DependencyObject element, UIElement? value) 41 | => element.SetValue(HwndAdornmentProperty, value); 42 | 43 | /// Helper for getting from . 44 | /// to read from. 45 | /// HwndAdornment property value. 46 | [AttachedPropertyBrowsableForType(typeof(DependencyObject))] 47 | public static UIElement? GetHwndAdornment(DependencyObject element) 48 | => (UIElement?)element.GetValue(HwndAdornmentProperty); 49 | 50 | private static void OnHwndAdornmentChanged(DependencyObject d, DependencyPropertyChangedEventArgs args) 51 | { 52 | if (d is not FrameworkElement element) return; 53 | 54 | var adorner = GetHwndAdorner(element); 55 | 56 | if (args.NewValue is UIElement adornment) 57 | { 58 | if (adorner == null) 59 | { 60 | SetHwndAdorner(element, adorner = new HwndAdorner(element)); 61 | } 62 | 63 | adorner.Adornment = adornment; 64 | } 65 | else 66 | { 67 | if (adorner != null) 68 | { 69 | adorner.Dispose(); 70 | SetHwndAdorner(element, null); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/HwndExtensions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0-windows7.0 4 | true 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/Utils/DispatchUI.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Threading; 3 | 4 | namespace HwndExtensions.Utils; 5 | 6 | public static class DispatchUI 7 | { 8 | public static Dispatcher? MainDispatcher; 9 | 10 | public static Dispatcher? CurrentDispatcher() => Application.Current?.Dispatcher ?? MainDispatcher; 11 | 12 | /// 13 | /// Verify access to the main UI thread if exists 14 | /// 15 | public static void VerifyAccess() 16 | { 17 | var dispatcher = MainDispatcher ?? (Application.Current?.Dispatcher); 18 | dispatcher?.VerifyAccess(); 19 | } 20 | 21 | /// 22 | /// Run the current action on the UI thread if exists 23 | /// The action to run on ui thread 24 | /// Invoke the action with blocking (invoke) use. 25 | /// 26 | public static void OnUIThread(Action action, bool invokeBlocking = false) 27 | { 28 | // if no application is running or the main dispatcher run on the current thread 29 | if (MainDispatcher == null && Application.Current == null) 30 | { 31 | action(); 32 | return; 33 | } 34 | 35 | // get the current dispatcher, check access and run where needed 36 | Dispatcher dispatcherObject = MainDispatcher ?? Application.Current.Dispatcher; 37 | 38 | if (dispatcherObject == null || dispatcherObject.CheckAccess()) 39 | { 40 | action(); 41 | } 42 | else 43 | { 44 | // run the invocation blocking or async 45 | if (invokeBlocking) 46 | { 47 | dispatcherObject.Invoke(action); 48 | } 49 | else 50 | { 51 | dispatcherObject.BeginInvoke(action); 52 | } 53 | } 54 | } 55 | 56 | public static bool OnUIThreadAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal, Dispatcher? dispatcher = null) 57 | { 58 | dispatcher ??= MainDispatcher ?? Application.Current?.Dispatcher; 59 | if (dispatcher != null) 60 | { 61 | dispatcher.BeginInvoke(action, priority); 62 | return true; 63 | } 64 | 65 | return false; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/Utils/HwndSourceConnector.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Interop; 3 | 4 | namespace HwndExtensions.Utils; 5 | 6 | /// 7 | /// A class for managing the connection of an UIElement to its HwndSouce container. 8 | /// Notifying on any HwndSouce change. 9 | /// 10 | public abstract class HwndSourceConnector : IDisposable 11 | { 12 | private readonly UIElement m_connector; 13 | 14 | protected bool Activated { get; private set; } 15 | 16 | protected HwndSourceConnector(UIElement connector) 17 | { 18 | m_connector = connector; 19 | } 20 | 21 | protected void Activate() 22 | { 23 | if (Activated) return; 24 | 25 | if (PresentationSource.FromVisual(m_connector) is HwndSource hwndSource) 26 | { 27 | OnSourceConnected(hwndSource); 28 | } 29 | 30 | PresentationSource.AddSourceChangedHandler(m_connector, OnSourceChanged); 31 | Activated = true; 32 | } 33 | 34 | protected void Deactivate() 35 | { 36 | if (!Activated) return; 37 | 38 | if (PresentationSource.FromVisual(m_connector) is HwndSource hwndSource) 39 | { 40 | OnSourceDisconnected(hwndSource); 41 | } 42 | 43 | PresentationSource.RemoveSourceChangedHandler(m_connector, OnSourceChanged); 44 | Activated = false; 45 | } 46 | 47 | 48 | private void OnSourceChanged(object sender, SourceChangedEventArgs args) 49 | { 50 | if (args.OldSource is HwndSource oldSource) 51 | { 52 | OnSourceDisconnected(oldSource); 53 | } 54 | 55 | if (args.NewSource is HwndSource newSource) 56 | { 57 | OnSourceConnected(newSource); 58 | } 59 | } 60 | 61 | protected abstract void OnSourceDisconnected(HwndSource disconnectedSource); 62 | protected abstract void OnSourceConnected(HwndSource connectedSource); 63 | 64 | protected virtual void Dispose(bool disposing) 65 | { 66 | if (disposing) 67 | { 68 | Deactivate(); 69 | } 70 | } 71 | 72 | public void Dispose() 73 | { 74 | Dispose(true); 75 | GC.SuppressFinalize(this); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/Utils/RectUtil.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Media; 3 | 4 | namespace HwndExtensions.Utils; 5 | 6 | internal static class RectUtil 7 | { 8 | internal static Rect ElementToRoot(Rect rectElement, Visual element, PresentationSource presentationSource) 9 | => element.TransformToAncestor(presentationSource.RootVisual).TransformBounds(rectElement); 10 | 11 | internal static Rect RootToClient(Rect rectRoot, PresentationSource presentationSource) 12 | { 13 | var target = presentationSource.CompositionTarget; 14 | var matrixRootTransform = GetVisualTransform(target.RootVisual); 15 | var rectRootUntransformed = Rect.Transform(rectRoot, matrixRootTransform); 16 | var matrixDPI = target.TransformToDevice; 17 | return Rect.Transform(rectRootUntransformed, matrixDPI); 18 | } 19 | 20 | internal static Matrix GetVisualTransform(Visual v) 21 | { 22 | if (v == null) 23 | { 24 | return Matrix.Identity; 25 | } 26 | var m = Matrix.Identity; 27 | 28 | if (VisualTreeHelper.GetTransform(v) is { } transform) 29 | { 30 | m = Matrix.Multiply(m, transform.Value); 31 | } 32 | 33 | var offset = VisualTreeHelper.GetOffset(v); 34 | m.Translate(offset.X, offset.Y); 35 | 36 | return m; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /hwnd-adorner/HwndExtensions/Utils/WindowConnector.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Interop; 3 | 4 | namespace HwndExtensions.Utils; 5 | 6 | public abstract class WindowConnector : HwndSourceConnector 7 | { 8 | protected WindowConnector(UIElement connector) 9 | : base(connector) 10 | { 11 | } 12 | 13 | protected sealed override void OnSourceConnected(HwndSource connectedSource) 14 | { 15 | if (connectedSource.RootVisual is Window window) 16 | { 17 | OnWindowConnected(window); 18 | } 19 | } 20 | 21 | protected sealed override void OnSourceDisconnected(HwndSource disconnectedSource) 22 | { 23 | if (disconnectedSource.RootVisual is Window window) 24 | { 25 | OnWindowDisconnected(window); 26 | } 27 | } 28 | 29 | protected abstract void OnWindowDisconnected(Window disconnectedWindow); 30 | protected abstract void OnWindowConnected(Window connectedWindow); 31 | } 32 | -------------------------------------------------------------------------------- /hwnd-adorner/License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Michael Sutton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /hwnd-adorner/README.md: -------------------------------------------------------------------------------- 1 | hwnd-adorner 2 | ============ 3 | 4 | WPF library supporting layers (adornments) over any hwnd hosted by an HwndHost. 5 | Here is my post about the project: http://blogs.microsoft.co.il/michaels/2014/11/28/wpf-hwnd-adorner/ 6 | 7 | 8 | fork from https://github.com/michaelsutton/hwnd-adorner -------------------------------------------------------------------------------- /ignore-packages.json: -------------------------------------------------------------------------------- 1 | [ 2 | "*Analyzers", 3 | "Microsoft-WindowsAPICodePack-*", 4 | "Microsoft.Windows.SDK.*", 5 | "Microsoft.Windows.WDK.*", 6 | "Microsoft.Management.Infrastructure.Runtime.*", 7 | "runtime.*" 8 | ] -------------------------------------------------------------------------------- /include-projects.json: -------------------------------------------------------------------------------- 1 | [ 2 | "WindowTranslator\\WindowTranslator.csproj", 3 | "Plugins\\WindowTranslator.Plugin.DeepLTranslatePlugin\\WindowTranslator.Plugin.DeepLTranslatePlugin.csproj", 4 | "Plugins\\WindowTranslator.Plugin.FoMPlugin\\WindowTranslator.Plugin.FoMPlugin.csproj", 5 | "Plugins\\WindowTranslator.Plugin.GoogleAIPlugin\\WindowTranslator.Plugin.GoogleAIPlugin.csproj", 6 | "Plugins\\WindowTranslator.Plugin.LLMPlugin\\WindowTranslator.Plugin.LLMPlugin.csproj" 7 | ] -------------------------------------------------------------------------------- /memo.md: -------------------------------------------------------------------------------- 1 | * ウィンドウの取り込み 2 | * ウィンドウのキャプチャ 3 | * ライブラリ 4 | * OCR 5 | * キャッシュ 6 | 7 | https://qiita.com/kouh/items/7b55fbc72ffc91983575 8 | https://blog.tmyt.jp/entry/2019/09/26/071634 9 | https://docs.microsoft.com/ja-jp/dotnet/desktop/wpf/advanced/hosting-win32-content-in-wpf 10 | https://yotiky.hatenablog.com/entry/unity_uaal-wpf 11 | https://nakatsudotnet.blog.fc2.com/blog-entry-28.html 12 | https://gist.github.com/itsho/8b0e761d9114e27c8570fbf95465bbfc 13 | 14 | https://github.com/michaelsutton/hwnd-adorner 15 | https://github.com/microsoft/Windows.UI.Composition-Win32-Samples 16 | 17 | ## オーバーレイの選択肢 18 | 19 | * キャプチャーそのまま 20 | * CompositonXaml? 21 | * ウィンドウ埋め込み 22 | * 埋め込んだウィンドウにオーバーレイする仕組みの実装 23 | * マウスのスルー 24 | * https://www.nuget.org/packages/AirspaceFixer/ 25 | * ウィンドウ最前面 26 | * 対象ウィンドウ位置・サイズの特定 27 | * `User32.GetWindowRect()` 28 | * 自身のウィンドウの移動 29 | * ↑で取得した値を入れるとDPIの違いでサイズが一致しない 30 | * ポジションがうまく設定できない 31 | * ウインドウ最前面の設定 32 | * `Window.Topmost` 33 | * マウスのスルー 34 | 35 | 36 | ## ライセンス収集 37 | 38 | ``` 39 | dotnet restore 40 | dotnet project-licenses -t -u --packages-filter /Analyzers$/ -m -i . --include-project-file --use-project-assets-json --manual-package-information .\manual-package-information.json -f licenses -e 41 | ``` 42 | 43 | ## GAS 44 | 45 | * `clasp push`でpush 46 | * -------------------------------------------------------------------------------- /package-information.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Id": "SharpDX", 4 | "Version": "4.2.0", 5 | "License": "MIT", 6 | "Copyright": "Copyright (c) 2010-2014 SharpDX - Alexandre Mutel", 7 | "Authors": "Alexandre Mutel", 8 | "ProjectUrl": "https://github.com/sharpdx/SharpDX" 9 | }, 10 | { 11 | "Id": "SharpDX.Direct3D11", 12 | "Version": "4.2.0", 13 | "License": "MIT", 14 | "Copyright": "Copyright (c) 2010-2014 SharpDX - Alexandre Mutel", 15 | "Authors": "Alexandre Mutel", 16 | "ProjectUrl": "https://github.com/sharpdx/SharpDX" 17 | }, 18 | { 19 | "Id": "SharpDX.DXGI", 20 | "Version": "4.2.0", 21 | "License": "MIT", 22 | "Copyright": "Copyright (c) 2010-2014 SharpDX - Alexandre Mutel", 23 | "Authors": "Alexandre Mutel", 24 | "ProjectUrl": "https://github.com/sharpdx/SharpDX" 25 | }, 26 | { 27 | "Id": "Weikio.PluginFramework", 28 | "Version": "1.5.1", 29 | "License": "MIT", 30 | "Copyright": "Copyright (c) 2019 Weik.io", 31 | "Authors": "Weik.io", 32 | "ProjectUrl": "https://github.com/weikio/PluginFramework" 33 | }, 34 | { 35 | "Id": "Weikio.PluginFramework.Abstractions", 36 | "Version": "1.5.1", 37 | "License": "MIT", 38 | "Copyright": "Copyright (c) 2019 Weik.io", 39 | "Authors": "Weik.io", 40 | "ProjectUrl": "https://github.com/weikio/PluginFramework" 41 | }, 42 | { 43 | "Id": "Weikio.PluginFramework.AspNetCore", 44 | "Version": "1.5.1", 45 | "License": "MIT", 46 | "Copyright": "Copyright (c) 2019 Weik.io", 47 | "Authors": "Weik.io", 48 | "ProjectUrl": "https://github.com/weikio/PluginFramework" 49 | }, 50 | { 51 | "Id": "Weikio.PluginFramework.Configuration", 52 | "Version": "1.5.1", 53 | "License": "MIT", 54 | "Copyright": "Copyright (c) 2019 Weik.io", 55 | "Authors": "Weik.io", 56 | "ProjectUrl": "https://github.com/weikio/PluginFramework" 57 | }, 58 | { 59 | "Id": "OpenAI", 60 | "Version": "2.1.0", 61 | "License": "MIT", 62 | "Copyright": "Copyright (c) 2024 OpenAI (https://openai.com)", 63 | "Authors": "OpenAI", 64 | "ProjectUrl": "https://github.com/openai/openai-dotnet" 65 | }, 66 | { 67 | "Id": "Quickenshtein", 68 | "Version": "1.5.1", 69 | "License": "MIT", 70 | "Copyright": "Copyright (c) 2020 James Turner", 71 | "Authors": "James Turner", 72 | "ProjectUrl": "https://github.com/Turnerj/Quickenshtein" 73 | }, 74 | { 75 | "Id": "ValueTaskSupplement", 76 | "Version": "1.1.0", 77 | "License": "MIT", 78 | "Copyright": "Cysharp", 79 | "Authors": "Cysharp", 80 | "ProjectUrl": "https://github.com/Cysharp/ValueTaskSupplement" 81 | } 82 | ] -------------------------------------------------------------------------------- /video/83A8T890N5M/captions.de.sbv: -------------------------------------------------------------------------------- 1 | 0:00:00.428,0:00:04.843 2 | Automatische Übersetzung von PC-Spielen mit Google Übersetzer 3 | 4 | 0:00:13.555,0:00:17.394 5 | Stellen Sie sicher, dass das Übersetzungsmodul auf Google Übersetzer eingestellt ist, und drücken Sie OK 6 | 7 | 0:00:18.810,0:00:21.959 8 | Wählen Sie Ihr Google-Konto aus und melden Sie sich an 9 | 10 | 0:00:21.959,0:00:26.143 11 | Tap "Select All" and Wählen Sie "Alle auswählen" und klicken Sie auf "Weiter"! 12 | "Continue"! 13 | -------------------------------------------------------------------------------- /video/83A8T890N5M/captions.en.sbv: -------------------------------------------------------------------------------- 1 | 0:00:00.428,0:00:04.843 2 | Automatically translate PC games using Google Translate 3 | 4 | 0:00:13.555,0:00:17.394 5 | Ensure that the translation module is set to Google Translate, then press OK 6 | 7 | 0:00:18.810,0:00:21.959 8 | Select your Google account and log in 9 | 10 | 0:00:21.959,0:00:26.143 11 | Tap "Select All" and then "Continue"! 12 | -------------------------------------------------------------------------------- /video/83A8T890N5M/captions.kr.sbv: -------------------------------------------------------------------------------- 1 | 0:00:00.428,0:00:04.843 2 | Google 번역으로 PC 게임 자동 번역 3 | 4 | 0:00:13.555,0:00:17.394 5 | 번역 모듈이 Google 번역으로 설정되어 있는지 확인한 후 OK를 누르세요 6 | 7 | 0:00:18.810,0:00:21.959 8 | Google 계정을 선택하고 로그인하세요 9 | 10 | 0:00:21.959,0:00:26.143 11 | ‘전체 선택’을 누르고 ‘계속’을 클릭하세요❗ 12 | -------------------------------------------------------------------------------- /video/83A8T890N5M/captions.zh-cn.sbv: -------------------------------------------------------------------------------- 1 | 0:00:00.428,0:00:04.843 2 | 使用Google翻译自动翻译PC游戏 3 | 4 | 0:00:13.555,0:00:17.394 5 | 确认翻译模块已设为Google翻译,然后点击确定 6 | 7 | 0:00:18.810,0:00:21.959 8 | 选择Google账号并登录 9 | 10 | 0:00:21.959,0:00:26.143 11 | 点击“全选”然后点击“继续”❗ 12 | -------------------------------------------------------------------------------- /video/83A8T890N5M/captions.zh-tw.sbv: -------------------------------------------------------------------------------- 1 | 0:00:00.428,0:00:04.843 2 | 使用Google翻譯自動翻譯PC遊戲 3 | 4 | 0:00:13.555,0:00:17.394 5 | 確認翻譯模組已設為Google翻譯,然後點擊確定 6 | 7 | 0:00:18.810,0:00:21.959 8 | 選擇Google帳號並登入 9 | 10 | 0:00:21.959,0:00:26.143 11 | 點選「全選」然後點選「繼續」❗ 12 | --------------------------------------------------------------------------------