├── .gitignore
├── LICENSE
├── README.md
├── TsubakiTest
├── FakeApiKeys.cs
├── TestTranslator.cs
└── TsubakiTest.csproj
├── TsubakiTranslator.sln
└── TsubakiTranslator
├── AboutMePage.xaml
├── AboutMePage.xaml.cs
├── App.xaml
├── App.xaml.cs
├── AssemblyInfo.cs
├── BasicLibrary
├── ClipboardHookHandler.cs
├── FileHandler.cs
├── GDI32.cs
├── GamesConfig.cs
├── HotkeyHandler.cs
├── OcrProgram.cs
├── OtherConfig.cs
├── ProcessHelper.cs
├── ScreenshotHandler.cs
├── SourceTextHandler.cs
├── TTSHandler.cs
├── TextHookHandler.cs
├── TranslateAPIConfig.cs
├── TranslateDataList.cs
├── TranslateHandler.cs
├── User32.cs
└── WindowConfig.cs
├── HookResultDisplay.xaml
├── HookResultDisplay.xaml.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── OtherSettingPage.xaml
├── OtherSettingPage.xaml.cs
├── Properties
├── PublishProfiles
│ └── FolderProfile.pubxml
├── Resources.Designer.cs
└── Resources.resx
├── Resources
├── Icon
│ ├── Tsubaki.ico
│ └── Tsubaki.png
└── Textractor
│ ├── x64
│ ├── TextractorCLI.exe
│ └── texthook.dll
│ └── x86
│ ├── TextractorCLI.exe
│ └── texthook.dll
├── ScreenshotWindow.xaml
├── ScreenshotWindow.xaml.cs
├── Themes
└── ScrollViewerStyle.xaml
├── TranslateAPILibrary
├── AliyunTranslator.cs
├── BaiduTranslator.cs
├── BingTranslator.cs
├── CaiyunTranslator.cs
├── ChatGptTranslator.cs
├── CommonFunction.cs
├── DeepLTranslator.cs
├── IBMTranslator.cs
├── ICiBaTranslator.cs
├── ITranslator.cs
├── TencentTranslator.cs
├── VolcengineTranslator.cs
├── XiaoniuTranslator.cs
└── YeekitTranslator.cs
├── TranslateWindow.xaml
├── TranslateWindow.xaml.cs
├── TranslatedResultDisplay.xaml
├── TranslatedResultDisplay.xaml.cs
├── TranslatedResultItem.xaml
├── TranslatedResultItem.xaml.cs
├── TsubakiTranslator.csproj
├── TsubakiTranslator.csproj.user
├── UserConfigPage.xaml
├── UserConfigPage.xaml.cs
├── UserGamePage.xaml
├── UserGamePage.xaml.cs
├── WinStylePage.xaml
└── WinStylePage.xaml.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | /.vs/
2 | /TsubakiTranslator/bin/
3 | /TsubakiTranslator/obj/
4 | /TsubakiTest/ApiKeys.cs
5 | /TsubakiTest/bin/
6 | /TsubakiTest/obj/
7 |
8 | *.user
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 此项目不再维护,推荐使用[LunaTranslator](https://github.com/HIllya51/LunaTranslator)来替代。
2 |
3 | #
TsubakiTranslator
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 终归还是来了呢。
12 |
13 | 我寻找到的,神明大人。
14 |
15 |
16 |
17 | ## 项目的由来
18 |
19 | 在开发这个翻译器之前,我用过几款其他类似的翻译器,其中最喜欢的就是[YUKI Galgame 翻译器](https://github.com/project-yuki/YUKI),它的简洁和可扩展非常对我的胃口,遗憾的是有各种奇奇怪怪的bug且GUI不够完善,作者也很久没有维护了。因此我产生了在该项目的设计的基础上开发新项目并进行完善的想法。
20 |
21 | ## 项目特点
22 |
23 | - 以Hook方式提取游戏文本,支持32位和64位的游戏。
24 | - 基于.NET 6 + WPF开发。
25 | - 界面采用Material Design设计风格,简洁易用。
26 | - 去除了所有的离线翻译接口,完全采用在线API进行翻译。
27 | - 支持翻译中文和英文两种源语言的游戏。
28 | - 新增了多种类的翻译API,可同时对照翻译。
29 | - 对于Hook文本文字重复的现象,提供了按重复字数去重的功能。
30 | - 对于混乱的Hook文本,提供自定义正则规则进行文本替换的功能。
31 | - 支持翻译剪切板文本并提供文本处理功能。
32 | - 支持Windows 10自带的OCR功能。
33 |
34 | ## 使用方法
35 | - [下载地址](https://github.com/Isayama-Kagura/TsubakiTranslator/releases)
36 | 1. 第一次使用时,先进入设置,选择想要使用的翻译API,填写必要信息。
37 | 2. 打开游戏,点击右上角“进程号打开”,选择需要翻译的游戏进程,填写必要的信息后点“确定”。
38 | 3. 进入到hook文本选择界面,让游戏的文本变化,选择提取文本和游戏文本完全一致的项。
39 | 4. 愉快的进行游戏。
40 | 5. 翻译器正常退出后会保存本次使用翻译器的配置和游戏数据,下次进入游戏打开历史游戏记录,选择对应游戏进程按上述步骤进行翻译。
41 | 6. 当Hook获得的文本有规律的混乱时,支持自定义正则表达式进行文本匹配替换(e.g. aaabbbccc的文本要转换成abc,匹配表达式为`(.){3}`,替换表达式为`$1`)。**注意:正则表达式的匹配和替换的模式遵循C#规范,请认真学习相关格式后再进行配置!!!**
42 |
43 | ## 监视剪切板功能
44 | 监视剪切板功能,使翻译器除了对Hook提取的文本进行翻译,还可以对一些游戏(AGTH提取/RPGMaker/Unity)进行特殊处理后再进行翻译。详情可以去VNR吧找相关教程学习。
45 |
46 | ## 文本转语音(TTS)功能
47 | 通过TTS功能可以播放一些文本的语音。该功能采用目前TTS领域最先进的微软Azure的接口,最接近人类真实的语音语调。使用该功能需要用户自行注册一个Azure免费账号。
48 |
49 | ## 光学字符识别(OCR)功能
50 | 基于Windows 10 UWP自带的OCR接口实现,在Windows 10 Build 10240以上版本的系统可以使用,分为手动截图和选区自动截图。可在翻译界面中点击截图按钮或者按快捷键逐个翻译,或者选定区域后,自动对该区域截图翻译。
51 |
52 | ## 支持的翻译API
53 | 目前有支持的翻译API包括阿里、百度、彩云、DeepL、IBM、爱词霸、腾讯、小牛、火山、Yeekit。
54 |
55 | ## 疑问解答
56 |
57 | Q:为什么我玩xxx游戏时提取不到文本/闪退/卡死/翻译API不正常?
58 | A:这类问题可能是本项目的程序设计有缺陷,也可能是依赖项目的不足。其中提取不到文本时请尝试用管理员权限运行翻译器。如遇这类问题无法解决,请详细描述现象、所做的操作、配置,最好能配图,然后提出issue或者给我发邮件,在我项目范围内的会尽量帮助解决。
59 |
60 | Q:游戏的配置文件保存在哪里?
61 | A:在游戏根目录的`config/`文件夹下,更新软件时可以备份该目录,然后复制到新的翻译器根目录下。
62 |
63 | Q:自动提取的游戏文本混乱怎么办?
64 | A:当文本单字重复时,可设置重复次数进行去重(e.g. aaabbbccc的文本,即重复3次),或自定义正则规则去除杂乱文字,或用其他方法把文本导出至剪切板进行翻译,仍无法解决请尝试使用特殊码。
65 |
66 |
67 | ## 联系作者
68 |
69 | 如对本项目有任何建议或疑问,可提出issue或者发送邮件至`isayama_kagura@qq.com`。
70 |
71 | ## 依赖的项目
72 | - [Textractor](https://github.com/Artikash/Textractor)
73 | - [MaterialDesignInXamlToolkit](https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit)
74 | - [Windows Community Toolkit](https://github.com/CommunityToolkit/WindowsCommunityToolkit)
75 | - [RestSharp](https://github.com/restsharp/RestSharp)
76 |
77 | ## 部分代码参考
78 | - [御坂翻译器](https://github.com/hanmin0822/MisakaTranslator)
79 | - [YUKI Galgame 翻译器](https://github.com/project-yuki/YUKI)
80 |
81 |
--------------------------------------------------------------------------------
/TsubakiTest/FakeApiKeys.cs:
--------------------------------------------------------------------------------
1 | namespace TsubakiTest
2 | {
3 | public static class FakeApiKeys
4 | {
5 | public const string ChatGptKey = "api token";
6 | }
7 | }
--------------------------------------------------------------------------------
/TsubakiTest/TestTranslator.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using TsubakiTranslator.TranslateAPILibrary;
3 |
4 | namespace TsubakiTest
5 | {
6 | public class Tests
7 | {
8 | string translateWithTranslator(ITranslator translator,int index, string param1, string param2, string content)
9 | {
10 | translator.TranslatorInit(index, param1, param2);
11 | return translator.Translate(content);
12 | }
13 |
14 | [Test]
15 | public void TestChatGpt()
16 | {
17 | // var content = @"宇宙に始まりはあるが、終わりはない。---無限
18 | //星にもまた始まりはあるが、自らの力をもって滅び逝く。---有限";
19 | // var translator = new ChatGptTranslator {
20 | // SourceLanguage = "Japanese"
21 | // };
22 | //var result = translateWithTranslator(translator,
23 | // ApiKeys.ChatGptKey,
24 | // "",
25 | // content);
26 |
27 | /*
28 | * a possible result is:
29 | * 宇宙有开端,但没有终点。---无限
30 | * 星辰也有起源,但注定会因自身力量的消耗而灭亡。---有限
31 | */
32 | //Console.WriteLine(result);
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/TsubakiTest/TsubakiTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0-windows10.0.17763.0
5 | enable
6 | enable
7 |
8 | false
9 | true
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/TsubakiTranslator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33103.184
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TsubakiTranslator", "TsubakiTranslator\TsubakiTranslator.csproj", "{75F37E85-1AA1-45AB-AC0A-5443B01015F9}"
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 | {75F37E85-1AA1-45AB-AC0A-5443B01015F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {75F37E85-1AA1-45AB-AC0A-5443B01015F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {75F37E85-1AA1-45AB-AC0A-5443B01015F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {75F37E85-1AA1-45AB-AC0A-5443B01015F9}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {E5AC39DD-C3B6-414E-A242-297F794125E0}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/TsubakiTranslator/AboutMePage.xaml:
--------------------------------------------------------------------------------
1 |
9 |
12 |
13 |
14 |
15 |
16 |
19 |
22 |
23 |
26 |
27 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/TsubakiTranslator/AboutMePage.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 |
5 | namespace TsubakiTranslator
6 | {
7 | ///
8 | /// AboutMePage.xaml 的交互逻辑
9 | ///
10 | public partial class AboutMePage : UserControl
11 | {
12 | public AboutMePage()
13 | {
14 | InitializeComponent();
15 | }
16 |
17 | private void GitHubButton_OnClick(object sender, RoutedEventArgs e)
18 | {
19 |
20 | // 激活的是当前默认的浏览器
21 | Process.Start("explorer.exe", "https://github.com/Isayama-Kagura/TsubakiTranslator");
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/TsubakiTranslator/App.xaml:
--------------------------------------------------------------------------------
1 |
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 |
--------------------------------------------------------------------------------
/TsubakiTranslator/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Linq;
3 | using System.Windows;
4 | using TsubakiTranslator.BasicLibrary;
5 |
6 | namespace TsubakiTranslator
7 | {
8 | ///
9 | /// Interaction logic for App.xaml
10 | ///
11 | public partial class App : Application
12 | {
13 | private readonly string baseDir = System.AppDomain.CurrentDomain.BaseDirectory;
14 |
15 | public static WindowConfig WindowConfig { get; private set; }
16 |
17 | public static GamesConfig GamesConfig { get; private set; }
18 |
19 | public static TranslateAPIConfig TranslateAPIConfig { get; private set; }
20 |
21 | public static OtherConfig OtherConfig { get; private set; }
22 |
23 | protected override void OnStartup(StartupEventArgs e)
24 | {
25 | base.OnStartup(e);
26 |
27 | WindowConfig = FileHandler.DeserializeObject(
28 | baseDir + @"config/WindowConfig.json"
29 | ) ?? new WindowConfig();
30 |
31 | GamesConfig = FileHandler.DeserializeObject(
32 | baseDir + @"config/GamesData.json"
33 | ) ?? new GamesConfig();
34 |
35 | TranslateAPIConfig =
36 | FileHandler.DeserializeObject(baseDir +
37 | @"config/APIConfig.json") ??
38 | new TranslateAPIConfig();
39 |
40 | OtherConfig = FileHandler.DeserializeObject(
41 | baseDir + @"config/OtherConfig.json"
42 | ) ?? new OtherConfig();
43 |
44 | var processes = Process.GetProcessesByName("TsubakiTranslator")
45 | .Where(proc => proc.Id != System.Environment.ProcessId);
46 | foreach (var proc in processes)
47 | {
48 | try
49 | {
50 | proc.Kill();
51 | }
52 | catch
53 | {
54 | // ignored
55 | }
56 | }
57 | }
58 |
59 | protected override void OnExit(ExitEventArgs e)
60 | {
61 | FileHandler.SerializeObject(WindowConfig,
62 | baseDir + @"config/WindowConfig.json");
63 | FileHandler.SerializeObject(GamesConfig,
64 | baseDir + @"config/GamesData.json");
65 | FileHandler.SerializeObject(TranslateAPIConfig,
66 | baseDir + @"config/APIConfig.json");
67 | FileHandler.SerializeObject(OtherConfig,
68 | baseDir + @"config/OtherConfig.json");
69 |
70 | base.OnExit(e);
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/TsubakiTranslator/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | [assembly: ThemeInfo(
4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5 | //(used if a resource is not found in the page,
6 | // or application resource dictionaries)
7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8 | //(used if a resource is not found in the page,
9 | // app, or any theme specific resource dictionaries)
10 | )]
11 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/ClipboardHookHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Interop;
4 |
5 | namespace TsubakiTranslator.BasicLibrary
6 | {
7 | public class ClipboardHookHandler : IDisposable
8 | {
9 | const int WM_CLIPBOARDUPDATE = 0x031D;
10 |
11 |
12 |
13 | HwndSource _hwndSource;
14 |
15 | public void Dispose()
16 | {
17 | _hwndSource.RemoveHook(new HwndSourceHook(OnHooked));
18 | User32.RemoveClipboardFormatListener(_hwndSource.Handle);
19 | //_hwndSource?.Dispose();
20 | }
21 |
22 | public ClipboardHookHandler(Window window)
23 | {
24 | WindowInteropHelper helper = new WindowInteropHelper(window);
25 | _hwndSource = HwndSource.FromHwnd(helper.Handle);
26 | bool r = User32.AddClipboardFormatListener(_hwndSource.Handle);
27 | if (r)
28 | {
29 | _hwndSource.AddHook(new HwndSourceHook(OnHooked));
30 | }
31 | }
32 |
33 | private IntPtr OnHooked(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
34 | {
35 | if (msg == WM_CLIPBOARDUPDATE)
36 | {
37 | ClipboardUpdated?.Invoke(this, EventArgs.Empty);
38 | return IntPtr.Zero;
39 | }
40 | return IntPtr.Zero;
41 | }
42 |
43 | public event EventHandler ClipboardUpdated;
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/FileHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text.Json;
4 | using System.Windows.Forms;
5 |
6 | namespace TsubakiTranslator.BasicLibrary
7 | {
8 | class FileHandler
9 | {
10 | public static string SelectFolderPath()
11 | {
12 | string path = "";
13 | FolderBrowserDialog dialog = new FolderBrowserDialog();
14 | dialog.Description = "请选择文件夹作为路径";
15 | if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
16 | {
17 | path = dialog.SelectedPath; // "e:/go"
18 | }
19 |
20 | return path;
21 |
22 | }
23 |
24 | public static void SerializeObject(T value, string path)
25 | {
26 | var jsonString = JsonSerializer.SerializeToUtf8Bytes(value);
27 | try
28 | {
29 | using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
30 | {
31 | fs.Write(jsonString, 0, jsonString.Length);
32 | }
33 | }
34 | catch (Exception e)
35 | {
36 | System.Windows.MessageBox.Show(e.Message);
37 | }
38 | }
39 |
40 | public static T DeserializeObject(string path)
41 | {
42 | T result = default(T);
43 | if (CreateFile(path))
44 | return result;
45 |
46 | try
47 | {
48 | using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
49 | {
50 | byte[] byteArray = new byte[fs.Length];
51 | fs.Read(byteArray, 0, byteArray.Length);
52 | var utf8Reader = new Utf8JsonReader(byteArray);
53 | result = JsonSerializer.Deserialize(ref utf8Reader);
54 | }
55 |
56 | }
57 | catch (Exception e)
58 | {
59 | System.Windows.MessageBox.Show(e.Message);
60 | }
61 |
62 | return result;
63 | }
64 |
65 | public static bool CreateFile(string path)
66 | {
67 | string directory = System.IO.Path.GetDirectoryName(path);
68 | bool flag = false;
69 |
70 | if (!Directory.Exists(directory)) // 返回bool类型,存在返回true,不存在返回false
71 | {
72 | Directory.CreateDirectory(directory); //不存在则创建路径
73 | }
74 |
75 | if (!File.Exists(path)) // 返回bool类型,存在返回true,不存在返回false
76 | {
77 | File.Create(path).Close(); //不存在则创建文件
78 | flag = true;
79 | }
80 |
81 | return flag;
82 |
83 | }
84 |
85 | public static void AppendTextToFile(string text, string path)
86 | {
87 | using (StreamWriter sw = File.AppendText(path))
88 | {
89 | sw.WriteLine(text);
90 | }
91 | }
92 |
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/GDI32.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace TsubakiTranslator.BasicLibrary
5 | {
6 | public static class GDI32
7 | {
8 | #region Enums
9 | ///
10 | /// Specifies a raster-operation code. These codes define how the color data for the
11 | /// source rectangle is to be combined with the color data for the destination
12 | /// rectangle to achieve the final color.
13 | ///
14 | public enum TernaryRasterOperations : uint
15 | {
16 | /// dest = source
17 | SRCCOPY = 0x00CC0020,
18 | /// dest = source OR dest
19 | SRCPAINT = 0x00EE0086,
20 | /// dest = source AND dest
21 | SRCAND = 0x008800C6,
22 | /// dest = source XOR dest
23 | SRCINVERT = 0x00660046,
24 | /// dest = source AND (NOT dest)
25 | SRCERASE = 0x00440328,
26 | /// dest = (NOT source)
27 | NOTSRCCOPY = 0x00330008,
28 | /// dest = (NOT src) AND (NOT dest)
29 | NOTSRCERASE = 0x001100A6,
30 | /// dest = (source AND pattern)
31 | MERGECOPY = 0x00C000CA,
32 | /// dest = (NOT source) OR dest
33 | MERGEPAINT = 0x00BB0226,
34 | /// dest = pattern
35 | PATCOPY = 0x00F00021,
36 | /// dest = DPSnoo
37 | PATPAINT = 0x00FB0A09,
38 | /// dest = pattern XOR dest
39 | PATINVERT = 0x005A0049,
40 | /// dest = (NOT dest)
41 | DSTINVERT = 0x00550009,
42 | /// dest = BLACK
43 | BLACKNESS = 0x00000042,
44 | /// dest = WHITE
45 | WHITENESS = 0x00FF0062,
46 | ///
47 | /// Capture window as seen on screen. This includes layered windows
48 | /// such as WPF windows with AllowsTransparency="true"
49 | ///
50 | CAPTUREBLT = 0x40000000
51 | }
52 | #endregion
53 |
54 | #region DLL Imports
55 |
56 | [DllImport("gdi32.dll", EntryPoint = "BitBlt", SetLastError = true)]
57 | [return: MarshalAs(UnmanagedType.Bool)]
58 | public static extern bool BitBlt([In] IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, [In] IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
59 | #endregion
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/GamesConfig.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using System.Collections.ObjectModel;
3 |
4 | namespace TsubakiTranslator.BasicLibrary
5 | {
6 | public partial class RegexRuleData : ObservableObject
7 | {
8 | [ObservableProperty]
9 | private string sourceRegex;
10 | [ObservableProperty]
11 | private string destinationRegex;
12 |
13 | public RegexRuleData(string sourceRegex, string destinationRegex)
14 | {
15 | this.sourceRegex = sourceRegex;
16 | this.destinationRegex = destinationRegex;
17 | }
18 |
19 | }
20 |
21 | public partial class GameData : ObservableObject
22 | {
23 | [ObservableProperty]
24 | ///
25 | /// 游戏名(非进程名,但在游戏名未知的情况下先使用进程名替代)
26 | ///
27 | private string gameName;
28 |
29 | [ObservableProperty]
30 | ///
31 | /// 游戏名(非进程名,但在游戏名未知的情况下先使用进程名替代)
32 | ///
33 | private string processName;
34 |
35 | [ObservableProperty]
36 | ///
37 | /// 特殊码值,仅在hook模式有效
38 | ///
39 | private string hookCode;
40 |
41 | [ObservableProperty]
42 | ///
43 | /// 文本重复次数
44 | ///
45 | private int duplicateTimes;
46 |
47 | public ObservableCollection RegexRuleItems { get; set; }
48 |
49 | public GameData()
50 | {
51 | RegexRuleItems = new ObservableCollection();
52 | }
53 |
54 | }
55 |
56 | public partial class GamesConfig : ObservableObject
57 | {
58 | [ObservableProperty]
59 | private ObservableCollection gameDatas;
60 | [ObservableProperty]
61 | private ObservableCollection clipBoardRegexRules;
62 |
63 |
64 | public GamesConfig()
65 | {
66 | GameDatas = new ObservableCollection();
67 | ClipBoardRegexRules = new ObservableCollection();
68 | }
69 |
70 |
71 |
72 | }
73 | }
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/HotkeyHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TsubakiTranslator.BasicLibrary
4 | {
5 | public class HotkeyHandler
6 | {
7 | private IntPtr mainFormHandle;
8 | public IntPtr MainFormHandle { get => mainFormHandle; }
9 |
10 | private int id = 856;
11 | private byte modifiers;
12 | private int key;
13 |
14 | public int Id { get => id; }
15 | public void RegisterHotKey(IntPtr mainFormHandle, ScreenshotHotkey hotkey)
16 | {
17 | this.mainFormHandle = mainFormHandle;
18 |
19 | modifiers = hotkey.Modifiers;
20 | key = hotkey.Key;
21 |
22 | if (key != 0)
23 | {
24 | hotkey.Conflict = !User32.RegisterHotKey(mainFormHandle, id, modifiers, key);
25 | }
26 |
27 | }
28 |
29 | public void UnRegisterHotKey()
30 | {
31 | User32.UnregisterHotKey(mainFormHandle, id);
32 | }
33 |
34 | //public void ReRegisterHotKey(ScreenshotHotkey hotkey)
35 | //{
36 | // if (key == 0)
37 | // {
38 | // User32.UnregisterHotKey(mainFormHandle, id);
39 | // }
40 | // else if (modifiers != hotkey.Modifiers || key != hotkey.Key)
41 | // {
42 | // User32.UnregisterHotKey(mainFormHandle, id);
43 | // hotkey.Conflict = !User32.RegisterHotKey(mainFormHandle, id, hotkey.Modifiers, hotkey.Key);
44 | // }
45 | // modifiers = hotkey.Modifiers;
46 | // key = hotkey.Key;
47 |
48 | //}
49 |
50 |
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/OcrProgram.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Runtime.Versioning;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 | using System.Windows.Interop;
9 | using System.Windows.Media.Imaging;
10 | using Windows.Globalization;
11 | using Windows.Graphics.Imaging;
12 | using Windows.Media.Ocr;
13 |
14 | namespace TsubakiTranslator.BasicLibrary
15 | {
16 | public class OcrProgram
17 | {
18 | private string language;
19 |
20 |
21 | public OcrProgram(int srcLangIndex)
22 | {
23 | if (srcLangIndex == (int)ConstantValues.Language.Japanese)
24 | {
25 | language = "ja";
26 | }
27 | else if (srcLangIndex == (int)ConstantValues.Language.English)
28 | {
29 | language = "en-US";
30 | }
31 | }
32 |
33 | [SupportedOSPlatform("windows10.0.10240")]
34 | public static System.Collections.Generic.IEnumerable GetSupportedLanguages()
35 | {
36 |
37 | //Console.WriteLine("Supported languages:");
38 | var result = from lang in OcrEngine.AvailableRecognizerLanguages
39 | where lang.LanguageTag.Equals("ja") || lang.LanguageTag.Equals("en-US")
40 | select lang.DisplayName;
41 |
42 | return result;
43 |
44 | }
45 |
46 | [SupportedOSPlatform("windows10.0.10240")]
47 | public async Task RecognizeAsync(Bitmap CaptureBitmap)
48 | {
49 | //Bitmap to BitmapSource
50 | BitmapSource bitmapSource;
51 | try
52 | {
53 | bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(CaptureBitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
54 | }
55 | catch
56 | {
57 | bitmapSource = default;
58 | }
59 |
60 | //BitmapSource to SoftwareBitmap
61 | // 画像データをメモリストリームへ書き出し
62 | PngBitmapEncoder encoder = new PngBitmapEncoder();
63 | encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(bitmapSource));
64 | MemoryStream temp_stream = new MemoryStream();
65 | encoder.Save(temp_stream);
66 |
67 | // メモリストリームを変換
68 | var converted_stream = WindowsRuntimeStreamExtensions.AsRandomAccessStream(temp_stream);
69 |
70 | // メモリストリームからOCR用画像データの生成
71 | var decorder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(converted_stream);
72 | SoftwareBitmap softwareBitmap = await decorder.GetSoftwareBitmapAsync();
73 | converted_stream.Dispose();
74 | temp_stream.Close();
75 |
76 |
77 | Language lang = new Language(language);
78 | string space = language.Contains("zh") || language.Contains("ja") ? "" : " ";
79 | string result = null;
80 | if (OcrEngine.IsLanguageSupported(lang))
81 | {
82 | OcrEngine engine = OcrEngine.TryCreateFromLanguage(lang);
83 | if (engine != null)
84 | {
85 | OcrResult ocrResult = await engine.RecognizeAsync(softwareBitmap);
86 | foreach (var tempLine in ocrResult.Lines)
87 | {
88 | string line = "";
89 | foreach (var word in tempLine.Words)
90 | {
91 | line += word.Text + space;
92 | }
93 | //result += line + Environment.NewLine;
94 | result += line;
95 | }
96 | }
97 | }
98 | else
99 | {
100 | throw new Exception(string.Format("Language {0} is not supported", language));
101 | };
102 | softwareBitmap.Dispose();
103 | return await Task.Run(() =>
104 | {
105 | return result;
106 | });
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/OtherConfig.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using System;
3 |
4 | namespace TsubakiTranslator.BasicLibrary
5 | {
6 | public class ConstantValues
7 | {
8 | public enum Language { Japanese, English };
9 | }
10 |
11 | public partial class OtherConfig : ObservableObject
12 | {
13 | [ObservableProperty]
14 | private bool isAutoScreenshot = false;
15 | [ObservableProperty]
16 | private ScreenshotHotkey screenshotHotkey = new ScreenshotHotkey();
17 | [ObservableProperty]
18 | private int interval = 3;
19 | [ObservableProperty]
20 | private int sourceLangIndex = 0;
21 | [ObservableProperty]
22 | private bool saveLogEnabled = false;
23 | [ObservableProperty]
24 | private string logFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
25 |
26 | }
27 | public partial class ScreenshotHotkey : ObservableObject
28 | {
29 | [ObservableProperty]
30 | private byte modifiers = 0;
31 | [ObservableProperty]
32 | private int key = 115;
33 | [ObservableProperty]
34 | private string text = "F4";
35 | [ObservableProperty]
36 | private bool conflict = false;
37 |
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/ProcessHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Runtime.InteropServices;
5 |
6 | namespace TsubakiTranslator.BasicLibrary
7 | {
8 | public class ProcessHelper
9 | {
10 |
11 | [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
12 | [return: MarshalAs(UnmanagedType.Bool)]
13 | internal static extern bool IsWow64Process([In] IntPtr process, [Out] out bool wow64Process);
14 |
15 | ///
16 | /// 获得当前系统进程列表 形式:直接用于显示的字串和进程PID
17 | ///
18 | ///
19 | public static LinkedList GetProcessList()
20 | {
21 | LinkedList list = new LinkedList();
22 |
23 | //获取系统进程列表
24 | Process[] ps = Process.GetProcesses();
25 | foreach (Process p in ps)
26 | {
27 | if (p.MainWindowHandle != IntPtr.Zero)
28 | {
29 | string info = p.Id + " — " + p.ProcessName;
30 | list.AddLast(info);
31 | }
32 | }
33 | return list;
34 | }
35 |
36 |
37 | public static bool IsWinX86(Process process)
38 | {
39 |
40 | IntPtr processHandle;
41 | bool retVal;
42 |
43 | try
44 | {
45 | processHandle = Process.GetProcessById(process.Id).Handle;
46 | }
47 | catch
48 | {
49 | return false;
50 | }
51 | return IsWow64Process(processHandle, out retVal) && retVal;
52 |
53 | }
54 |
55 |
56 |
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/ScreenshotHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.IO;
4 | using System.Windows;
5 | using System.Windows.Forms;
6 |
7 | namespace TsubakiTranslator.BasicLibrary
8 | {
9 | public static class ScreenshotHandler
10 | {
11 | public static Bitmap GetCapture(Rect CaptureRegion)
12 | {
13 | var bitmap = new Bitmap((int)CaptureRegion.Width, (int)CaptureRegion.Height);
14 | var graphic = Graphics.FromImage(bitmap);
15 | var screen = SystemInformation.VirtualScreen;
16 |
17 | IntPtr hWnd = IntPtr.Zero;
18 | IntPtr hDC = IntPtr.Zero;
19 | IntPtr graphDC = IntPtr.Zero;
20 | try
21 | {
22 | hWnd = User32.GetDesktopWindow();
23 | hDC = User32.GetWindowDC(hWnd);
24 | graphDC = graphic.GetHdc();
25 | var copyResult = GDI32.BitBlt(graphDC, 0, 0, (int)CaptureRegion.Width, (int)CaptureRegion.Height, hDC, (int)CaptureRegion.Left, (int)CaptureRegion.Top, GDI32.TernaryRasterOperations.SRCCOPY | GDI32.TernaryRasterOperations.CAPTUREBLT);
26 | if (!copyResult)
27 | {
28 | throw new Exception("Screen capture failed.");
29 | }
30 | graphic.ReleaseHdc(graphDC);
31 | User32.ReleaseDC(hWnd, hDC);
32 |
33 | // Get cursor information to draw on the screenshot.
34 | //var ci = new User32.CursorInfo();
35 | //ci.cbSize = Marshal.SizeOf(ci);
36 | //User32.GetCursorInfo(out ci);
37 | //if (ci.flags == User32.CURSOR_SHOWING)
38 | //{
39 | // using (var icon = System.Drawing.Icon.FromHandle(ci.hCursor))
40 | // {
41 | // graphic.DrawIcon(icon, (int)(ci.ptScreenPos.x - screen.Left - CaptureRegion.Left), (int)(ci.ptScreenPos.y - screen.Top - CaptureRegion.Top));
42 | // }
43 | //}
44 |
45 | }
46 | catch (Exception ex)
47 | {
48 | graphic.ReleaseHdc(graphDC);
49 | User32.ReleaseDC(hWnd, hDC);
50 | //throw ex;
51 | System.Windows.MessageBox.Show(ex.Message, "GetCapture Function Error", MessageBoxButton.OK, MessageBoxImage.Error);
52 | }
53 | return bitmap;
54 | }
55 |
56 |
57 | public static bool ImageBase64Compare(Bitmap firstImage, Bitmap secondImage)
58 | {
59 | using (MemoryStream ms = new MemoryStream())
60 | {
61 | firstImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
62 | String firstBitmap = Convert.ToBase64String(ms.ToArray());
63 | ms.Position = 0;
64 |
65 | secondImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
66 | String secondBitmap = Convert.ToBase64String(ms.ToArray());
67 |
68 | if (firstBitmap.Equals(secondBitmap))
69 | {
70 | return true;
71 | }
72 | else
73 | {
74 | return false;
75 | }
76 | };
77 |
78 | }
79 |
80 | //public static void SaveCapture(Bitmap CaptureBitmap)
81 | //{
82 | // System.Windows.Forms.Clipboard.SetImage(CaptureBitmap);
83 |
84 | //}
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/SourceTextHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text;
3 | using System.Text.RegularExpressions;
4 |
5 | namespace TsubakiTranslator.BasicLibrary
6 | {
7 | public class SourceTextHandler
8 | {
9 | public int DuplicateTimes { get; }
10 | public LinkedList RegexRules { get; }
11 |
12 | public SourceTextHandler(int duplicateTimes, LinkedList regexRules)
13 | {
14 | DuplicateTimes = duplicateTimes;
15 | RegexRules = regexRules;
16 | }
17 |
18 | public string HandleText(string text)
19 | {
20 | string result = text;
21 |
22 | if (DuplicateTimes >= 2)
23 | {
24 | StringBuilder sb = new StringBuilder();
25 | for (int i = 0; i < text.Length; i += DuplicateTimes)
26 | sb.Append(text[i]);
27 | result = sb.ToString();
28 | }
29 |
30 | if (RegexRules.Count != 0)
31 | {
32 | foreach (RegexRuleData rule in RegexRules)
33 | if (rule.SourceRegex.Trim().Length != 0)
34 | result = Regex.Replace(result, rule.SourceRegex, rule.DestinationRegex);
35 | }
36 |
37 | return result;
38 | }
39 |
40 |
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/TTSHandler.cs:
--------------------------------------------------------------------------------
1 | using RestSharp;
2 | using System;
3 | using System.IO;
4 | using System.Media;
5 | using System.Threading.Tasks;
6 | using System.Timers;
7 | using TsubakiTranslator.TranslateAPILibrary;
8 |
9 | namespace TsubakiTranslator.BasicLibrary
10 | {
11 | public class TTSHandler
12 | {
13 | private string Region { get; }
14 | private string Key { get; }
15 |
16 | private string Language { get; }
17 |
18 | private string VoiceName { get; }
19 |
20 | private Timer RequestTokenTimer { get; }
21 | private string Token { get; set; }
22 |
23 | private RestClient Client { get; }
24 |
25 | private string errorMessage;
26 | public string ErrorMessage { get => errorMessage; }
27 |
28 | public TTSHandler(string region, string key, string language, string voiceName)
29 | {
30 | Region = region;
31 | Key = key;
32 | Language = language;
33 | VoiceName = voiceName;
34 |
35 | Client = new RestClient(CommonFunction.Client);
36 |
37 | //每9分钟请求一次
38 | int interval = 9 * 60 * 1000;
39 | RequestTokenTimer = new Timer(interval);
40 | RequestTokenTimer.AutoReset = true;
41 | RequestTokenTimer.Elapsed += OnTimedEvent;
42 |
43 | OnTimedEvent(null, null);
44 | RequestTokenTimer.Start();
45 |
46 | }
47 |
48 | public async Task SpeakTextAsync(string text)
49 | {
50 | var request = new RestRequest($"https://{Region}.tts.speech.microsoft.com/cognitiveservices/v1", Method.Post);
51 | request.AddHeader("Authorization", "Bearer " + Token);
52 | request.AddHeader("X-Microsoft-OutputFormat", "riff-24khz-16bit-mono-pcm");
53 | //request.AddHeader("content-type", "application/ssml+xml");
54 |
55 | string bodyString = @$"{text} ";
56 |
57 | //request.AddParameter("application/ssml+xml", bodyString, ParameterType.RequestBody);
58 | //request.AddXmlBody(bodyString);
59 | request.AddStringBody(bodyString, "application/ssml+xml");
60 |
61 | RestResponse response = await Client.ExecuteAsync(request);
62 |
63 | if (response.StatusCode == System.Net.HttpStatusCode.OK)
64 | {
65 | byte[] result = response.RawBytes;
66 | using (MemoryStream ms = new MemoryStream(result))
67 | {
68 | // Construct the sound player
69 | SoundPlayer player = new SoundPlayer(ms);
70 | player.Play();
71 | }
72 | return true;
73 | }
74 | else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
75 | {
76 | OnTimedEvent(null, null);
77 | errorMessage = response.ErrorException.Message;
78 | return false;
79 | }
80 | else
81 | {
82 | errorMessage = response.ErrorException.Message;
83 | return false;
84 | }
85 |
86 | }
87 |
88 | private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
89 | {
90 | var request = new RestRequest($"https://{Region}.api.cognitive.microsoft.com/sts/v1.0/issuetoken", Method.Post);
91 | request.AddHeader("Ocp-Apim-Subscription-Key", Key);
92 |
93 | var response = Client.Execute(request);
94 |
95 | Token = response.Content;
96 |
97 | }
98 |
99 | public void Dispose()
100 | {
101 | RequestTokenTimer.Stop();
102 | RequestTokenTimer.Dispose();
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/TextHookHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace TsubakiTranslator.BasicLibrary
8 | {
9 | public class TextHookHandler
10 | {
11 | ///
12 | /// Textractor进程
13 | ///
14 | private Process processTextractor;
15 | public Process ProcessTextractor { get => processTextractor; }
16 |
17 | private Process processGame;
18 | public Process ProcessGame { get => processGame; }
19 |
20 |
21 | public HashSet SelectedHookCode { get; set; } = new HashSet();
22 |
23 | public TextHookHandler(Process p, string hookCode)
24 | {
25 | Init(p, hookCode);
26 | }
27 |
28 |
29 | ~TextHookHandler()
30 | {
31 | CloseTextractor();
32 | }
33 |
34 | ///
35 | /// 初始化Textractor,建立CLI与本软件间的通信
36 | ///
37 | /// 成功返回真,失败返回假
38 | public async void Init(Process gameProcess, string hookCode)
39 | {
40 | bool isX86 = ProcessHelper.IsWinX86(gameProcess);
41 |
42 | //当游戏退出时可以获得退出事件。
43 | processGame = gameProcess;
44 | processGame.EnableRaisingEvents = true;
45 |
46 | string path = Environment.CurrentDirectory + @"\Resources\Textractor\" + (isX86 ? "x86" : "x64") + @"\TextractorCLI.exe";//打开对应的Textractor路径
47 |
48 | ProcessStartInfo processStartInfo = new ProcessStartInfo()
49 | {
50 | UseShellExecute = false,
51 | CreateNoWindow = true,
52 | StandardOutputEncoding = Encoding.Unicode,
53 | StandardInputEncoding = new UnicodeEncoding(false, false),
54 | FileName = path,
55 | RedirectStandardInput = true,
56 | RedirectStandardOutput = true,
57 | RedirectStandardError = true
58 | };
59 |
60 | processTextractor = Process.Start(processStartInfo);
61 | await AttachProcess();
62 |
63 | if (hookCode != null)
64 | await AttachProcessByHookCode(hookCode);
65 |
66 | ProcessTextractor.BeginOutputReadLine();
67 |
68 | }
69 |
70 | ///
71 | /// 向Textractor CLI写入命令
72 | /// 注入进程
73 | ///
74 | ///
75 | private async Task AttachProcess()
76 | {
77 | //ProcessTextractor.StandardInput.WriteLine("attach -P" + GamePID);
78 | //Console.Write("attach -P" + GamePID);
79 |
80 | //适用多个同名进程的情况,只在通过进程启动有效。
81 | Process[] processes = Process.GetProcessesByName(ProcessGame.ProcessName);
82 |
83 | foreach (Process process in processes)
84 | {
85 | await ProcessTextractor.StandardInput.WriteLineAsync("attach -P" + process.Id);
86 | await ProcessTextractor.StandardInput.FlushAsync();
87 | }
88 |
89 | }
90 |
91 | ///
92 | /// 向Textractor CLI写入命令
93 | /// 结束注入进程
94 | ///
95 | ///
96 | private async Task DetachProcess()
97 | {
98 | //适用多个同名进程的情况,只在通过进程启动有效。
99 | Process[] processes = Process.GetProcessesByName(ProcessGame.ProcessName);
100 | foreach (Process process in processes)
101 | {
102 | await ProcessTextractor.StandardInput.WriteLineAsync("detach -P" + process.Id);
103 | await ProcessTextractor.StandardInput.FlushAsync();
104 | }
105 | }
106 |
107 | ///
108 | /// 向Textractor CLI写入命令
109 | /// 给定特殊码注入,由Textractor作者指导方法
110 | ///
111 | ///
112 | private async Task AttachProcessByHookCode(string hookCode)
113 | {
114 | //解决有hookcode时莫名的报错。
115 | //await Task.Delay(10);
116 |
117 | Process[] processes = Process.GetProcessesByName(ProcessGame.ProcessName);
118 | foreach (Process process in processes)
119 | {
120 | await ProcessTextractor.StandardInput.WriteLineAsync(hookCode + " -P" + process.Id);
121 | await ProcessTextractor.StandardInput.FlushAsync();
122 | }
123 | }
124 |
125 | ///
126 | /// 关闭Textractor进程,关闭前Detach所有Hook
127 | ///
128 | public async void CloseTextractor()
129 | {
130 | /*
131 | * TODO:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
132 | * 这里如果进程退出会出现异常
133 | */
134 | if (ProcessTextractor != null && ProcessTextractor.HasExited == false)
135 | {
136 | await DetachProcess();
137 | ProcessTextractor.Kill();
138 |
139 | }
140 |
141 | processTextractor = null;
142 | }
143 |
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/TranslateAPIConfig.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 |
3 | namespace TsubakiTranslator.BasicLibrary
4 | {
5 | public partial class TranslateAPIConfig : ObservableObject
6 | {
7 | [ObservableProperty]
8 | private bool ttsIsEnabled;
9 | [ObservableProperty]
10 | private string ttsRegion;
11 | [ObservableProperty]
12 | private string ttsResourceKey;
13 |
14 | [ObservableProperty]
15 | private bool aliIsEnabled;
16 | [ObservableProperty]
17 | private string aliSecretId;
18 | [ObservableProperty]
19 | private string aliSecretKey;
20 |
21 | [ObservableProperty]
22 | private bool baiduIsEnabled;
23 | [ObservableProperty]
24 | private string baiduAppID;
25 | [ObservableProperty]
26 | private string baiduSecretKey;
27 |
28 | [ObservableProperty]
29 | private bool bingIsEnabled;
30 |
31 | [ObservableProperty]
32 | private bool caiyunIsEnabled;
33 | [ObservableProperty]
34 | private string caiyunToken;
35 |
36 | [ObservableProperty]
37 | private bool chatGptIsEnabled;
38 | [ObservableProperty]
39 | private string chatGptToken;
40 |
41 | [ObservableProperty]
42 | private bool deeplIsEnabled;
43 | [ObservableProperty]
44 | private bool deeplIsFreeApi;
45 | [ObservableProperty]
46 | private string deeplSecretKey;
47 |
48 | [ObservableProperty]
49 | private bool ibmIsEnabled;
50 |
51 | [ObservableProperty]
52 | private bool iCiBaIsEnabled;
53 |
54 | [ObservableProperty]
55 | private bool tencentIsEnabled;
56 | [ObservableProperty]
57 | private string tencentSecretID;
58 | [ObservableProperty]
59 | private string tencentSecretKey;
60 |
61 | [ObservableProperty]
62 | private bool xiaoniuIsEnabled;
63 | [ObservableProperty]
64 | private string xiaoniuApiKey;
65 |
66 | [ObservableProperty]
67 | private bool volcengineIsEnabled;
68 |
69 | [ObservableProperty]
70 | private bool yeekitIsEnabled;
71 |
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/TranslateDataList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using TsubakiTranslator.BasicLibrary;
6 |
7 | namespace TsubakiTranslator
8 | {
9 | //存储单次翻译的结果数据
10 | public class TranslateData
11 | {
12 | public string SourceText { get; }
13 | public Dictionary ResultText { get; }
14 |
15 | public TranslateData(string sourceText, Dictionary resultText)
16 | {
17 | SourceText = sourceText;
18 | ResultText = resultText;
19 | }
20 |
21 | }
22 |
23 | public class TranslateDataList
24 | {
25 |
26 | private int MaxLength { get; }
27 | private LinkedList list;
28 |
29 | private LinkedListNode currentData;
30 | private LinkedListNode CurrentData { get => currentData; }
31 |
32 | private string DataLogFilePath { get; }
33 |
34 | public TranslateDataList(int maxLength)
35 | {
36 | MaxLength = maxLength;
37 | list = new LinkedList();
38 | }
39 | public TranslateDataList(int maxLength, string logPath)
40 | {
41 | MaxLength = maxLength;
42 | list = new LinkedList();
43 |
44 | DateTime dt = DateTime.Now;
45 | DataLogFilePath = logPath + "\\translated_" + string.Format("{0:yyMMddHHmmss}", dt) + ".log";
46 | FileHandler.CreateFile(DataLogFilePath);
47 |
48 | }
49 |
50 | ///
51 | /// 输入TranslateData,插入队尾
52 | ///
53 | ///
54 | ///
55 | public void AddTranslateData(TranslateData translateData)
56 | {
57 | if (list.Count >= MaxLength)
58 | {
59 | if (DataLogFilePath != null)
60 | SaveDataToFile(list.First.Value);
61 | list.RemoveFirst();
62 | }
63 | list.AddLast(translateData);
64 | currentData = list.Last;
65 | }
66 |
67 | //获得最新的翻译结果数据
68 | public TranslateData GetCurrentData()
69 | {
70 | return list.Last.Value;
71 | }
72 |
73 | public TranslateData GetNextData()
74 | {
75 | if (CurrentData != list.Last)
76 | currentData = CurrentData.Next;
77 | return CurrentData.Value;
78 | }
79 |
80 | public TranslateData GetPreviousData()
81 | {
82 | if (CurrentData != list.First)
83 | currentData = CurrentData.Previous;
84 | return CurrentData.Value;
85 | }
86 |
87 | public TranslateData GetFirstData()
88 | {
89 | currentData = list.First;
90 | return CurrentData.Value;
91 | }
92 |
93 | public TranslateData GetLastData()
94 | {
95 | currentData = list.Last;
96 | return CurrentData.Value;
97 | }
98 |
99 | public int Count()
100 | {
101 | return list.Count();
102 | }
103 |
104 | public void SaveAllDataToFile()
105 | {
106 | foreach (TranslateData data in list)
107 | {
108 | SaveDataToFile(data);
109 | }
110 | }
111 | private void SaveDataToFile(TranslateData data)
112 | {
113 | var sb = new StringBuilder();
114 | sb.AppendLine(data.SourceText);
115 | foreach (string key in data.ResultText.Keys)
116 | sb.Append(key).Append(": ").Append(data.ResultText[key]).AppendLine();
117 |
118 | string result = sb.ToString();
119 |
120 | FileHandler.AppendTextToFile(result, DataLogFilePath);
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/TranslateHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using TsubakiTranslator.TranslateAPILibrary;
4 |
5 | namespace TsubakiTranslator.BasicLibrary
6 | {
7 | class TranslateHandler
8 | {
9 | //对翻译API进行初始化。
10 | public static LinkedList GetSelectedTranslators(TranslateAPIConfig translateAPIConfig, int srcLangIndex)
11 | {
12 | LinkedList translators = new LinkedList();
13 |
14 | if (translateAPIConfig.AliIsEnabled)
15 | {
16 | ITranslator aliyun = new AliyunTranslator();
17 | aliyun.TranslatorInit(srcLangIndex, translateAPIConfig.AliSecretId, translateAPIConfig.AliSecretKey);
18 | translators.AddLast(aliyun);
19 | }
20 |
21 | if (translateAPIConfig.BaiduIsEnabled)
22 | {
23 | ITranslator baidu = new BaiduTranslator();
24 | baidu.TranslatorInit(srcLangIndex, translateAPIConfig.BaiduAppID, translateAPIConfig.BaiduSecretKey);
25 | translators.AddLast(baidu);
26 | }
27 |
28 | if (translateAPIConfig.BingIsEnabled)
29 | {
30 | ITranslator bing = new BingTranslator();
31 | bing.TranslatorInit(srcLangIndex, null, null);
32 | translators.AddLast(bing);
33 | }
34 |
35 | if (translateAPIConfig.CaiyunIsEnabled)
36 | {
37 | ITranslator caiyun = new CaiyunTranslator();
38 | caiyun.TranslatorInit(srcLangIndex, translateAPIConfig.CaiyunToken, "");
39 | translators.AddLast(caiyun);
40 | }
41 |
42 | if (translateAPIConfig.ChatGptIsEnabled)
43 | {
44 | ITranslator chatgpt = new ChatGptTranslator();
45 | chatgpt.TranslatorInit(srcLangIndex, translateAPIConfig.ChatGptToken, "");
46 | translators.AddLast(chatgpt);
47 | }
48 |
49 | if (translateAPIConfig.DeeplIsEnabled)
50 | {
51 | ITranslator deepl = new DeepLTranslator();
52 | deepl.TranslatorInit(srcLangIndex, translateAPIConfig.DeeplSecretKey, translateAPIConfig.DeeplIsFreeApi ? null : "");
53 | translators.AddLast(deepl);
54 | }
55 |
56 |
57 | if (translateAPIConfig.IbmIsEnabled)
58 | {
59 | ITranslator ibm = new IBMTranslator();
60 | ibm.TranslatorInit(srcLangIndex, null, null);
61 | translators.AddLast(ibm);
62 |
63 | }
64 |
65 | if (translateAPIConfig.ICiBaIsEnabled)
66 | {
67 | ITranslator iCiBa = new ICiBaTranslator();
68 | iCiBa.TranslatorInit(srcLangIndex, null, null);
69 | translators.AddLast(iCiBa);
70 |
71 | }
72 |
73 | if (translateAPIConfig.TencentIsEnabled)
74 | {
75 | ITranslator tencent = new TencentTranslator();
76 | tencent.TranslatorInit(srcLangIndex, translateAPIConfig.TencentSecretID, translateAPIConfig.TencentSecretKey);
77 | translators.AddLast(tencent);
78 | }
79 |
80 | if (translateAPIConfig.XiaoniuIsEnabled)
81 | {
82 | ITranslator xiaoniu = new XiaoniuTranslator();
83 | xiaoniu.TranslatorInit(srcLangIndex, translateAPIConfig.XiaoniuApiKey, "");
84 | translators.AddLast(xiaoniu);
85 | }
86 |
87 | if (translateAPIConfig.VolcengineIsEnabled)
88 | {
89 | ITranslator volcengine = new VolcengineTranslator();
90 | volcengine.TranslatorInit(srcLangIndex, null, null);
91 | translators.AddLast(volcengine);
92 | }
93 |
94 | if (translateAPIConfig.YeekitIsEnabled)
95 | {
96 | ITranslator yeekit = new YeekitTranslator();
97 | yeekit.TranslatorInit(srcLangIndex, null, null);
98 | translators.AddLast(yeekit);
99 | }
100 |
101 | return translators;
102 | }
103 |
104 |
105 | ///
106 | /// 根据翻译类名创建对象实例
107 | ///
108 | ///
109 | /// 命名空间.类型名
110 | ///
111 | public static T CreateInstance(string fullName)
112 | {
113 | //string path = fullName + "," + assemblyName;//命名空间.类型名,程序集
114 | Type o = Type.GetType(fullName);//加载类型
115 | object obj = Activator.CreateInstance(o, true);//根据类型创建实例
116 | return (T)obj;//类型转换并返回
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/User32.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace TsubakiTranslator.BasicLibrary
5 | {
6 | public static class User32
7 | {
8 | public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
9 | [StructLayout(LayoutKind.Sequential)]
10 | public struct RECT
11 | {
12 | public int Left, Top, Right, Bottom;
13 |
14 | public RECT(int left, int top, int right, int bottom)
15 | {
16 | Left = left;
17 | Top = top;
18 | Right = right;
19 | Bottom = bottom;
20 | }
21 |
22 | public RECT(System.Drawing.Rectangle r) : this(r.Left, r.Top, r.Right, r.Bottom) { }
23 |
24 | public int X
25 | {
26 | get { return Left; }
27 | set { Right -= (Left - value); Left = value; }
28 | }
29 |
30 | public int Y
31 | {
32 | get { return Top; }
33 | set { Bottom -= (Top - value); Top = value; }
34 | }
35 |
36 | public int Height
37 | {
38 | get { return Bottom - Top; }
39 | set { Bottom = value + Top; }
40 | }
41 |
42 | public int Width
43 | {
44 | get { return Right - Left; }
45 | set { Right = value + Left; }
46 | }
47 |
48 | public System.Drawing.Point Location
49 | {
50 | get { return new System.Drawing.Point(Left, Top); }
51 | set { X = value.X; Y = value.Y; }
52 | }
53 |
54 | public System.Drawing.Size Size
55 | {
56 | get { return new System.Drawing.Size(Width, Height); }
57 | set { Width = value.Width; Height = value.Height; }
58 | }
59 |
60 | public static implicit operator System.Drawing.Rectangle(RECT r)
61 | {
62 | return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height);
63 | }
64 |
65 | public static implicit operator RECT(System.Drawing.Rectangle r)
66 | {
67 | return new RECT(r);
68 | }
69 |
70 | public static bool operator ==(RECT r1, RECT r2)
71 | {
72 | return r1.Equals(r2);
73 | }
74 |
75 | public static bool operator !=(RECT r1, RECT r2)
76 | {
77 | return !r1.Equals(r2);
78 | }
79 |
80 | public bool Equals(RECT r)
81 | {
82 | return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom;
83 | }
84 |
85 | public override bool Equals(object obj)
86 | {
87 | if (obj is RECT)
88 | return Equals((RECT)obj);
89 | else if (obj is System.Drawing.Rectangle)
90 | return Equals(new RECT((System.Drawing.Rectangle)obj));
91 | return false;
92 | }
93 |
94 | public override int GetHashCode()
95 | {
96 | return ((System.Drawing.Rectangle)this).GetHashCode();
97 | }
98 |
99 | public override string ToString()
100 | {
101 | return string.Format(System.Globalization.CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, Right, Bottom);
102 | }
103 | }
104 | public const Int32 CURSOR_SHOWING = 0x00000001;
105 | [StructLayout(LayoutKind.Sequential)]
106 | public struct POINT
107 | {
108 | public Int32 x;
109 | public Int32 y;
110 | }
111 | [StructLayout(LayoutKind.Sequential)]
112 | public struct CursorInfo
113 | {
114 | public Int32 cbSize;
115 | public Int32 flags;
116 | public IntPtr hCursor;
117 | public POINT ptScreenPos;
118 | }
119 | [DllImport("user32.dll", SetLastError = false)]
120 | public static extern IntPtr GetDesktopWindow();
121 | [DllImport("user32.dll", SetLastError = false)]
122 | public static extern IntPtr GetShellWindow();
123 |
124 | [DllImport("user32.dll")]
125 | public static extern IntPtr GetWindowDC(IntPtr hWnd);
126 | [DllImport("User32.dll")]
127 | public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
128 | [DllImport("user32.dll")]
129 | public static extern bool GetCursorInfo(out CursorInfo pci);
130 |
131 | [DllImport("user32.dll")]
132 | public static extern IntPtr WindowFromPoint(System.Drawing.Point p);
133 | [DllImport("user32.dll", SetLastError = true)]
134 | [return: MarshalAs(UnmanagedType.Bool)]
135 | public static extern bool GetCursorPos(out System.Drawing.Point lpPoint);
136 | [DllImport("user32.dll", SetLastError = true)]
137 | public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
138 | [DllImport("user32.dll")]
139 | [return: MarshalAs(UnmanagedType.Bool)]
140 | public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
141 | [DllImport("user32.dll")]
142 | public static extern bool IsWindowVisible(IntPtr hwnd);
143 |
144 | [DllImport("User32.dll")]
145 | public static extern bool AddClipboardFormatListener(IntPtr hwnd);
146 |
147 | [DllImport("User32.dll")]
148 | public static extern bool RemoveClipboardFormatListener(IntPtr hwnd);
149 |
150 | ///
151 | /// 注册全局热键
152 | ///
153 | /// 要定义热键的窗口的句柄
154 | /// 定义热键ID(不能与其它ID重复,全局唯一)
155 | /// 标识热键是否在按Alt、Ctrl、Shift、Windows等键时才会生效
156 | /// 定义热键的内容
157 | /// 成功,返回值不为0,失败,返回值为0。要得到扩展错误信息,调用GetLastError
158 | [DllImport("user32.dll", SetLastError = true)]
159 | public static extern bool RegisterHotKey(IntPtr hWnd, int id, byte fsModifiers, int vk);
160 |
161 | ///
162 | /// 取消注册全局热键
163 | ///
164 | /// 要取消热键的窗口的句柄
165 | /// 要取消热键的ID
166 | ///
167 | [DllImport("user32.dll", SetLastError = true)]
168 | public static extern bool UnregisterHotKey(IntPtr hWnd, int id);
169 |
170 | ///
171 | /// 该函数将指定的窗口设置到Z序的顶部。
172 | ///
173 | ///
174 | [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
175 | public static extern int BringWindowToTop(IntPtr hWnd);
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/TsubakiTranslator/BasicLibrary/WindowConfig.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using System.Windows.Media;
3 |
4 | namespace TsubakiTranslator.BasicLibrary
5 | {
6 | public partial class WindowConfig : ObservableObject
7 | {
8 | [ObservableProperty]
9 | private double mainWindowWidth = 400;
10 | [ObservableProperty]
11 | private double mainWindowHeight = 800;
12 | [ObservableProperty]
13 | private double translateWindowHeight = 400;
14 | [ObservableProperty]
15 | private double translateWindowWidth = 800;
16 | [ObservableProperty]
17 | private double translateWindowLeft = 400;
18 | [ObservableProperty]
19 | private double translateWindowTop = 200;
20 | [ObservableProperty]
21 | private bool translateWindowTopmost = false;
22 | [ObservableProperty]
23 | private int translateWindowTransparency = 165;
24 | [ObservableProperty]
25 | private Color sourceTextColor = Colors.BurlyWood;
26 | [ObservableProperty]
27 | private Color translatedTextColor = Colors.WhiteSmoke;
28 | [ObservableProperty]
29 | private bool sourceTextVisibility = true;
30 | [ObservableProperty]
31 | private bool translatorNameVisibility = true;
32 | [ObservableProperty]
33 | private string sourceTextFontFamily = "Microsoft YaHei UI";
34 | [ObservableProperty]
35 | private string translatedTextFontFamily = "Microsoft YaHei UI";
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/TsubakiTranslator/HookResultDisplay.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
14 |
15 |
16 |
20 |
21 |
22 |
27 |
28 |
29 |
30 |
31 |
35 |
36 |
37 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/TsubakiTranslator/HookResultDisplay.xaml.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Collections.ObjectModel;
5 | using System.Windows.Controls;
6 |
7 | namespace TsubakiTranslator
8 | {
9 | ///
10 | /// HookResultDisplay.xaml 的交互逻辑
11 | ///
12 | public partial class HookResultDisplay : UserControl
13 | {
14 | private Dictionary HookItemDict;
15 | private TranslateWindow translateWindow;
16 |
17 | public HookResultDisplay(TranslateWindow translateWindow)
18 | {
19 | InitializeComponent();
20 |
21 | HookItemDict = new Dictionary();
22 | this.translateWindow = translateWindow;
23 |
24 | HookDataSet = new ObservableCollection();
25 | HookDataGrid.ItemsSource = HookDataSet;
26 | }
27 |
28 | public void UpdateHookResultItem(string hookcode, string content)
29 | {
30 | if (HookItemDict.ContainsKey(hookcode))
31 | HookItemDict[hookcode].HookText = content;
32 | else
33 | {
34 | HookData item = new HookData(hookcode, content);
35 | HookItemDict.Add(hookcode, item);
36 | //Dispatcher是一个线程控制器,要控制线程里跑的东西,就要经过它。
37 | //WPF里面,有个所谓UI线程,后台代码不能直接操作UI控件,需要控制,就要通过这个Dispatcher。
38 | App.Current.Dispatcher.Invoke((Action)(() =>
39 | {
40 | /// start 你的逻辑代码
41 | HookDataSet.Add(item);
42 | /// end
43 | }));
44 |
45 |
46 | }
47 | }
48 |
49 | public HashSet GetSelectedHookData()
50 | {
51 | HashSet result = new HashSet();
52 | foreach(HookData data in HookDataSet)
53 | {
54 | if (data.IsSelected)
55 | {
56 | result.Add(data);
57 | }
58 | }
59 | return result;
60 | }
61 |
62 | private ObservableCollection HookDataSet { get; }
63 |
64 |
65 | }
66 |
67 | public class HookData : ObservableObject
68 | {
69 |
70 | private string hookCode;
71 | private string hookText;
72 | private bool isSelected;
73 |
74 | public HookData(string hookCode, string hookText)
75 | {
76 | this.hookCode = hookCode;
77 | this.hookText = hookText;
78 | }
79 | public string HookCode
80 | {
81 | get => hookCode;
82 | set => SetProperty(ref hookCode, value);
83 | }
84 | public string HookText
85 | {
86 | get => hookText;
87 | set => SetProperty(ref hookText, value);
88 | }
89 |
90 | public bool IsSelected
91 | {
92 | get => isSelected;
93 | set => SetProperty(ref isSelected, value);
94 | }
95 |
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/TsubakiTranslator/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
40 |
41 |
42 |
46 |
47 |
48 |
52 |
53 |
56 |
57 |
58 |
59 |
60 |
61 |
63 |
64 |
65 |
69 |
70 |
71 |
73 |
77 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
89 |
93 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
104 |
108 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
119 |
123 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
134 |
138 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/TsubakiTranslator/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Input;
3 |
4 | namespace TsubakiTranslator
5 | {
6 | ///
7 | /// Interaction logic for MainWindow.xaml
8 | ///
9 | public partial class MainWindow : Window
10 | {
11 |
12 | public MainWindow()
13 | {
14 | InitializeComponent();
15 |
16 | this.DataContext = App.WindowConfig;
17 |
18 | }
19 |
20 | private void ColorZone_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
21 | {
22 | /* 如何在Window.ResizeMode属性为CanResize的时候,阻止窗口拖动到屏幕边缘自动最大化。
23 | (When the Window.ResizeMode property is CanResize,
24 | when the window is dragged to the edge of the screen,
25 | it prevents the window from automatically maximizing.)*/
26 | if (e.ChangedButton == MouseButton.Left)
27 | {
28 | if (Mouse.LeftButton == MouseButtonState.Pressed)
29 | {
30 | var windowMode = this.ResizeMode;
31 | if (this.ResizeMode != ResizeMode.NoResize)
32 | {
33 | this.ResizeMode = ResizeMode.NoResize;
34 | }
35 |
36 | this.UpdateLayout();
37 |
38 |
39 | /* 当点击拖拽区域的时候,让窗口跟着移动
40 | (When clicking the drag area, make the window follow) */
41 | DragMove();
42 |
43 |
44 | if (this.ResizeMode != windowMode)
45 | {
46 | this.ResizeMode = windowMode;
47 | }
48 |
49 | this.UpdateLayout();
50 | }
51 | }
52 | }
53 |
54 |
55 | private void CloseButton_Click(object sender, RoutedEventArgs e)
56 | {
57 | Application.Current.Shutdown();
58 | }
59 |
60 | private void MinimizeButton_Click(object sender, RoutedEventArgs e)
61 | {
62 | //this.Hide();
63 | WindowState = WindowState.Minimized;
64 | }
65 |
66 | private void ColorZone_MouseDoubleClick(object sender, MouseButtonEventArgs e)
67 | {
68 | if (this.WindowState == WindowState.Maximized)
69 | WindowState = WindowState.Normal;
70 | else if (this.WindowState == WindowState.Normal)
71 | WindowState = WindowState.Maximized;
72 | }
73 |
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/TsubakiTranslator/OtherSettingPage.xaml:
--------------------------------------------------------------------------------
1 |
10 |
13 |
14 |
17 |
18 |
19 |
20 |
22 |
23 |
30 |
31 |
32 |
33 |
34 |
35 |
37 |
38 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
61 |
63 |
64 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
79 |
81 |
82 |
83 |
84 |
87 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/TsubakiTranslator/OtherSettingPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using System.Windows.Controls;
3 | using System.Windows.Input;
4 | using TsubakiTranslator.BasicLibrary;
5 |
6 | namespace TsubakiTranslator
7 | {
8 | ///
9 | /// OcrSettingPage.xaml 的交互逻辑
10 | ///
11 | public partial class OtherSettingPage : UserControl
12 | {
13 | private byte HotkeysModifiers { get; set; }
14 | private int HotkeysKey { get; set; }
15 | private string HotkeysText { get; set; } = "";
16 |
17 | private OtherConfig OtherConfig { get; }
18 |
19 | public OtherSettingPage()
20 | {
21 | InitializeComponent();
22 |
23 | OtherConfig = App.OtherConfig;
24 | this.DataContext = App.OtherConfig;
25 |
26 | }
27 |
28 | //快捷键复用
29 | private void HotkeyTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
30 | {
31 | e.Handled = true;
32 | HotkeysModifiers = 0;
33 | HotkeysKey = 0;
34 | HotkeysText = "";
35 | Key key = (e.Key == Key.System ? e.SystemKey : e.Key);
36 | if (key == Key.LeftShift || key == Key.RightShift
37 | || key == Key.LeftCtrl || key == Key.RightCtrl
38 | || key == Key.LeftAlt || key == Key.RightAlt
39 | || key == Key.LWin || key == Key.RWin)
40 | {
41 | return;
42 | }
43 | StringBuilder shortcutText = new StringBuilder();
44 | if ((Keyboard.Modifiers & ModifierKeys.Control) != 0)
45 | {
46 | HotkeysModifiers += 2;
47 | shortcutText.Append("Ctrl + ");
48 | }
49 | if ((Keyboard.Modifiers & ModifierKeys.Shift) != 0)
50 | {
51 | HotkeysModifiers += 4;
52 | shortcutText.Append("Shift + ");
53 | }
54 | if ((Keyboard.Modifiers & ModifierKeys.Alt) != 0)
55 | {
56 | HotkeysModifiers += 1;
57 | shortcutText.Append("Alt + ");
58 | }
59 | if (HotkeysModifiers == 0 && (key < Key.F1 || key > Key.F12))
60 | {
61 | HotkeysKey = 0;
62 | shortcutText.Clear();
63 | ((TextBox)sender).Text = HotkeysText = "";
64 | return;
65 | }
66 | HotkeysKey = KeyInterop.VirtualKeyFromKey(key);
67 | shortcutText.Append(key.ToString());
68 | ((TextBox)sender).Text = HotkeysText = shortcutText.ToString();
69 | }
70 |
71 | private void ScreenshotHotkeyTextBox_KeyUp(object sender, KeyEventArgs e)
72 | {
73 | Key key = (e.Key == Key.System ? e.SystemKey : e.Key);
74 | if (key == Key.LeftShift || key == Key.RightShift
75 | || key == Key.LeftCtrl || key == Key.RightCtrl
76 | || key == Key.LeftAlt || key == Key.RightAlt
77 | || key == Key.LWin || key == Key.RWin)
78 | {
79 | return;
80 | }
81 | App.OtherConfig.ScreenshotHotkey.Modifiers = HotkeysModifiers;
82 | App.OtherConfig.ScreenshotHotkey.Key = HotkeysKey;
83 | App.OtherConfig.ScreenshotHotkey.Text = HotkeysText.ToString();
84 | //HotKeysUtil.ReRegisterHotKey();
85 | //HotKeyConflictCheck();
86 | }
87 |
88 | //private void ScreenshotHotkeyConflictCheck()
89 | //{
90 | //this.OcrHotKeyConflictLabel.Visibility = GlobalConfig.HotKeys.Ocr.Conflict ? Visibility.Visible : Visibility.Hidden;
91 | //this.GetWordsTranslateHotKeyConflictLabel.Visibility = GlobalConfig.HotKeys.GetWordsTranslate.Conflict ? Visibility.Visible : Visibility.Hidden;
92 | //this.ScreenshotTranslateHotKeyConflictLabel.Visibility = GlobalConfig.HotKeys.ScreenshotTranslate.Conflict ? Visibility.Visible : Visibility.Hidden;
93 | //this.TopMostHotKeyConflictLabel.Visibility = GlobalConfig.HotKeys.TopMost.Conflict ? Visibility.Visible : Visibility.Hidden;
94 | //}
95 |
96 | private void SelectFolder_Button_Click(object sender, System.Windows.RoutedEventArgs e)
97 | {
98 | string path = FileHandler.SelectFolderPath();
99 |
100 | App.OtherConfig.LogFolderPath = path;
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/TsubakiTranslator/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | bin\Release\net6.0-windows\publish\win64
10 | FileSystem
11 | net6.0-windows10.0.17763.0
12 | win-x64
13 | true
14 | true
15 | false
16 | false
17 |
18 |
--------------------------------------------------------------------------------
/TsubakiTranslator/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 此代码由工具生成。
4 | // 运行时版本:4.0.30319.42000
5 | //
6 | // 对此文件的更改可能会导致不正确的行为,并且如果
7 | // 重新生成代码,这些更改将会丢失。
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace TsubakiTranslator.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// 一个强类型的资源类,用于查找本地化的字符串等。
17 | ///
18 | // 此类是由 StronglyTypedResourceBuilder
19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
21 | // (以 /str 作为命令选项),或重新生成 VS 项目。
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.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("TsubakiTranslator.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// 重写当前线程的 CurrentUICulture 属性,对
51 | /// 使用此强类型资源类的所有资源查找执行重写。
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 | /// 查找 System.Byte[] 类型的本地化资源。
65 | ///
66 | internal static byte[] Tsubaki {
67 | get {
68 | object obj = ResourceManager.GetObject("Tsubaki", resourceCulture);
69 | return ((byte[])(obj));
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/TsubakiTranslator/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\Icon\Tsubaki.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
123 |
124 |
--------------------------------------------------------------------------------
/TsubakiTranslator/Resources/Icon/Tsubaki.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Isayama-Kagura/TsubakiTranslator/21cda4d25ea61ce673fa7fa7c6149364be967dc5/TsubakiTranslator/Resources/Icon/Tsubaki.ico
--------------------------------------------------------------------------------
/TsubakiTranslator/Resources/Icon/Tsubaki.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Isayama-Kagura/TsubakiTranslator/21cda4d25ea61ce673fa7fa7c6149364be967dc5/TsubakiTranslator/Resources/Icon/Tsubaki.png
--------------------------------------------------------------------------------
/TsubakiTranslator/Resources/Textractor/x64/TextractorCLI.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Isayama-Kagura/TsubakiTranslator/21cda4d25ea61ce673fa7fa7c6149364be967dc5/TsubakiTranslator/Resources/Textractor/x64/TextractorCLI.exe
--------------------------------------------------------------------------------
/TsubakiTranslator/Resources/Textractor/x64/texthook.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Isayama-Kagura/TsubakiTranslator/21cda4d25ea61ce673fa7fa7c6149364be967dc5/TsubakiTranslator/Resources/Textractor/x64/texthook.dll
--------------------------------------------------------------------------------
/TsubakiTranslator/Resources/Textractor/x86/TextractorCLI.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Isayama-Kagura/TsubakiTranslator/21cda4d25ea61ce673fa7fa7c6149364be967dc5/TsubakiTranslator/Resources/Textractor/x86/TextractorCLI.exe
--------------------------------------------------------------------------------
/TsubakiTranslator/Resources/Textractor/x86/texthook.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Isayama-Kagura/TsubakiTranslator/21cda4d25ea61ce673fa7fa7c6149364be967dc5/TsubakiTranslator/Resources/Textractor/x86/texthook.dll
--------------------------------------------------------------------------------
/TsubakiTranslator/ScreenshotWindow.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/TsubakiTranslator/ScreenshotWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.Windows;
4 | using System.Windows.Input;
5 | using System.Windows.Interop;
6 | using TsubakiTranslator.BasicLibrary;
7 |
8 | namespace TsubakiTranslator
9 | {
10 | ///
11 | /// ScreenshotWindowTest.xaml 的交互逻辑
12 | ///
13 | public partial class ScreenshotWindow : Window
14 | {
15 | public ScreenshotWindow()
16 | {
17 | InitializeComponent();
18 | }
19 |
20 | private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
21 | {
22 | /* 如何在Window.ResizeMode属性为CanResize的时候,阻止窗口拖动到屏幕边缘自动最大化。
23 | (When the Window.ResizeMode property is CanResize,
24 | when the window is dragged to the edge of the screen,
25 | it prevents the window from automatically maximizing.)*/
26 | if (e.ChangedButton == MouseButton.Left)
27 | {
28 | if (Mouse.LeftButton == MouseButtonState.Pressed)
29 | {
30 | var windowMode = this.ResizeMode;
31 | if (this.ResizeMode != ResizeMode.NoResize)
32 | {
33 | this.ResizeMode = ResizeMode.NoResize;
34 | }
35 |
36 | this.UpdateLayout();
37 |
38 |
39 | /* 当点击拖拽区域的时候,让窗口跟着移动
40 | (When clicking the drag area, make the window follow) */
41 | DragMove();
42 |
43 |
44 | if (this.ResizeMode != windowMode)
45 | {
46 | this.ResizeMode = windowMode;
47 | }
48 |
49 | this.UpdateLayout();
50 | }
51 | }
52 |
53 | }
54 |
55 |
56 | public Bitmap TakeScreenshot()
57 | {
58 | WindowInteropHelper helper = new WindowInteropHelper(this);
59 | User32.RECT rect1;
60 | User32.GetWindowRect(HwndSource.FromHwnd(helper.Handle).Handle, out rect1);
61 |
62 | // get te hDC of the target window
63 | //IntPtr hdcSrc = User32.GetWindowDC(hWnd);
64 | // get the size
65 | User32.RECT windowRect = new User32.RECT();
66 | User32.GetWindowRect(HwndSource.FromHwnd(helper.Handle).Handle, out windowRect);
67 |
68 | var rect = new Rect(Math.Round((double)windowRect.Left, 0),
69 | Math.Round((double)windowRect.Top, 0),
70 | Math.Round((double)windowRect.Width, 0),
71 | Math.Round((double)windowRect.Height, 0));
72 |
73 | Bitmap image = ScreenshotHandler.GetCapture(rect);
74 |
75 | //image.Save(@"C:\Users\HP\Desktop\test.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
76 | return image;
77 | }
78 |
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/TsubakiTranslator/Themes/ScrollViewerStyle.xaml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
27 |
28 |
42 |
56 |
57 |
58 |
116 |
117 |
118 |
216 |
217 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/AliyunTranslator.cs:
--------------------------------------------------------------------------------
1 | using RestSharp;
2 | using System;
3 | using System.Security.Cryptography;
4 | using System.Text;
5 | using System.Text.Json;
6 | using System.Text.RegularExpressions;
7 |
8 | namespace TsubakiTranslator.TranslateAPILibrary
9 | {
10 | //API文档介绍 https://help.aliyun.com/document_detail/158269.html
11 |
12 | class AliyunTranslator : ITranslator
13 | {
14 | private readonly string name = "阿里";
15 | private readonly string[] langList = { "ja", "en" };
16 |
17 | public string Name { get => name; }
18 |
19 | private string SourceLanguage { get; set; }
20 |
21 | private string SecretId { get; set; }
22 | private string SecretKey { get; set; }
23 |
24 | public string Translate(string sourceText)
25 | {
26 | string desLang = "zh";
27 |
28 | string method = "POST";
29 | string accept = "application/json";
30 | string contentType = "application/json; charset=utf-8";
31 | string date = DateTime.UtcNow.ToString("r");
32 | string host = "mt.cn-hangzhou.aliyuncs.com";
33 | string path = "/api/translate/web/general";
34 |
35 |
36 | var body = new
37 | {
38 | FormatType = "text",
39 | Scene = "general",
40 | SourceLanguage = SourceLanguage,
41 | SourceText = sourceText,
42 | TargetLanguage = desLang,
43 | };
44 |
45 | string bodyString = JsonSerializer.Serialize(body);
46 | string bodyMd5 = string.Empty;
47 | using (MD5 md5Hash = MD5.Create())
48 | {
49 | // 将输入字符串转换为字节数组并计算哈希数据
50 | byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(bodyString));
51 | // 返回BASE64字符串
52 | bodyMd5 = Convert.ToBase64String(data);
53 | }
54 |
55 | string uuid = Guid.NewGuid().ToString();
56 |
57 | string stringToSign = method + "\n" + accept + "\n" + bodyMd5 + "\n" + contentType + "\n" + date + "\n"
58 | + "x-acs-signature-method:HMAC-SHA1\n"
59 | + "x-acs-signature-nonce:" + uuid + "\n"
60 | + path;
61 |
62 | string signature = string.Empty;
63 | using (HMACSHA1 mac = new HMACSHA1(Encoding.UTF8.GetBytes(SecretKey)))
64 | {
65 | byte[] hash = mac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign));
66 | signature = Convert.ToBase64String(hash);
67 | }
68 |
69 | string authHeader = "acs " + SecretId + ":" + signature;
70 |
71 | var client = new RestClient(CommonFunction.Client);
72 | var request = new RestRequest($"https://{host}{path}", Method.Post);
73 | request.AddHeader("Authorization", authHeader);
74 | request.AddHeader("Accept", accept);
75 | request.AddHeader("Content-MD5", bodyMd5);
76 | //request.AddHeader("Content-Type", host);
77 | request.AddHeader("Date", date);
78 | request.AddHeader("x-acs-signature-method", "HMAC-SHA1");
79 | request.AddHeader("x-acs-signature-nonce", uuid);
80 |
81 | request.AddStringBody(bodyString, DataFormat.Json);
82 |
83 |
84 | var response = client.Execute(request);
85 | string result = response.Content;
86 | if (response.IsSuccessful)
87 | {
88 | Regex codeReg = new Regex("},\"Code\":\"200\"");
89 | if (codeReg.IsMatch(result))
90 | {
91 | Regex reg = new Regex(@",""Translated"":""(.*?)""},");
92 | Match match = reg.Match(response.Content);
93 | result = match.Groups[1].Value;
94 | }
95 | return result;
96 | }
97 |
98 | else
99 | {
100 | return result;
101 | }
102 |
103 |
104 | }
105 |
106 | public void TranslatorInit(int index, string param1, string param2)
107 | {
108 | SourceLanguage = langList[index];
109 | SecretId = param1;
110 | SecretKey = param2;
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/BaiduTranslator.cs:
--------------------------------------------------------------------------------
1 |
2 | using System;
3 | using System.Net.Http;
4 | using System.Text.RegularExpressions;
5 | using System.Web;
6 |
7 | namespace TsubakiTranslator.TranslateAPILibrary
8 | {
9 | public class BaiduTranslator : ITranslator
10 | {
11 | //语言简写列表 https://api.fanyi.baidu.com/product/113
12 |
13 | private string appId;//百度翻译API 的APP ID
14 | private string secretKey;//百度翻译API 的密钥
15 |
16 | private readonly string name = "百度";
17 | private readonly string[] langList = { "jp", "en" };
18 | public string Name { get => name; }
19 |
20 | private string SourceLanguage { get; set; }
21 |
22 | public string Translate(string sourceText)
23 | {
24 |
25 | string desLang = "zh";
26 |
27 | string retString;
28 |
29 | string salt = new Random().Next(100000).ToString();
30 |
31 | string sign = CommonFunction.EncryptString(appId + sourceText + salt + secretKey);
32 |
33 |
34 | string bodyString = $"q={HttpUtility.UrlEncode(sourceText)}&from={SourceLanguage}&to={desLang}&appid={appId}&salt={salt}&sign={sign}";
35 |
36 | HttpContent content = new StringContent(bodyString);
37 | content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
38 |
39 | //var sb = new StringBuilder("https://api.fanyi.baidu.com/api/trans/vip/translate?")
40 | // .Append("q=").Append(HttpUtility.UrlEncode(q))
41 | // .Append("&from=").Append(SourceLanguage)
42 | // .Append("&to=").Append(desLang)
43 | // .Append("&appid=").Append(appId)
44 | // .Append("&salt=").Append(salt)
45 | // .Append("&sign=").Append(sign);
46 | //string url = sb.ToString();
47 |
48 | string url = @"https://api.fanyi.baidu.com/api/trans/vip/translate";
49 |
50 | var client = CommonFunction.Client;
51 | try
52 | {
53 | HttpResponseMessage response = client.PostAsync(url, content).GetAwaiter().GetResult();
54 | response.EnsureSuccessStatusCode();//用来抛异常的
55 | retString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
56 | //retString = hc.GetStringAsync(url).GetAwaiter().GetResult();
57 | }
58 | catch (System.Net.Http.HttpRequestException ex)
59 | {
60 | return ex.Message;
61 | }
62 | catch (System.Threading.Tasks.TaskCanceledException ex)
63 | {
64 | return ex.Message;
65 | }
66 |
67 | retString = Regex.Unescape(retString);
68 | Regex reg = new Regex(@""",""dst"":""(.*?)""\}");
69 | Match match = reg.Match(retString);
70 |
71 | string result = match.Groups[1].Value;
72 | result = Regex.Unescape(result);
73 | return result;
74 |
75 |
76 |
77 |
78 | }
79 |
80 | public void TranslatorInit(int index, string param1, string param2)
81 | {
82 | SourceLanguage = langList[index];
83 | appId = param1;
84 | secretKey = param2;
85 | }
86 |
87 |
88 |
89 |
90 |
91 | }
92 |
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/BingTranslator.cs:
--------------------------------------------------------------------------------
1 | using RestSharp;
2 | using System.Text.RegularExpressions;
3 | using System.Threading.Tasks;
4 |
5 | namespace TsubakiTranslator.TranslateAPILibrary
6 | {
7 | public class BingTranslator : ITranslator
8 | {
9 | private string SourceLanguage { get; set; }
10 |
11 | private readonly string name = "必应";
12 | private readonly string[] langList = { "ja", "en" };
13 | public string Name { get => name; }
14 | private string Key { get; set; }
15 | private string Token { get; set; }
16 | private string Iid { get; set; }
17 | private string Ig { get; set; }
18 | private int TranslateTimes { get; set; }
19 |
20 | private RestClient Client { get; }
21 |
22 | public BingTranslator()
23 | {
24 | Client = new RestClient(CommonFunction.Client);
25 | Task.Run(async () => await InitializeAsync());
26 | }
27 |
28 | public string Translate(string sourceText)
29 | {
30 | string desLang = "zh-Hans";
31 |
32 |
33 | string url = "https://cn.bing.com/ttranslatev3" + $"?isVertical=1&&IG={Ig}&IID={Iid}" + TranslateTimes++;
34 | var request = new RestRequest(url, Method.Post);
35 | string bodyString = $"fromLang={SourceLanguage}&text={sourceText}&to={desLang}&token={Token}&key={Key}";
36 | request.AddStringBody(bodyString, "application/x-www-form-urlencoded");
37 |
38 |
39 | var response = Client.Execute(request);
40 |
41 | string result = "";
42 |
43 | string responseBody = response.Content;
44 | if (response.IsSuccessful)
45 | {
46 | var regex = new Regex(@"""translations"":\[{""text"":""(.*?)""");
47 | var match = regex.Match(responseBody);
48 | result = match.Groups[1].Value;
49 | }
50 | else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
51 | {
52 | Task.Run(async () => await InitializeAsync());
53 | result = Translate(sourceText);
54 | }
55 | else
56 | {
57 | result = responseBody;
58 | }
59 |
60 |
61 | return result;
62 | }
63 |
64 | private async Task InitializeAsync()
65 | {
66 | string url = "https://cn.bing.com/translator";
67 | var request = new RestRequest(url, Method.Post);
68 | var response = await Client.ExecuteAsync(request);
69 | string result = response.Content;
70 | if (result == null)
71 | return;
72 |
73 | Regex regex = new Regex("params_RichTranslateHelper = \\[(.+?),\"(.+?)\",.+?");
74 | var match = regex.Match(result);
75 | Token = match.Groups[2].Value;
76 | Key = match.Groups[1].Value;
77 |
78 | regex = new Regex(@"""rich_tta"" data-iid=""(.+?)""");
79 | match = regex.Match(result);
80 | Iid = match.Groups[1].Value + ".";
81 |
82 | regex = new Regex(@""",IG:""(.+?)"",EventID:");
83 | match = regex.Match(result);
84 | Ig = match.Groups[1].Value;
85 |
86 | TranslateTimes = 0;
87 | }
88 |
89 | public void TranslatorInit(int index, string param1, string param2)
90 | {
91 | SourceLanguage = langList[index];
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/CaiyunTranslator.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Text.RegularExpressions;
3 |
4 | namespace TsubakiTranslator.TranslateAPILibrary
5 | {
6 | //API查询文档 https://fanyi.caiyunapp.com/#/api
7 | ///
8 | /// 只能中英或者中日互译
9 | ///
10 | public class CaiyunTranslator : ITranslator
11 | {
12 | private string caiyunToken;//彩云小译 令牌
13 |
14 | private readonly string name = "彩云";
15 | private readonly string[] langList = { "ja", "en" };
16 | public string Name { get => name; }
17 |
18 | private string SourceLanguage { get; set; }
19 |
20 | public string Translate(string sourceText)
21 | {
22 | string desLang = "zh";
23 |
24 | string retString;
25 |
26 |
27 | string url = "https://api.interpreter.caiyunai.com/v1/translator";
28 | //json参数
29 | string jsonParam = "{\"source\": [\"" + sourceText + "\"], \"trans_type\": \"" + $"{SourceLanguage}2{desLang}" + "\", \"request_id\": \"demo\", \"detect\": true}";
30 |
31 | var client = CommonFunction.Client;
32 |
33 | HttpContent content = new StringContent(jsonParam);
34 | content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
35 | content.Headers.Add("X-Authorization", "token " + caiyunToken);
36 |
37 | try
38 | {
39 | HttpResponseMessage response = client.PostAsync(url, content).GetAwaiter().GetResult();//改成自己的
40 | response.EnsureSuccessStatusCode();//用来抛异常的
41 | retString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
42 | }
43 | catch (System.Net.Http.HttpRequestException ex)
44 | {
45 | return ex.Message;
46 | }
47 | catch (System.Threading.Tasks.TaskCanceledException ex)
48 | {
49 | return ex.Message;
50 | }
51 |
52 | string result = Regex.Unescape(retString);
53 | Regex reg = new Regex(@"""target"":\[""(.*?)""\],");
54 | Match match = reg.Match(result);
55 |
56 | result = match.Groups[1].Value;
57 |
58 | return result;
59 |
60 | }
61 |
62 | public void TranslatorInit(int index, string param1, string param2 = "")
63 | {
64 | SourceLanguage = langList[index];
65 | caiyunToken = param1;
66 | }
67 |
68 |
69 | }
70 |
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/ChatGptTranslator.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Net.Http.Headers;
3 | using System.Text.Json;
4 | using System.Text.Json.Serialization;
5 |
6 | namespace TsubakiTranslator.TranslateAPILibrary
7 | {
8 | internal class Message
9 | {
10 | [JsonPropertyName("role")] public string Role { get; set; }
11 | [JsonPropertyName("content")] public string Content { get; set; }
12 | }
13 |
14 | internal class RequestBody
15 | {
16 | [JsonPropertyName("model")] public string Model { get; set; }
17 | [JsonPropertyName("messages")] public Message[] Messages { get; set; }
18 | [JsonPropertyName("temperature")] public int Temperature { get; set; }
19 | [JsonPropertyName("max_tokens")] public int MaxTokens { get; set; }
20 | }
21 |
22 | internal class Response
23 | {
24 | public class Usage_
25 | {
26 | [JsonPropertyName("prompt_tokens")] public int PromptTokens { get; set; }
27 | [JsonPropertyName("completion_tokens")] public int CompletionTokens { get; set; }
28 | [JsonPropertyName("total_tokens")] public int TotalTokens { get; set; }
29 | }
30 | public class Choice
31 | {
32 | [JsonPropertyName("message")] public Message Message { get; set; }
33 | [JsonPropertyName("finish_reason")] public string FinishReason { get; set; }
34 | [JsonPropertyName("index")] public int Index { get; set; }
35 | }
36 |
37 | [JsonPropertyName("id")] public string Id { get; set; }
38 | [JsonPropertyName("model")] public string Model { get; set; }
39 | [JsonPropertyName("usage")] public Usage_ Usage { get; set; }
40 | [JsonPropertyName("choices")] public Choice[] Choices { get; set; }
41 | }
42 |
43 | public class ChatGptTranslator : ITranslator
44 | {
45 | private string token;
46 | private HttpClient client;
47 | private const string Url = @"https://api.openai.com/v1/chat/completions";
48 |
49 | private RequestBody NewRequest(string content)
50 | {
51 | return new RequestBody
52 | {
53 | Messages = new Message[] {
54 | new() {
55 | Role = "system",
56 | Content = "You are a translation engine that can only translate text and cannot interpret it.",
57 | },
58 | new() {
59 | Role = "user",
60 | Content = $"translate from {SourceLanguage} to Simplified Chinese",
61 | },
62 | new() {
63 | Role = "user",
64 | Content = $"\"{content}\"",
65 | },
66 | },
67 | Model = "gpt-3.5-turbo",
68 | Temperature = 0,
69 | MaxTokens = 250,
70 | };
71 | }
72 |
73 | private readonly string[] langList = { "Japanese", "English" };
74 | private readonly string name = "ChatGPT";
75 | public string Name { get => name; }
76 |
77 | private string SourceLanguage { get; set; }
78 |
79 |
80 | public string Translate(string sourceText)
81 | {
82 | var bodyString = JsonSerializer.Serialize(NewRequest(sourceText));
83 |
84 | HttpContent content = new StringContent(bodyString);
85 | content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
86 |
87 | string respString;
88 | try
89 | {
90 | var response = client.PostAsync(Url, content).GetAwaiter().GetResult();
91 | response.EnsureSuccessStatusCode();
92 | respString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
93 | }
94 | catch (HttpRequestException ex)
95 | {
96 | return ex.Message;
97 | }
98 | catch (System.Threading.Tasks.TaskCanceledException ex)
99 | {
100 | return ex.Message;
101 | }
102 |
103 | var resp = JsonSerializer.Deserialize(respString);
104 | return resp?.Choices.Length != 0 ? resp?.Choices[0].Message.Content.Trim('"') : "";
105 | }
106 |
107 | public void TranslatorInit(int index, string param1, string _)
108 | {
109 | SourceLanguage = langList[index];
110 | token = param1;
111 | client = CommonFunction.NewClient();
112 | client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/CommonFunction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Http;
4 | using System.Net.Http.Headers;
5 | using System.Security.Cryptography;
6 | using System.Text;
7 |
8 | namespace TsubakiTranslator.TranslateAPILibrary
9 | {
10 | public static class CommonFunction
11 | {
12 |
13 |
14 | static CommonFunction()
15 | {
16 | client = new HttpClient(new HttpClientHandler()
17 | {
18 | AutomaticDecompression = DecompressionMethods.GZip
19 | });
20 | //{ Timeout = TimeSpan.FromSeconds(6) };
21 |
22 | HttpRequestHeaders headers = client.DefaultRequestHeaders;
23 | headers.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36");
24 | headers.AcceptEncoding.ParseAdd("gzip");
25 | headers.Connection.ParseAdd("keep-alive");
26 | ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
27 | }
28 |
29 | static readonly private HttpClient client;
30 | ///
31 | /// 获得HttpClient单例,第一次调用自动初始化
32 | ///
33 | public static HttpClient Client
34 | {
35 | get => client;
36 | }
37 |
38 | ///
39 | /// new http client
40 | ///
41 | public static HttpClient NewClient()
42 | {
43 | var c = new HttpClient(new HttpClientHandler()
44 | {
45 | AutomaticDecompression = DecompressionMethods.GZip
46 | });
47 | foreach (var (k, v) in client.DefaultRequestHeaders)
48 | {
49 | c.DefaultRequestHeaders.Add(k, v);
50 | }
51 | return c;
52 | }
53 |
54 | ///
55 | /// 计算MD5值
56 | ///
57 | ///
58 | ///
59 | public static string EncryptString(string str)
60 | {
61 | MD5 md5 = MD5.Create();
62 | // 将字符串转换成字节数组
63 | byte[] byteOld = Encoding.UTF8.GetBytes(str);
64 | // 调用加密方法
65 | byte[] byteNew = md5.ComputeHash(byteOld);
66 | // 将加密结果转换为字符串
67 | StringBuilder sb = new StringBuilder();
68 | foreach (byte b in byteNew)
69 | {
70 | // 将字节转换成16进制表示的字符串,
71 | sb.Append(b.ToString("x2"));
72 | }
73 | // 返回加密的字符串
74 | return sb.ToString();
75 | }
76 |
77 | ///
78 | /// 计算时间戳
79 | ///
80 | ///
81 | public static string GetTimeStamp()
82 | {
83 | TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
84 | return Convert.ToInt64(ts.TotalSeconds).ToString();
85 | }
86 |
87 |
88 |
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/DeepLTranslator.cs:
--------------------------------------------------------------------------------
1 | using RestSharp;
2 | using System.Collections.Generic;
3 | using System.Text.Json;
4 |
5 | namespace TsubakiTranslator.TranslateAPILibrary
6 | {
7 | //API文档 https://www.deepl.com/docs-api
8 | public class DeepLTranslator : ITranslator
9 | {
10 | // DeepL免费版和收费版使用不同url
11 | // https://api-free.deepl.com/v2/translate
12 | // https://api.deepl.com/v2/translate
13 | private string apiUrl;
14 | private string secretKey; //DeepL翻译API的秘钥
15 |
16 | private readonly string[] langList = { "JA", "EN" };
17 | private readonly string name = "DeepL";
18 | public string Name { get => name; }
19 |
20 | private string SourceLanguage { get; set; }
21 |
22 |
23 | public string Translate(string sourceText)
24 | {
25 | string desLang = "ZH";
26 |
27 | string resultStr;
28 |
29 | var client = new RestClient(CommonFunction.Client);
30 | var request = new RestRequest(apiUrl, Method.Post);
31 | request.AddHeader("Authorization", $"DeepL-Auth-Key {secretKey}");
32 |
33 | request.AddQueryParameter("text", sourceText).AddQueryParameter("source_lang", SourceLanguage).AddQueryParameter("target_lang", desLang);
34 |
35 | var response = client.Execute(request);
36 |
37 | resultStr = response.Content;
38 |
39 |
40 | DeepLTranslateResult translateResult = JsonSerializer.Deserialize(resultStr);
41 | if (translateResult != null && translateResult.translations != null && translateResult.translations.Count > 0)
42 | {
43 | return translateResult.translations[0].text;
44 | }
45 | else
46 | {
47 | return "Cannot get translation from: " + resultStr;
48 | }
49 | }
50 |
51 | public void TranslatorInit(int index, string param1, string param2)
52 | {
53 | SourceLanguage = langList[index];
54 | secretKey = param1;
55 | if (param2 == null)
56 | apiUrl = @"https://api-free.deepl.com/v2/translate";
57 | else
58 | apiUrl = @"https://api.deepl.com/v2/translate";
59 | }
60 |
61 | class DeepLTranslateResult
62 | {
63 | public List translations { get; set; }
64 | }
65 |
66 | class DeepLTranslations
67 | {
68 | public string detected_source_language { get; set; }
69 | public string text { get; set; }
70 | }
71 |
72 | }
73 |
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/IBMTranslator.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Text.Json;
3 | using System.Text.RegularExpressions;
4 |
5 | namespace TsubakiTranslator.TranslateAPILibrary
6 | {
7 | //API文档 https://cloud.ibm.com/docs/language-translator?topic=language-translator-translation-models
8 |
9 | public class IBMTranslator : ITranslator
10 | {
11 | private readonly string[] langList = { "ja", "en" };
12 | private readonly string name = "IBM";
13 | public string Name { get => name; }
14 |
15 | private string SourceLanguage { get; set; }
16 |
17 | public string Translate(string sourceText)
18 | {
19 | string desLang = "zh";
20 |
21 | var body = new
22 | {
23 | text = sourceText,
24 | source = SourceLanguage,
25 | target = desLang
26 | };
27 |
28 | string bodyString = JsonSerializer.Serialize(body);
29 |
30 | string url = @"https://www.ibm.com//demos/live/watson-language-translator/api/translate/text";
31 |
32 | HttpClient client = CommonFunction.Client;
33 |
34 | HttpContent content = new StringContent(bodyString);
35 | content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
36 |
37 | //HttpResponseMessage resp;
38 | //var hc = CommonFunction.Client;
39 | //var req = new HttpRequestMessage(HttpMethod.Post, URL);
40 | //req.Content = new StringContent("{\"text\":[\""+ sourceText.Replace("\"", "") +"\"],\"model_id\":\"" + SourceLanguage + "-" + desLang + "\"}",null,"application/json");
41 | //req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(ApiKey)));
42 |
43 | try
44 | {
45 | HttpResponseMessage response = client.PostAsync(url, content).GetAwaiter().GetResult();//改成自己的
46 | response.EnsureSuccessStatusCode();//用来抛异常的
47 | string responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
48 |
49 | Regex reg = new Regex(@"\[\{""translation"":""(.*?)""\}\]");
50 | Match match = reg.Match(responseBody);
51 |
52 | string result = match.Groups[1].Value;
53 | result = Regex.Unescape(result);
54 | return result;
55 | //string ret = result.payload.translations[0].translation;
56 |
57 | //return ret;
58 |
59 | //return responseBody;
60 | }
61 | catch (System.Net.Http.HttpRequestException ex)
62 | {
63 |
64 | return ex.Message;
65 | }
66 | catch (System.Threading.Tasks.TaskCanceledException ex)
67 | {
68 |
69 | return ex.Message;
70 | }
71 |
72 | }
73 |
74 | public void TranslatorInit(int index, string param1, string param2)
75 | {
76 | SourceLanguage = langList[index];
77 | }
78 |
79 |
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/ICiBaTranslator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Security.Cryptography;
4 | using System.Text;
5 | using System.Text.RegularExpressions;
6 |
7 | namespace TsubakiTranslator.TranslateAPILibrary
8 | {
9 | public class ICiBaTranslator : ITranslator
10 | {
11 | private string SourceLanguage { get; set; }
12 |
13 | private readonly string[] langList = { "ja", "en" };
14 | private readonly string name = "爱词霸";
15 | public string Name { get => name; }
16 |
17 | public string Translate(string sourceText)
18 | {
19 | string desLang = "zh";
20 |
21 | string sign;
22 |
23 | using (MD5 md5Hash = MD5.Create())
24 | {
25 | // 将输入字符串转换为字节数组并计算哈希数据
26 | byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes("6key_web_fanyiifanyiweb8hc9s98e" + sourceText.Trim()));
27 | // 返回16进制字符串
28 | sign = Convert.ToHexString(data).ToLower();
29 | }
30 |
31 | sign = sign.Substring(0, 16);
32 |
33 | string param = $"c=trans&m=fy&client=6&auth_user=key_web_fanyi&sign={sign}";
34 |
35 | var bodyString = $"from={SourceLanguage}&to={desLang}&q={sourceText}";
36 |
37 | string url = @"http://ifanyi.iciba.com/index.php" + "?" + param;
38 |
39 | var client = CommonFunction.Client;
40 |
41 | HttpContent content = new StringContent(bodyString);
42 | content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
43 |
44 | try
45 | {
46 | HttpResponseMessage response = client.PostAsync(url, content).GetAwaiter().GetResult();//改成自己的
47 | response.EnsureSuccessStatusCode();//用来抛异常的
48 | string responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
49 |
50 | Regex reg = new Regex(@",""out"":""(.*?)"",""reqid""");
51 | Match match = reg.Match(responseBody);
52 |
53 | string result = match.Groups[1].Value;
54 | result = Regex.Unescape(result);
55 | return result;
56 | //string ret = result.payload.translations[0].translation;
57 |
58 | //return ret;
59 |
60 | //return responseBody;
61 | }
62 | catch (System.Net.Http.HttpRequestException ex)
63 | {
64 |
65 | return ex.Message;
66 | }
67 | catch (System.Threading.Tasks.TaskCanceledException ex)
68 | {
69 |
70 | return ex.Message;
71 | }
72 |
73 | }
74 |
75 | public void TranslatorInit(int index, string param1, string param2)
76 | {
77 | SourceLanguage = langList[index];
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/ITranslator.cs:
--------------------------------------------------------------------------------
1 | namespace TsubakiTranslator.TranslateAPILibrary
2 | {
3 | public interface ITranslator
4 | {
5 | ///
6 | /// 翻译API初始化
7 | ///
8 | /// 参数一 源文本语言序号
9 | /// 参数二 一般是appID或者路径(为路径时参数三无效)
10 | /// 参数三 一般是密钥
11 | void TranslatorInit(int langIndex, string param1, string param2);
12 |
13 | ///
14 | /// 翻译一条语句
15 | ///
16 | /// 源文本
17 | /// 翻译后的语句,如果翻译有错误会返回空,可以通过GetLastError来获取错误
18 | string Translate(string sourceText);
19 |
20 | string Name { get; }
21 |
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/TencentTranslator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Net.Http;
4 | using System.Security.Cryptography;
5 | using System.Text;
6 | using System.Text.Json;
7 | using System.Threading.Tasks;
8 | using System.Web;
9 |
10 | namespace TsubakiTranslator.TranslateAPILibrary
11 | {
12 | //API文档 https://cloud.tencent.com/document/product/551/15614
13 | public class TencentTranslator : ITranslator
14 | {
15 |
16 | private string SecretId { get; set; }//腾讯翻译API SecretId
17 | private string SecretKey { get; set; }//腾讯翻译API SecretKey
18 |
19 | private readonly string[] langList = { "ja", "en" };
20 | private readonly string name = "腾讯";
21 | public string Name { get => name; }
22 |
23 | private string SourceLanguage { get; set; }
24 |
25 | public string Translate(string sourceText)
26 | {
27 | string desLang = "zh";
28 |
29 | string retString;
30 |
31 | string salt = new Random().Next(100000).ToString();
32 |
33 | string url = "https://tmt.tencentcloudapi.com/";
34 |
35 | string action = "TextTranslate";
36 | string version = "2018-03-21";
37 |
38 | // long timestamp = ToTimestamp() / 1000;
39 | // string requestTimestamp = timestamp.ToString();
40 | Dictionary param = new Dictionary();
41 | param.Add("Nonce", salt);
42 | param.Add("ProjectId", "0");
43 | param.Add("SourceText", sourceText);
44 | param.Add("Region", "ap-guangzhou");
45 | param.Add("Action", action);
46 | param.Add("Source", SourceLanguage);
47 | // param.Add("Nonce", Math.Abs(new Random().Next()).ToString());
48 | param.Add("Timestamp", CommonFunction.GetTimeStamp());
49 | param.Add("Version", version);
50 | param.Add("Target", desLang);
51 | param.Add("SecretId", SecretId);
52 |
53 | SortedDictionary headers = new SortedDictionary(param, StringComparer.Ordinal);
54 |
55 |
56 |
57 |
58 | //string sigInParam = MakeSignPlainText(headers, "GET", endpoint, "/");
59 | string retStr = "POSTtmt.tencentcloudapi.com/?";
60 | string body = "";
61 | foreach (string key in headers.Keys)
62 | {
63 | body += string.Format("{0}={1}&", key, headers[key]);
64 | }
65 | body = body.TrimEnd('&');
66 |
67 | retStr += body;
68 | string sigInParam = retStr;
69 |
70 | //string sigOutParam = Sign(SecretKey, sigInParam);
71 | string signRet = string.Empty;
72 | using (HMACSHA1 mac = new HMACSHA1(Encoding.UTF8.GetBytes(SecretKey)))
73 | {
74 | byte[] hash = mac.ComputeHash(Encoding.UTF8.GetBytes(sigInParam));
75 | signRet = Convert.ToBase64String(hash);
76 | }
77 | string sigOutParam = signRet;
78 |
79 | //req = req + "&Signature=" + HttpUtility.UrlEncode(Convert.ToBase64String(result));
80 | body = body + "&Signature=" + HttpUtility.UrlEncode(sigOutParam);
81 |
82 | HttpContent content = new StringContent(body);
83 | content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
84 |
85 |
86 | var client = CommonFunction.Client;
87 |
88 |
89 | try
90 | {
91 | HttpResponseMessage response = client.PostAsync(url, content).GetAwaiter().GetResult();//改成自己的
92 | response.EnsureSuccessStatusCode();//用来抛异常的
93 | retString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
94 |
95 | }
96 | catch (System.Net.Http.HttpRequestException ex)
97 | {
98 | return ex.Message;
99 | }
100 | catch (TaskCanceledException ex)
101 | {
102 | return ex.Message;
103 | }
104 |
105 | TencentOldTransOutInfo oinfo = JsonSerializer.Deserialize(retString);
106 |
107 | if (oinfo.Response.Error == null)
108 | //得到翻译结果
109 | return oinfo.Response.TargetText;
110 | else
111 | return "ErrorID:" + oinfo.Response.Error.Code + " ErrorInfo:" + oinfo.Response.Error.Message;
112 |
113 | }
114 |
115 | public void TranslatorInit(int index, string param1, string param2)
116 | {
117 | SourceLanguage = langList[index];
118 | SecretId = param1;
119 | SecretKey = param2;
120 | }
121 |
122 | class TencentOldTransOutInfo
123 | {
124 | public TencentOldTransResult Response { get; set; }
125 | }
126 |
127 | class TencentOldTransResult
128 | {
129 | public string RequestId { get; set; }
130 | public string TargetText { get; set; }
131 | public string Source { get; set; }
132 | public string Target { get; set; }
133 | public TencentOldTransOutError Error { get; set; }
134 | }
135 |
136 | class TencentOldTransOutError
137 | {
138 | public string Code { get; set; }
139 | public string Message { get; set; }
140 | }
141 |
142 | }
143 |
144 |
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/VolcengineTranslator.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Text.Json;
3 | using System.Text.RegularExpressions;
4 |
5 | namespace TsubakiTranslator.TranslateAPILibrary
6 | {
7 | class VolcengineTranslator : ITranslator
8 | {
9 | private readonly string[] langList = { "ja", "en" };
10 | private readonly string name = "火山";
11 | public string Name { get => name; }
12 |
13 | private string SourceLanguage { get; set; }
14 |
15 | public string Translate(string sourceText)
16 | {
17 | string desLang = "zh";
18 |
19 | var body = new
20 | {
21 | text = sourceText,
22 | source = SourceLanguage,
23 | target = desLang
24 | };
25 |
26 | string bodyString = JsonSerializer.Serialize(body);
27 |
28 | string url = @"https://www.volcengine.com/api/exp/2/model-ii";
29 |
30 | HttpClient client = CommonFunction.Client;
31 |
32 | HttpContent content = new StringContent(bodyString);
33 | content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
34 |
35 | //HttpResponseMessage resp;
36 | //var hc = CommonFunction.Client;
37 | //var req = new HttpRequestMessage(HttpMethod.Post, URL);
38 | //req.Content = new StringContent("{\"text\":[\""+ sourceText.Replace("\"", "") +"\"],\"model_id\":\"" + SourceLanguage + "-" + desLang + "\"}",null,"application/json");
39 | //req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(ApiKey)));
40 |
41 | try
42 | {
43 | HttpResponseMessage response = client.PostAsync(url, content).GetAwaiter().GetResult();//改成自己的
44 | response.EnsureSuccessStatusCode();//用来抛异常的
45 | string responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
46 |
47 | Regex reg = new Regex(@"\[\{""Translation"":""(.*?)"",""DetectedSourceLanguage""");
48 | Match match = reg.Match(responseBody);
49 |
50 | string result = match.Groups[1].Value;
51 | result = Regex.Unescape(result);
52 | return result;
53 | //string ret = result.payload.translations[0].translation;
54 |
55 | //return ret;
56 |
57 | //return responseBody;
58 | }
59 | catch (System.Net.Http.HttpRequestException ex)
60 | {
61 |
62 | return ex.Message;
63 | }
64 | catch (System.Threading.Tasks.TaskCanceledException ex)
65 | {
66 |
67 | return ex.Message;
68 | }
69 |
70 | }
71 |
72 | public void TranslatorInit(int index, string param1, string param2)
73 | {
74 | SourceLanguage = langList[index];
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/XiaoniuTranslator.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Text.Json;
3 |
4 | namespace TsubakiTranslator.TranslateAPILibrary
5 | {
6 | //API文档 https://niutrans.com/documents/contents/trans_text
7 | public class XiaoniuTranslator : ITranslator
8 | {
9 | private readonly string[] langList = { "ja", "en" };
10 | private readonly string name = "小牛";
11 |
12 | private string ApiKey;
13 | public string Name { get => name; }
14 |
15 | private string SourceLanguage { get; set; }
16 |
17 | public string Translate(string sourceText)
18 | {
19 | string desLang = "zh";
20 |
21 |
22 | string retString;
23 |
24 | var body = new
25 | {
26 | from = SourceLanguage,
27 | to = desLang,
28 | apikey = ApiKey,
29 | src_text = sourceText,
30 | };
31 |
32 | string bodyString = JsonSerializer.Serialize(body);
33 |
34 | string url = @"https://api.niutrans.com/NiuTransServer/translation";
35 |
36 | HttpClient client = CommonFunction.Client;
37 |
38 | HttpContent content = new StringContent(bodyString);
39 | content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
40 |
41 |
42 | try
43 | {
44 | HttpResponseMessage response = client.PostAsync(url, content).GetAwaiter().GetResult();//改成自己的
45 | response.EnsureSuccessStatusCode();//用来抛异常的
46 | retString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
47 | }
48 | catch (System.Net.Http.HttpRequestException ex)
49 | {
50 | return ex.Message;
51 | }
52 | catch (System.Threading.Tasks.TaskCanceledException ex)
53 | {
54 | return ex.Message;
55 | }
56 |
57 | XiaoniuTransOutInfo oinfo = JsonSerializer.Deserialize(retString);
58 |
59 | if (oinfo.error_code == null)
60 | return oinfo.tgt_text;
61 | else
62 | return oinfo.error_msg;
63 |
64 |
65 |
66 | }
67 |
68 | public void TranslatorInit(int index, string param1, string param2)
69 | {
70 | SourceLanguage = langList[index];
71 | ApiKey = param1;
72 | }
73 |
74 | class XiaoniuTransOutInfo
75 | {
76 | public string from { get; set; }
77 | public string to { get; set; }
78 | public string src_text { get; set; }
79 | public string tgt_text { get; set; }
80 | public string error_code { get; set; }
81 | public string error_msg { get; set; }
82 | }
83 |
84 | }
85 |
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateAPILibrary/YeekitTranslator.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Text.RegularExpressions;
3 | using System.Web;
4 |
5 | namespace TsubakiTranslator.TranslateAPILibrary
6 | {
7 | public class YeekitTranslator : ITranslator
8 | {
9 | private readonly string[] langList = { "nja", "nen" };
10 | private readonly string name = "Yeekit";
11 | public string Name { get => name; }
12 |
13 | private string SourceLanguage { get; set; }
14 |
15 | public string Translate(string sourceText)
16 | {
17 | string desLang = "nzh";
18 |
19 | //var body = new
20 | //{
21 | // srcl = SourceLanguage,
22 | // tgtl = desLang,
23 | // app_source = 9001,
24 | // text = sourceText,
25 | // domain = "auto"
26 | //};
27 |
28 | //string bodyString = JsonSerializer.Serialize(body);
29 |
30 | string bodyString = $"content[]={HttpUtility.UrlEncode(sourceText)}&sourceLang={SourceLanguage}&targetLang={desLang}";
31 |
32 | HttpContent content = new StringContent(bodyString);
33 | content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
34 |
35 | string url = @"https://www.yeekit.com/site/dotranslate";
36 |
37 | HttpClient client = CommonFunction.Client;
38 |
39 | try
40 | {
41 | HttpResponseMessage response = client.PostAsync(url, content).GetAwaiter().GetResult();//改成自己的
42 | response.EnsureSuccessStatusCode();//用来抛异常的
43 | string responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
44 |
45 | responseBody = responseBody.Replace(@"\n", string.Empty).Replace(" ", string.Empty);
46 |
47 | responseBody = Regex.Unescape(responseBody);
48 |
49 | Regex reg = new Regex(@"""text"":""(.*?)"",""translatetime""");
50 | Match match = reg.Match(responseBody);
51 |
52 | string result = match.Groups[1].Value;
53 |
54 | return result;
55 | }
56 | catch (System.Net.Http.HttpRequestException ex)
57 | {
58 | return ex.Message;
59 | }
60 | catch (System.Threading.Tasks.TaskCanceledException ex)
61 | {
62 | return ex.Message;
63 | }
64 | }
65 |
66 | public void TranslatorInit(int index, string param1, string param2)
67 | {
68 | SourceLanguage = langList[index];
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslateWindow.xaml:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
36 |
37 |
42 |
43 |
47 |
48 |
56 |
57 |
61 |
62 |
66 |
67 |
71 |
72 |
76 |
77 |
81 |
82 |
87 |
88 |
93 |
94 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslatedResultDisplay.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
18 |
19 |
23 |
24 |
25 |
26 |
27 |
31 |
35 |
36 |
37 |
38 |
40 |
41 |
44 |
45 |
46 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslatedResultDisplay.xaml.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text.RegularExpressions;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using System.Windows.Media;
9 | using TsubakiTranslator.BasicLibrary;
10 | using TsubakiTranslator.TranslateAPILibrary;
11 |
12 | namespace TsubakiTranslator
13 | {
14 | ///
15 | /// TranslatedResultDisplay.xaml 的交互逻辑
16 | ///
17 | public partial class TranslatedResultDisplay : UserControl
18 | {
19 | SourceTextContent sourceTextContent;
20 | Dictionary displayTextContent;
21 |
22 | LinkedList translators;
23 |
24 | private TranslateDataList results;
25 | public TranslateDataList Results { get => results; }
26 |
27 | SourceTextHandler sourceTextHandler;
28 |
29 | ClipboardHookHandler clipboardHookHandler;
30 |
31 | public bool TranslatorEnabled { get; set; } = true;
32 |
33 | private void Init()
34 | {
35 | SourceText.Foreground = new SolidColorBrush(App.WindowConfig.SourceTextColor);
36 | SourceText.FontFamily = new FontFamily(App.WindowConfig.SourceTextFontFamily);
37 |
38 | translators = TranslateHandler.GetSelectedTranslators(App.TranslateAPIConfig, App.OtherConfig.SourceLangIndex);
39 |
40 | displayTextContent = new Dictionary();
41 | foreach (ITranslator t in translators)
42 | {
43 | TranslatedResultItem resultItem = new TranslatedResultItem(t.Name, "");
44 |
45 | if (!App.WindowConfig.TranslatorNameVisibility)
46 | resultItem.APINameTextBlock.Visibility = Visibility.Collapsed;
47 |
48 | resultItem.ResultTextBlock.Foreground = new SolidColorBrush(App.WindowConfig.TranslatedTextColor);
49 | resultItem.ResultTextBlock.FontFamily = new FontFamily(App.WindowConfig.TranslatedTextFontFamily);
50 | TranslateResultPanel.Children.Add(resultItem);
51 | displayTextContent.Add(t.Name, resultItem.TranslatedData);
52 | }
53 |
54 |
55 | sourceTextContent = new SourceTextContent();
56 | this.DataContext = sourceTextContent;
57 |
58 | if (App.OtherConfig.SaveLogEnabled)
59 | results = new TranslateDataList(40, App.OtherConfig.LogFolderPath);
60 | else
61 | //最多保留40条历史记录
62 | results = new TranslateDataList(40);
63 | }
64 |
65 | //对应注入模式
66 | public TranslatedResultDisplay(SourceTextHandler sourceTextHandler)
67 | {
68 | InitializeComponent();
69 |
70 | Init();
71 |
72 | this.sourceTextHandler = sourceTextHandler;
73 |
74 | }
75 |
76 | //对应剪切板翻译模式
77 | public TranslatedResultDisplay(ClipboardHookHandler clipboardHookHandler, SourceTextHandler sourceTextHandler)
78 | {
79 | InitializeComponent();
80 |
81 | Init();
82 |
83 | this.clipboardHookHandler = clipboardHookHandler;
84 |
85 | this.sourceTextHandler = sourceTextHandler;
86 |
87 | this.clipboardHookHandler.ClipboardUpdated += TranslteClipboardText;
88 | }
89 |
90 | //对应OCR翻译模式
91 | public TranslatedResultDisplay()
92 | {
93 | InitializeComponent();
94 |
95 | Init();
96 |
97 | }
98 |
99 |
100 | public void TranslateHookText(string text)
101 | {
102 | string sourceText = sourceTextHandler.HandleText(text);
103 |
104 | if (Regex.Replace(sourceText, @"\s", "").Equals(""))
105 | return;
106 |
107 |
108 | Task.Run(() => TranslateAndDisplay(sourceText));
109 | }
110 |
111 | public void TranslteClipboardText(object sender, EventArgs e)
112 | {
113 | if (!TranslatorEnabled)
114 | return;
115 |
116 | IDataObject iData = Clipboard.GetDataObject();
117 |
118 | if (!iData.GetDataPresent(DataFormats.Text))
119 | return;
120 |
121 | string sourceText = Clipboard.GetText();
122 | sourceText = Regex.Replace(sourceText, @"[\r\n\t\f]", "");
123 | sourceText = sourceTextHandler.HandleText(sourceText);
124 | Task.Run(() => TranslateAndDisplay(sourceText));
125 | }
126 |
127 |
128 |
129 | public void TranslateAndDisplay(string sourceText)
130 | {
131 | TranslateData currentResult = new TranslateData(sourceText, new Dictionary());
132 | Results.AddTranslateData(currentResult);
133 |
134 | sourceTextContent.BindingText = currentResult.SourceText;
135 |
136 | foreach (var key in displayTextContent.Keys)
137 | {
138 | displayTextContent[key].TranslatedResult = "";
139 | currentResult.ResultText.Add(key, "");
140 | }
141 |
142 |
143 | Parallel.ForEach(translators,
144 | t =>
145 | {
146 | string result = t.Translate(currentResult.SourceText);
147 | currentResult.ResultText[t.Name] = result;
148 | displayTextContent[t.Name].TranslatedResult = result;
149 | });
150 | }
151 |
152 | class SourceTextContent : ObservableObject
153 | {
154 |
155 | private string text;
156 |
157 | public string BindingText
158 | {
159 | get => text;
160 | set => SetProperty(ref text, value);
161 | }
162 |
163 | }
164 |
165 |
166 |
167 | private void ArrowLeft_Button_Click(object sender, RoutedEventArgs e)
168 | {
169 | if (Results.Count() == 0)
170 | return;
171 | TranslateData result = Results.GetPreviousData();
172 | ShowTranslateResult(result);
173 | }
174 |
175 | private void ArrowRight_Button_Click(object sender, RoutedEventArgs e)
176 | {
177 | if (Results.Count() == 0)
178 | return;
179 | TranslateData result = Results.GetNextData();
180 | ShowTranslateResult(result);
181 | }
182 |
183 | private void ChevronTripleLeft_Button_Click(object sender, RoutedEventArgs e)
184 | {
185 | if (Results.Count() == 0)
186 | return;
187 | TranslateData result = Results.GetFirstData();
188 | ShowTranslateResult(result);
189 |
190 | }
191 |
192 | private void ChevronTripleRight_Button_Click(object sender, RoutedEventArgs e)
193 | {
194 | if (Results.Count() == 0)
195 | return;
196 | TranslateData result = Results.GetLastData();
197 | ShowTranslateResult(result);
198 | }
199 |
200 | private void ShowTranslateResult(TranslateData data)
201 | {
202 | sourceTextContent.BindingText = data.SourceText;
203 |
204 | foreach (ITranslator t in translators)
205 | displayTextContent[t.Name].TranslatedResult = data.ResultText[t.Name];
206 |
207 | }
208 |
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslatedResultItem.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
16 |
17 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TranslatedResultItem.xaml.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using System.Windows.Controls;
3 |
4 | namespace TsubakiTranslator
5 | {
6 | ///
7 | /// TranslatedResultDisplay.xaml 的交互逻辑
8 | ///
9 | public partial class TranslatedResultItem : UserControl
10 | {
11 | public TranslatedData TranslatedData { get; }
12 |
13 | public TranslatedResultItem(string apiName, string translatedResult)
14 | {
15 | InitializeComponent();
16 |
17 | TranslatedData = new TranslatedData();
18 | TranslatedData.TranslatorName = apiName;
19 | TranslatedData.TranslatedResult = translatedResult;
20 |
21 | this.DataContext = TranslatedData;
22 | }
23 |
24 | public void DecreaseFontSize()
25 | {
26 | APINameTextBlock.FontSize--;
27 | ResultTextBlock.FontSize--;
28 | }
29 |
30 | public void IncreaseFontSize()
31 | {
32 | APINameTextBlock.FontSize++;
33 | ResultTextBlock.FontSize++;
34 | }
35 |
36 | }
37 |
38 | public class TranslatedData : ObservableObject
39 | {
40 |
41 | private string translatorName;
42 | private string translatedResult;
43 |
44 | public string TranslatorName
45 | {
46 | get => translatorName;
47 | set => SetProperty(ref translatorName, value);
48 | }
49 | public string TranslatedResult
50 | {
51 | get => translatedResult;
52 | set => SetProperty(ref translatedResult, value);
53 | }
54 |
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TsubakiTranslator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net6.0-windows10.0.17763.0
6 | net6.0-windows7.0
7 | True
8 | true
9 | Resources\Icon\Tsubaki.ico
10 |
11 | true
12 |
13 |
14 | Isayama_Kagura
15 | OTAKU Technology Co., Ltd.
16 | https://github.com/Isayama-Kagura/TsubakiTranslator
17 | © 2021 Tsubaki Translator
18 | 1.0.6.2
19 | Tsubaki Translator
20 | 7.0
21 | true
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | True
43 | True
44 | Resources.resx
45 |
46 |
47 |
48 |
49 |
50 | ResXFileCodeGenerator
51 | Resources.Designer.cs
52 |
53 |
54 |
55 |
56 |
57 | Always
58 |
59 |
60 | Always
61 |
62 |
63 | Always
64 |
65 |
66 | Always
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/TsubakiTranslator/TsubakiTranslator.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <_LastSelectedProfileId>D:\kagura\TsubakiTranslator\TsubakiTranslator\Properties\PublishProfiles\FolderProfile.pubxml
5 |
6 |
7 |
8 | Designer
9 |
10 |
11 |
12 |
13 | Code
14 |
15 |
16 | Code
17 |
18 |
19 | Code
20 |
21 |
22 | Code
23 |
24 |
25 | Code
26 |
27 |
28 | Code
29 |
30 |
31 | Code
32 |
33 |
34 | Code
35 |
36 |
37 | Code
38 |
39 |
40 | Code
41 |
42 |
43 |
44 |
45 | Designer
46 |
47 |
48 | Designer
49 |
50 |
51 | Designer
52 |
53 |
54 | Designer
55 |
56 |
57 | Designer
58 |
59 |
60 | Designer
61 |
62 |
63 | Designer
64 |
65 |
66 | Designer
67 |
68 |
69 | Designer
70 |
71 |
72 | Designer
73 |
74 |
75 | Designer
76 |
77 |
78 | Designer
79 |
80 |
81 |
--------------------------------------------------------------------------------
/TsubakiTranslator/UserConfigPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Controls;
2 |
3 | namespace TsubakiTranslator
4 | {
5 | ///
6 | /// UserConfig.xaml 的交互逻辑
7 | ///
8 | public partial class UserConfigPage : UserControl
9 | {
10 |
11 | public UserConfigPage()
12 | {
13 | InitializeComponent();
14 | this.DataContext = App.TranslateAPIConfig;
15 | }
16 |
17 | //private void SelectPath_Button_Click(object sender, System.Windows.RoutedEventArgs e)
18 | //{
19 | // string path = FileHandler.SelectPath();
20 | // TranslateAPIConfig.LEPath = path;
21 | //}
22 | }
23 |
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/TsubakiTranslator/UserGamePage.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Runtime.Versioning;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 | using TsubakiTranslator.BasicLibrary;
10 |
11 |
12 | namespace TsubakiTranslator
13 | {
14 | ///
15 | /// UserConfig.xaml 的交互逻辑
16 | ///
17 | public partial class UserGamePage : UserControl
18 | {
19 |
20 | //public GamesConfig GamesConfig { get; }
21 |
22 | private class GameProcess
23 | {
24 | public int PID { get; set; }
25 | public string ProcessName { get; set; }
26 | public string ProcessDetail { get; set; }
27 | }
28 |
29 | public UserGamePage()
30 | {
31 | InitializeComponent();
32 |
33 | this.DataContext = App.GamesConfig;
34 | }
35 |
36 |
37 | private void DeleteGame_Button_Click(object sender, System.Windows.RoutedEventArgs e)
38 | {
39 | GameData item = (GameData)GameListDataGrid.SelectedItem;
40 | App.GamesConfig.GameDatas.Remove(item);
41 | }
42 |
43 | private void OpenHistoryGame_Button_Click(object sender, RoutedEventArgs e)
44 | {
45 | var list = GetGameProcesses();
46 | HistoryGameProcessList.ItemsSource = list;
47 |
48 | GameData item = (GameData)GameListDataGrid.SelectedItem;
49 |
50 | //刷新上下文
51 | HistoryGameInfo.DataContext = null;
52 | HistoryGameInfo.DataContext = item;
53 |
54 | }
55 |
56 | //历史游戏记录中打开游戏
57 | private void AcceptGame_Button_Click(object sender, System.Windows.RoutedEventArgs e)
58 | {
59 | GameData item = (GameData)GameListDataGrid.SelectedItem;
60 |
61 | item.GameName = HistoryGameName.Text;
62 | item.HookCode = HistoryHookCode.Text;
63 | int.TryParse(HistoryDuplicateTimes.Text, out int times);
64 | item.DuplicateTimes = times;
65 |
66 | GameProcess processInfo = (GameProcess)HistoryGameProcessList.SelectedItem;
67 |
68 | if (processInfo == null)
69 | return;
70 |
71 | item.ProcessName = processInfo.ProcessName;
72 |
73 | Process gameProcess = Process.GetProcessById(processInfo.PID);
74 |
75 | TextHookHandler textHookHandler;
76 | if (item.HookCode != null && item.HookCode.Trim().Length != 0)
77 | textHookHandler = new TextHookHandler(gameProcess, item.HookCode);
78 | else
79 | textHookHandler = new TextHookHandler(gameProcess, null);
80 |
81 | LinkedList regexRules = new LinkedList();
82 | foreach (var rule in item.RegexRuleItems)
83 | regexRules.AddLast(rule);
84 | SourceTextHandler sourceTextHandler = new SourceTextHandler(item.DuplicateTimes, regexRules);
85 |
86 | Window mainWindow = Window.GetWindow(this);
87 | mainWindow.Hide();
88 | TranslateWindow translateWindow = new TranslateWindow(mainWindow, textHookHandler, sourceTextHandler);
89 | translateWindow.Show();
90 |
91 | }
92 |
93 | private void OpenGameByPid_Button_Click(object sender, RoutedEventArgs e)
94 | {
95 | var list = GetGameProcesses();
96 | GameProcessList.ItemsSource = list;
97 | }
98 |
99 | //注入进程打开游戏
100 | private void AcceptProcess_Button_Click(object sender, RoutedEventArgs e)
101 | {
102 | GameProcess processInfo = (GameProcess)GameProcessList.SelectedItem;
103 | if (processInfo == null)
104 | return;
105 |
106 | Process gameProcess = Process.GetProcessById(processInfo.PID);
107 |
108 | int.TryParse(GameProcessDuplicateTimes.Text, out int times);
109 |
110 | var result = from data in App.GamesConfig.GameDatas
111 | where data.ProcessName.Equals(gameProcess.ProcessName)
112 | select data;
113 |
114 | if (result.Count() == 0)
115 | {
116 | GameData item = new GameData
117 | {
118 | HookCode = GameProcessHookCode.Text,
119 | DuplicateTimes = times,
120 | GameName = Path.GetFileName(gameProcess.MainModule.FileVersionInfo.FileName),
121 | ProcessName = gameProcess.ProcessName
122 | };
123 |
124 | App.GamesConfig.GameDatas.Add(item);
125 | }
126 |
127 | TextHookHandler textHookHandler;
128 | if (GameProcessHookCode.Text != null && GameProcessHookCode.Text.Trim().Length != 0)
129 | textHookHandler = new TextHookHandler(gameProcess, GameProcessHookCode.Text);
130 | else
131 | textHookHandler = new TextHookHandler(gameProcess, null);
132 |
133 | SourceTextHandler sourceTextHandler = new SourceTextHandler(times, new LinkedList());
134 |
135 | Window mainWindow = Window.GetWindow(this);
136 | mainWindow.Hide();
137 | TranslateWindow translateWindow = new TranslateWindow(mainWindow, textHookHandler, sourceTextHandler);
138 | translateWindow.Show();
139 |
140 | }
141 |
142 | private void AddRegexRule_Button_Click(object sender, RoutedEventArgs e)
143 | {
144 | var btn = sender as Button;
145 | var data = btn.DataContext as GameData;
146 |
147 | data.RegexRuleItems.Add(new RegexRuleData("", ""));
148 | }
149 |
150 | private void RemoveRegexRule_Button_Click(object sender, RoutedEventArgs e)
151 | {
152 | var btn = sender as Button;
153 | var data = btn.DataContext as GameData;
154 |
155 | data.RegexRuleItems.Clear();
156 | }
157 |
158 | //剪切板模式
159 | private void MonitorClipBoard_Button_Click(object sender, RoutedEventArgs e)
160 | {
161 | LinkedList regexRules = new LinkedList();
162 | foreach (var rule in App.GamesConfig.ClipBoardRegexRules)
163 | regexRules.AddLast(rule);
164 | SourceTextHandler sourceTextHandler = new SourceTextHandler(1, regexRules);
165 |
166 |
167 | Window mainWindow = Window.GetWindow(this);
168 | mainWindow.Hide();
169 | TranslateWindow translateWindow = new TranslateWindow(mainWindow, sourceTextHandler);
170 | translateWindow.Show();
171 | }
172 |
173 | private void Clipboard_AddRegexRule_Button_Click(object sender, RoutedEventArgs e)
174 | {
175 | App.GamesConfig.ClipBoardRegexRules.Add(new RegexRuleData("", ""));
176 | }
177 |
178 | private void Clipboard_RemoveRegexRule_Button_Click(object sender, RoutedEventArgs e)
179 | {
180 | App.GamesConfig.ClipBoardRegexRules.Clear();
181 | }
182 |
183 |
184 | //OCR模式
185 | private void OcrModeConfirm_Button_Click(object sender, RoutedEventArgs e)
186 | {
187 | Window mainWindow = Window.GetWindow(this);
188 | mainWindow.Hide();
189 | TranslateWindow translateWindow = new TranslateWindow(mainWindow);
190 | translateWindow.Show();
191 | }
192 |
193 | [SupportedOSPlatform("windows10.0.10240")]
194 | private void Ocr_Button_Click(object sender, RoutedEventArgs e)
195 | {
196 | OcrTipsPanel.Children.Clear();
197 |
198 | var languagesName = OcrProgram.GetSupportedLanguages();
199 | foreach (string lang in languagesName)
200 | {
201 | TextBlock tb = new TextBlock();
202 | tb.Text = lang;
203 | OcrTipsPanel.Children.Add(tb);
204 | }
205 | }
206 |
207 | private void GameProcessList_LostFocus(object sender, RoutedEventArgs e)
208 | {
209 | string text = ((ComboBox)sender).Text;
210 | if (!text.Equals(""))
211 | {
212 | bool flag = false;
213 | var list = GetGameProcesses();
214 |
215 | foreach (GameProcess gameProcess in list)
216 | {
217 | if (gameProcess.ProcessDetail.Equals(text))
218 | {
219 | flag = true;
220 | break;
221 | }
222 | }
223 |
224 | if (!flag)
225 | {
226 | ((ComboBox)sender).Text = "";
227 | }
228 | }
229 |
230 |
231 | }
232 |
233 | private void GameProcessList_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
234 | {
235 | string text = ((ComboBox)sender).Text;
236 | ((ComboBox)sender).ItemsSource = null;
237 | var list = GetGameProcesses();
238 | ObservableCollection processStrings = new ObservableCollection();
239 | if (!text.Equals(""))
240 | {
241 | foreach (GameProcess gameProcess in list)
242 | {
243 | if (gameProcess.ProcessDetail.Contains(text))
244 | {
245 | processStrings.Add(gameProcess);
246 | }
247 | }
248 | ((ComboBox)sender).ItemsSource = processStrings;
249 | }
250 | else
251 | ((ComboBox)sender).ItemsSource = list;
252 |
253 | ((ComboBox)sender).IsDropDownOpen = true;
254 | }
255 |
256 |
257 | private ObservableCollection GetGameProcesses()
258 | {
259 | Process[] ps = Process.GetProcesses();
260 | List list = new List();
261 |
262 | foreach (Process p in ps)
263 | {
264 | GameProcess gameProcess = new GameProcess { PID = p.Id, ProcessName = p.ProcessName, ProcessDetail = $"{p.ProcessName} - {p.Id}" };
265 | list.Add(gameProcess);
266 | }
267 | list.Sort((x, y) => string.Compare(x.ProcessName, y.ProcessName));
268 | ObservableCollection processProcesses = new ObservableCollection(list);
269 |
270 | return processProcesses;
271 | }
272 |
273 | private void GameProcessList_GotFocus(object sender, RoutedEventArgs e)
274 | {
275 | ((ComboBox)sender).IsDropDownOpen = true;
276 | }
277 | }
278 |
279 |
280 | }
281 |
--------------------------------------------------------------------------------
/TsubakiTranslator/WinStylePage.xaml:
--------------------------------------------------------------------------------
1 |
10 |
13 |
14 |
16 |
17 |
18 |
19 |
25 |
26 |
27 |
28 |
29 |
31 |
32 |
33 |
37 |
41 |
42 |
43 |
44 |
45 |
50 |
55 |
56 |
57 |
58 |
59 |
60 |
62 |
64 |
65 |
66 |
67 |
68 |
69 |
71 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/TsubakiTranslator/WinStylePage.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Windows;
4 | using System.Windows.Controls;
5 | using System.Windows.Markup;
6 | using System.Windows.Media;
7 |
8 | namespace TsubakiTranslator
9 | {
10 | ///
11 | /// WinStylePage.xaml 的交互逻辑
12 | ///
13 | public partial class WinStylePage : UserControl
14 | {
15 |
16 | public WinStylePage()
17 | {
18 | InitializeComponent();
19 |
20 | SetTextColorList();
21 |
22 | SetTextFontFamilyList();
23 | }
24 |
25 | private void SetTextColorList()
26 | {
27 | List