├── .github └── FUNDING.yml ├── ACT.FFXIVTranslate ├── RTFLib │ ├── RTF │ │ ├── IRtfProcessor.cs │ │ ├── RTFBorderSide.cs │ │ ├── RTFFont.cs │ │ ├── IRTFCell.cs │ │ ├── IRTFRow.cs │ │ ├── RTFAlignment.cs │ │ ├── RTFBuilder.UnWrapped.cs │ │ ├── RTFUtil.cs │ │ ├── RTFBuilder.RTFParaWrap.cs │ │ ├── RTFCell.cs │ │ ├── RTFRowDefinition.cs │ │ ├── RTFCellDefinition.cs │ │ ├── RTFBuilder.RTFFonts.cs │ │ ├── RTFCellDefinitionBuilder.cs │ │ ├── RTFBuilder.RTFFormatWrap.cs │ │ └── RTFBuilder.cs │ ├── RTFLib.sln │ ├── Properties │ │ └── AssemblyInfo.cs │ └── RTFLib.csproj ├── ACT.FFXIVTranslate │ ├── ILMergeOrder.txt │ ├── packages.config │ ├── localization │ │ └── Localization.cs │ ├── UpdateChecker.cs │ ├── app.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── translate │ │ ├── youdao │ │ │ ├── YoudaoTranslateProviderFactory.cs │ │ │ └── YoudaoTranslateProvider.cs │ │ ├── baidu │ │ │ ├── BaiduTranslateProviderFactory.cs │ │ │ └── BaiduTranslateProvider.cs │ │ ├── microsoft │ │ │ ├── MicrosoftTranslateProviderFactory.cs │ │ │ ├── AzureAuthToken.cs │ │ │ └── MicrosoftTranslateProvider.cs │ │ ├── google_unofficial │ │ │ ├── GoogleTranslateProviderFactory.cs │ │ │ └── GoogleTranslateProvider.cs │ │ ├── tencent │ │ │ ├── TencentTranslateProviderFactory.cs │ │ │ └── TencentTranslateProvider.cs │ │ ├── yandax │ │ │ ├── YandaxTranslateProviderFactory.cs │ │ │ └── YandaxTranslateProvider.cs │ │ ├── ITranslateProvider.cs │ │ ├── TranslateProviderPanel.cs │ │ └── TranslateService.cs │ ├── ProxyFactory.cs │ ├── ILMerge.props │ ├── WindowsMessagePump.cs │ ├── MainController.cs │ └── SettingsHolder.cs ├── ACT.FFXIVTranslate.Test │ ├── packages.config │ ├── app.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── WorldNameTest.cs │ ├── YandaxTranslateTest.cs │ ├── MicrosoftTranslateTest.cs │ ├── UnofficialGoogleTranslateTest.cs │ ├── BaiduTranslateTest.cs │ ├── YoudaoTranslateTest.cs │ └── ACT.FFXIVTranslate.Test.csproj └── ACT.FFXIVTranslate.sln ├── .gitmodules ├── appveyor.yml ├── .gitattributes ├── .gitignore ├── README.md └── MSBuild.ILMerge.Task.targets /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: Noisyfox 2 | custom: ['https://paypal.me/noisyfox'] 3 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/IRtfProcessor.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Noisyfox/ACT.FFXIVTranslate/HEAD/ACT.FFXIVTranslate/RTFLib/RTF/IRtfProcessor.cs -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ACT.FFXIVTranslate/ACT.FoxCommon"] 2 | path = ACT.FFXIVTranslate/ACT.FoxCommon 3 | url = https://github.com/Noisyfox/ACT.FoxCommon.git 4 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/ILMergeOrder.txt: -------------------------------------------------------------------------------- 1 | # this file contains the partial list of the merged assemblies in the merge order 2 | # you can fill it from the obj\CONFIG\PROJECT.ilmerge generated on every build 3 | # and finetune merge order to your satisfaction 4 | 5 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate.Test/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFBorderSide.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | namespace RTF 7 | { 8 | using System; 9 | 10 | [Flags] 11 | public enum RTFBorderSide 12 | { 13 | None = 0, 14 | Left = 0x01, 15 | Right = 0x02, 16 | Top = 0x04, 17 | Bottom = 0x08, 18 | Default = 0x0F, 19 | DoubleThickness = 0x10, 20 | DoubleBorder = 0x20 21 | } 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFFont.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | namespace RTF 6 | { 7 | public enum RTFFont 8 | { 9 | Arial, 10 | ArialBlack, 11 | BookmanOldStyle, 12 | Broadway, 13 | CenturyGothic , 14 | Consolas , 15 | CordiaNew , 16 | CourierNew, 17 | FontTimesNewRoman, 18 | Garamond, 19 | Georgia , 20 | Impact , 21 | LucidaConsole , 22 | Symbol , 23 | WingDings , 24 | MSSansSerif 25 | } 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate.Test/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("ACT.FFXIVTranslate.Test")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("ACT.FFXIVTranslate.Test")] 10 | [assembly: AssemblyCopyright("Copyright © 2017")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("81906072-43fd-40d0-916a-f16d0eda15ba")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/localization/Localization.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using ACT.FoxCommon.localization; 3 | 4 | namespace ACT.FFXIVTranslate.localization 5 | { 6 | public static class Localization 7 | { 8 | public static readonly LanguageDef[] SupportedLanguages = { 9 | LanguageDef.BuildLangFromCulture("zh-CN"), 10 | LanguageDef.BuildLangFromCulture("en-US"), 11 | }; 12 | 13 | private const string DefaultLanguage = "zh-CN"; 14 | 15 | static Localization() 16 | { 17 | LocalizationBase.InitLocalization(strings.ResourceManager, SupportedLanguages, DefaultLanguage); 18 | } 19 | 20 | 21 | public static void ConfigLocalization(string code) 22 | { 23 | strings.Culture = CultureInfo.GetCultureInfo(code); 24 | LocalizationBase.ConfigLocalization(code); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/UpdateChecker.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using ACT.FoxCommon.update; 3 | 4 | namespace ACT.FFXIVTranslate 5 | { 6 | internal class UpdateChecker : UpdateCheckerBase 7 | { 8 | public const string ReleasePage = "https://github.com/Noisyfox/ACT.FFXIVTranslate/releases"; 9 | 10 | 11 | protected override string UpdateUrl { get; } = "https://api.github.com/repos/Noisyfox/ACT.FFXIVTranslate/releases"; 12 | 13 | private const string NameMatcher = 14 | @"^ACT\.FFXIVTranslate(?:-|\.)(?\d+(?:\.\d+)*)(?:|-Release)\.7z$"; 15 | 16 | protected override string ParseVersion(string fileName) 17 | { 18 | var match = Regex.Match(fileName, NameMatcher); 19 | if (match.Success) 20 | { 21 | return match.Groups["version"].Value; 22 | } 23 | 24 | return null; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.6.1.{build} 2 | image: Visual Studio 2019 3 | configuration: 4 | - Debug 5 | - Release 6 | assembly_info: 7 | patch: true 8 | file: ACT.FFXIVTranslate\ACT.FFXIVTranslate*\**\AssemblyInfo.* 9 | assembly_version: '{version}' 10 | assembly_file_version: '{version}' 11 | assembly_informational_version: '{version}' 12 | before_build: 13 | - cmd: >- 14 | git submodule update --init --recursive 15 | 16 | nuget restore ACT.FFXIVTranslate 17 | 18 | cp MSBuild.ILMerge.Task.targets .\ACT.FFXIVTranslate\packages\MSBuild.ILMerge.Task.1.1.3\build\MSBuild.ILMerge.Task.targets 19 | build: 20 | project: ACT.FFXIVTranslate/ACT.FFXIVTranslate.sln 21 | verbosity: minimal 22 | after_build: 23 | - cmd: >- 24 | cd ACT.FFXIVTranslate\ACT.FFXIVTranslate\bin\%CONFIGURATION%\ 25 | 26 | 27 | 7z a -x!*.pdb %APPVEYOR_BUILD_FOLDER%\ACT.FFXIVTranslate-%APPVEYOR_BUILD_VERSION%-%CONFIGURATION%.7z .\* 28 | test: off 29 | artifacts: 30 | - path: ACT.FFXIVTranslate\ACT.FFXIVTranslate\bin\**\* 31 | name: Bin 32 | - path: ACT.FFXIVTranslate*.7z 33 | name: PKG 34 | 35 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/IRTFCell.cs: -------------------------------------------------------------------------------- 1 | namespace RTF 2 | { 3 | using System; 4 | 5 | // ---------------------------------------------------------------------------------------- 6 | // _ ___ _..-._ Date: 12/11/08 23:32 7 | // \`.|\..----...-'` `-._.-'' _.-..' 8 | // / ' ` , __.-'' 9 | // )/` _/ \ `-_, / Solution: RTFLib 10 | // `-'" `"\_ ,_.-;_.-\_ ', Project : RTFLib 11 | // _.-'_./ {_.' ; / Author : Anton 12 | // {_.-``-' {_/ Assembly: 1.0.0.0 13 | // Copyright © 2005-2008, Rogue Trader/MWM 14 | // Project Item Name: IRTFCell.cs - Code 15 | // Purpose: Exposes an underlying RTFBuilderbase 16 | // ---------------------------------------------------------------------------------------- 17 | /// 18 | /// Exposes an underlying RTFBuilderbase 19 | /// 20 | public interface IBuilderContent : IDisposable 21 | { 22 | RTFBuilderbase Content { get; } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/IRTFRow.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | namespace RTF 7 | { 8 | using System; 9 | using System.Collections.Generic; 10 | 11 | // ---------------------------------------------------------------------------------------- 12 | // _ ___ _..-._ Date: 12/11/08 23:33 13 | // \`.|\..----...-'` `-._.-'' _.-..' 14 | // / ' ` , __.-'' 15 | // )/` _/ \ `-_, / Solution: RTFLib 16 | // `-'" `"\_ ,_.-;_.-\_ ', Project : RTFLib 17 | // _.-'_./ {_.' ; / Author : Anton 18 | // {_.-``-' {_/ Assembly: 1.0.0.0 19 | // Copyright © 2005-2008, Rogue Trader/MWM 20 | // Project Item Name: IRTFRow.cs - Code 21 | // Purpose: Row Interface 22 | // ---------------------------------------------------------------------------------------- 23 | /// 24 | /// Row Interface 25 | /// 26 | public interface IRTFRow : IDisposable, IEnumerable 27 | { 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("ACT.FFXIVTranslate")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ACT.FFXIVTranslate")] 13 | [assembly: AssemblyCopyright("Copyright © 2017-2021 Noisyfox")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | //将 ComVisible 设置为 false 将使此程序集中的类型 18 | //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("7263a6d5-a142-4f3f-92a7-c3dfa9ba45b9")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.6.1.0")] 36 | [assembly: AssemblyFileVersion("1.6.1.0")] 37 | 38 | [assembly: InternalsVisibleTo("ACT.FFXIVTranslate.Test")] 39 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate.Test/WorldNameTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace ACT.FFXIVTranslate.Test 7 | { 8 | [TestClass] 9 | public class WorldNameTest 10 | { 11 | [TestMethod] 12 | public void TestParseSenderName() 13 | { 14 | var bytes = new byte[] 15 | { 16 | 0x02, 0x27, 0x13, 0x01, 0x0F, 0x01, 0x01, 0xEF, 0xBF, 0xBD, 0x20, 0x4D, 0x69, 0x6C, 0x61, 0x20, 17 | 0x45, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x03, 0xEE, 0x82, 0x96, 0x4D, 0x69, 0x6C, 0x61, 0x20, 18 | 0x45, 0x74, 0x65, 0x72, 0x6E, 0x61, 0x6C, 0x02, 0x27, 0x07, 0xEF, 0xBF, 0xBD, 0x01, 0x01, 0x01, 19 | 0xEF, 0xBF, 0xBD, 0x01, 0x03, 0x02, 0x12, 0x02, 0x59, 0x03, 0x55, 0x6C, 0x74, 0x69, 0x6D, 0x61 20 | }; 21 | // var bytes = new byte[] 22 | // { 23 | // 0x02, 0x12, 0x02, 0x59, 0x03, 0x55, 0x6C, 0x74, 0x69, 0x6D, 0x61 24 | // }; 25 | 26 | var input = Encoding.UTF8.GetString(bytes); 27 | 28 | var v = TextProcessor.ExtractName(input); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTFLib.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RTFLib", "RTFLib\RTFLib.csproj", "{CD1CF51F-CDE9-4403-A07E-C8CA4C015C58}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RTFLibDemo", "RTFLibDemo\RTFLibDemo.csproj", "{A0E9311B-1207-4331-BAB8-0ABA5B380589}" 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 | {CD1CF51F-CDE9-4403-A07E-C8CA4C015C58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {CD1CF51F-CDE9-4403-A07E-C8CA4C015C58}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {CD1CF51F-CDE9-4403-A07E-C8CA4C015C58}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {CD1CF51F-CDE9-4403-A07E-C8CA4C015C58}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {A0E9311B-1207-4331-BAB8-0ABA5B380589}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {A0E9311B-1207-4331-BAB8-0ABA5B380589}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {A0E9311B-1207-4331-BAB8-0ABA5B380589}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {A0E9311B-1207-4331-BAB8-0ABA5B380589}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate.Test/YandaxTranslateTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ACT.FFXIVTranslate.translate.yandax; 3 | using ACT.FoxCommon.localization; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace ACT.FFXIVTranslate.Test 7 | { 8 | [TestClass] 9 | public class YandaxTranslateTest 10 | { 11 | [TestMethod] 12 | public void TestYandaxTranslate() 13 | { 14 | var factory = new YandaxTranslateProviderFactory(); 15 | var key = factory.DefaultPublicKey; 16 | var to = LanguageDef.BuildLangFromCulture("zh"); 17 | var provider = factory.CreateProvider(key, null, to); 18 | 19 | var lines = new[] 20 | { 21 | new ChattingLine {RawContent = "This is a testing string."}, 22 | new ChattingLine {RawContent = "This is another testing string."}, 23 | new ChattingLine {RawContent = "Wow lots of strings!"}, 24 | new ChattingLine {RawContent = "I like the game called 'Final Fantasy XIV'!"}, 25 | new ChattingLine {RawContent = "This is a string contains html tag."}, 26 | new ChattingLine {RawContent = "This is a string < contains html tag."}, 27 | new ChattingLine {RawContent = "<"}, 28 | }.Select(it => 29 | { 30 | provider.PreprocessLine(it); 31 | return it; 32 | }).ToList(); 33 | 34 | provider.Translate(lines); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("RTFLib")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("RTFLib")] 13 | [assembly: AssemblyCopyright("Copyright © 2008")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("640e0969-5726-48bd-9855-ef80f796f2f1")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate.Test/MicrosoftTranslateTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ACT.FFXIVTranslate.translate.microsoft; 3 | using ACT.FoxCommon.localization; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace ACT.FFXIVTranslate.Test 7 | { 8 | [TestClass] 9 | public class MicrosoftTranslateTest 10 | { 11 | [TestMethod] 12 | public void TestMicrosoftTranslate() 13 | { 14 | var factory = new MicrosoftTranslateProviderFactory(); 15 | var key = "90ebd9b5e7544500a5c1cf3ff7996314"; 16 | var to = LanguageDef.BuildLangFromCulture("zh-CHS"); 17 | var provider = factory.CreateProvider(key, null, to); 18 | 19 | var lines = new[] 20 | { 21 | new ChattingLine{RawContent = "This is a testing string."}, 22 | new ChattingLine{RawContent = "This is another testing string."}, 23 | new ChattingLine{RawContent = "Wow lots of strings!"}, 24 | new ChattingLine{RawContent = "I like the game called 'Final Fantasy XIV'!"}, 25 | new ChattingLine{RawContent = "This is a string contains html tag."}, 26 | new ChattingLine{RawContent = "This is a string < contains html tag."}, 27 | new ChattingLine{RawContent = "<"}, 28 | }.Select(it => 29 | { 30 | provider.PreprocessLine(it); 31 | return it; 32 | }).ToList(); 33 | 34 | provider.Translate(lines); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/youdao/YoudaoTranslateProviderFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using ACT.FoxCommon.localization; 4 | 5 | namespace ACT.FFXIVTranslate.translate.youdao 6 | { 7 | internal class YoudaoTranslateProviderFactory : ITranslateProviderFactory 8 | { 9 | private readonly List _allSupportedLanguages = new[] 10 | { 11 | LanguageDef.BuildLangFromCulture("zh-CHS"), 12 | LanguageDef.BuildLangFromCulture("en", "EN"), 13 | LanguageDef.BuildLangFromCulture("ja"), 14 | LanguageDef.BuildLangFromCulture("fr"), 15 | LanguageDef.BuildLangFromCulture("ru"), 16 | LanguageDef.BuildLangFromCulture("ko"), 17 | }.ToList(); 18 | 19 | public string ProviderId => "有道翻译"; 20 | public string ProviderName => LocalizationBase.GetString("translateProviderNameYoudao", ProviderId); 21 | public bool SupportAutoDetect => true; 22 | public List SupportedSrcLanguages => _allSupportedLanguages; 23 | public List GetSupportedDestLanguages(LanguageDef srcLanguage) 24 | { 25 | return _allSupportedLanguages.Where(it => it != srcLanguage).ToList(); 26 | } 27 | public ProviderLegalInfo LegalInfo => null; 28 | public string DefaultPublicKey => null; 29 | 30 | public ITranslateProvider CreateProvider(string apiKey, LanguageDef src, LanguageDef dst) 31 | { 32 | return new YoudaoTranslateProvider(apiKey, src, dst); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate.Test/UnofficialGoogleTranslateTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ACT.FFXIVTranslate.localization; 3 | using ACT.FFXIVTranslate.translate.google_unofficial; 4 | using ACT.FoxCommon.localization; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | 7 | namespace ACT.FFXIVTranslate.Test 8 | { 9 | [TestClass] 10 | public class UnofficialGoogleTranslateTest 11 | { 12 | [TestMethod] 13 | public void TestUnofficialGoogleTranslate() 14 | { 15 | var factory = new GoogleTranslateProviderFactory(); 16 | var to = LanguageDef.BuildLangFromCulture("zh"); 17 | var provider = factory.CreateProvider(null, null, to); 18 | 19 | var lines = new[] 20 | { 21 | new ChattingLine{RawContent = "This is a testing string."}, 22 | new ChattingLine{RawContent = "This is another testing string."}, 23 | new ChattingLine{RawContent = "Wow lots of strings!"}, 24 | new ChattingLine{RawContent = "I like the game called 'Final Fantasy XIV'!"}, 25 | new ChattingLine{RawContent = "This is a string contains html tag."}, 26 | new ChattingLine{RawContent = "This is a string < contains html tag."}, 27 | new ChattingLine{RawContent = "<"}, 28 | new ChattingLine{RawContent = "你在逗我呢?"}, 29 | }.Select(it => 30 | { 31 | provider.PreprocessLine(it); 32 | return it; 33 | }).ToList(); 34 | 35 | provider.Translate(lines); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate.Test/BaiduTranslateTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ACT.FFXIVTranslate.translate.baidu; 3 | using ACT.FoxCommon.localization; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace ACT.FFXIVTranslate.Test 7 | { 8 | [TestClass] 9 | public class BaiduTranslateTest 10 | { 11 | [TestMethod] 12 | public void TestBaiduTranslate() 13 | { 14 | var factory = new BaiduTranslateProviderFactory(); 15 | var to = LanguageDef.BuildLangFromCulture("zh"); 16 | var appId = "YOUR-APP-ID"; 17 | var secret = "YOUR-APP-SECRET"; 18 | var provider = factory.CreateProvider($"{appId}:{secret}", null, to); 19 | 20 | var lines = new[] 21 | { 22 | new ChattingLine{RawContent = "This is a testing string."}, 23 | new ChattingLine{RawContent = "This is another testing string."}, 24 | new ChattingLine{RawContent = "Wow lots of strings!"}, 25 | new ChattingLine{RawContent = "I like the game called 'Final Fantasy XIV'!"}, 26 | new ChattingLine{RawContent = "This is a string contains html tag."}, 27 | new ChattingLine{RawContent = "This is a string < contains html tag."}, 28 | new ChattingLine{RawContent = "<"}, 29 | new ChattingLine{RawContent = "你在逗我呢?"}, 30 | }.Select(it => 31 | { 32 | provider.PreprocessLine(it); 33 | return it; 34 | }).ToList(); 35 | 36 | provider.Translate(lines); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate.Test/YoudaoTranslateTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using ACT.FFXIVTranslate.translate.youdao; 3 | using ACT.FoxCommon.localization; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace ACT.FFXIVTranslate.Test 7 | { 8 | [TestClass] 9 | public class YoudaoTranslateTest 10 | { 11 | [TestMethod] 12 | public void TestYoudaoTranslate() 13 | { 14 | var factory = new YoudaoTranslateProviderFactory(); 15 | var to = LanguageDef.BuildLangFromCulture("zh-CHS"); 16 | var appId = "YOUR-APP-ID"; 17 | var secret = "YOUR-APP-SECRET"; 18 | var provider = factory.CreateProvider($"{appId}:{secret}", null, to); 19 | 20 | var lines = new[] 21 | { 22 | new ChattingLine{RawContent = "This is a testing string."}, 23 | new ChattingLine{RawContent = "This is another testing string."}, 24 | new ChattingLine{RawContent = "Wow lots of strings!"}, 25 | new ChattingLine{RawContent = "I like the game called 'Final Fantasy XIV'!"}, 26 | new ChattingLine{RawContent = "This is a string contains html tag."}, 27 | new ChattingLine{RawContent = "This is a string < contains html tag."}, 28 | new ChattingLine{RawContent = "<"}, 29 | new ChattingLine{RawContent = "你在逗我呢?"}, 30 | }.Select(it => 31 | { 32 | provider.PreprocessLine(it); 33 | return it; 34 | }).ToList(); 35 | 36 | provider.Translate(lines); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/baidu/BaiduTranslateProviderFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using ACT.FoxCommon.localization; 4 | 5 | namespace ACT.FFXIVTranslate.translate.baidu 6 | { 7 | internal class BaiduTranslateProviderFactory : ITranslateProviderFactory 8 | { 9 | private readonly List _allSupportedLanguages = new[] 10 | { 11 | LanguageDef.BuildLangFromCulture("zh"), 12 | LanguageDef.BuildLangFromCulture("en"), 13 | LanguageDef.BuildLangFromCulture("ja", "jp"), 14 | LanguageDef.BuildLangFromCulture("de"), 15 | LanguageDef.BuildLangFromCulture("fr", "fra"), 16 | LanguageDef.BuildLangFromCulture("ru"), 17 | LanguageDef.BuildLangFromCulture("ko", "kor"), 18 | }.ToList(); 19 | 20 | public string ProviderId => "百度翻译"; 21 | public string ProviderName => LocalizationBase.GetString("translateProviderNameBaidu", ProviderId); 22 | 23 | public bool SupportAutoDetect => true; 24 | public List SupportedSrcLanguages => _allSupportedLanguages; 25 | public List GetSupportedDestLanguages(LanguageDef srcLanguage) 26 | { 27 | return _allSupportedLanguages.Where(it => it != srcLanguage).ToList(); 28 | } 29 | 30 | public ProviderLegalInfo LegalInfo => null; 31 | public string DefaultPublicKey => null; 32 | 33 | public ITranslateProvider CreateProvider(string apiKey, LanguageDef src, LanguageDef dst) 34 | { 35 | return new BaiduTranslateProvider(apiKey, src, dst); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/microsoft/MicrosoftTranslateProviderFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using ACT.FoxCommon.localization; 4 | 5 | namespace ACT.FFXIVTranslate.translate.microsoft 6 | { 7 | internal class MicrosoftTranslateProviderFactory : ITranslateProviderFactory 8 | { 9 | private readonly List _allSupportedLanguages = new[] 10 | { 11 | LanguageDef.BuildLangFromCulture("zh-CHS"), 12 | LanguageDef.BuildLangFromCulture("zh-CHT"), 13 | LanguageDef.BuildLangFromCulture("en"), 14 | LanguageDef.BuildLangFromCulture("ja"), 15 | LanguageDef.BuildLangFromCulture("de"), 16 | LanguageDef.BuildLangFromCulture("fr"), 17 | LanguageDef.BuildLangFromCulture("ru"), 18 | LanguageDef.BuildLangFromCulture("ko"), 19 | }.ToList(); 20 | 21 | public string ProviderId => "Microsoft Translator"; 22 | public string ProviderName => LocalizationBase.GetString("translateProviderNameMicrosoft", ProviderId); 23 | public bool SupportAutoDetect => true; 24 | public List SupportedSrcLanguages => _allSupportedLanguages; 25 | public List GetSupportedDestLanguages(LanguageDef srcLanguage) 26 | { 27 | return _allSupportedLanguages.Where(it => it != srcLanguage).ToList(); 28 | } 29 | public ProviderLegalInfo LegalInfo => null; 30 | public string DefaultPublicKey => null; 31 | 32 | public ITranslateProvider CreateProvider(string apiKey, LanguageDef src, LanguageDef dst) 33 | { 34 | return new MicrosoftTranslateProvider(apiKey, src, dst); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/google_unofficial/GoogleTranslateProviderFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using ACT.FoxCommon.localization; 4 | 5 | namespace ACT.FFXIVTranslate.translate.google_unofficial 6 | { 7 | internal class GoogleTranslateProviderFactory : ITranslateProviderFactory 8 | { 9 | private readonly List _allSupportedLanguages = new[] 10 | { 11 | LanguageDef.BuildLangFromCulture("zh-CN"), 12 | LanguageDef.BuildLangFromCulture("zh-TW"), 13 | LanguageDef.BuildLangFromCulture("en"), 14 | LanguageDef.BuildLangFromCulture("ja"), 15 | LanguageDef.BuildLangFromCulture("de"), 16 | LanguageDef.BuildLangFromCulture("fr"), 17 | LanguageDef.BuildLangFromCulture("ru"), 18 | LanguageDef.BuildLangFromCulture("ko"), 19 | }.ToList(); 20 | 21 | public string ProviderId => "Google Translate (unofficial)"; 22 | public string ProviderName => LocalizationBase.GetString("translateProviderNameGoogleUnofficial", ProviderId); 23 | public bool SupportAutoDetect => true; 24 | public List SupportedSrcLanguages => _allSupportedLanguages; 25 | public List GetSupportedDestLanguages(LanguageDef srcLanguage) 26 | { 27 | return _allSupportedLanguages.Where(it => it != srcLanguage).ToList(); 28 | } 29 | public ProviderLegalInfo LegalInfo => null; 30 | public string DefaultPublicKey => null; 31 | 32 | public ITranslateProvider CreateProvider(string apiKey, LanguageDef src, LanguageDef dst) 33 | { 34 | return new GoogleTranslateProvider(src, dst); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/tencent/TencentTranslateProviderFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using ACT.FoxCommon.localization; 4 | 5 | namespace ACT.FFXIVTranslate.translate.tencent 6 | { 7 | internal class TencentTranslateProviderFactory : ITranslateProviderFactory 8 | { 9 | private readonly List _allSupportedLanguages = new[] 10 | { 11 | LanguageDef.BuildLangFromCulture("zh-CN", "zh"), 12 | LanguageDef.BuildLangFromCulture("zh-TW"), 13 | LanguageDef.BuildLangFromCulture("en"), 14 | LanguageDef.BuildLangFromCulture("ja", "jp"), 15 | LanguageDef.BuildLangFromCulture("de"), 16 | LanguageDef.BuildLangFromCulture("fr"), 17 | LanguageDef.BuildLangFromCulture("ru"), 18 | LanguageDef.BuildLangFromCulture("ko", "kr"), 19 | }.ToList(); 20 | 21 | public string ProviderId => "Tencent Translate"; 22 | public string ProviderName => LocalizationBase.GetString("translateProviderNameTencent", ProviderId); 23 | public bool SupportAutoDetect => true; 24 | public List SupportedSrcLanguages => _allSupportedLanguages; 25 | public List GetSupportedDestLanguages(LanguageDef srcLanguage) 26 | { 27 | return _allSupportedLanguages.Where(it => it != srcLanguage).ToList(); 28 | } 29 | public ProviderLegalInfo LegalInfo => null; 30 | public string DefaultPublicKey => "AKIDMSHqSphlMjpeFsgPu9ByUjzZz9KHnOZf:1nLn10iCcsscLq54qY2JnMglaPyHK7oG"; 31 | 32 | public ITranslateProvider CreateProvider(string apiKey, LanguageDef src, LanguageDef dst) 33 | { 34 | return new TencentTranslateProvider(apiKey, src, dst); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFAlignment.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | namespace RTF 7 | { 8 | using System; 9 | 10 | [Flags] 11 | public enum RTFAlignment 12 | { 13 | None = 0, 14 | 15 | /// Content is vertically aligned at the bottom, and horizontally aligned at the center. 16 | /// 1 17 | BottomCenter = 512, 18 | /// Content is vertically aligned at the bottom, and horizontally aligned on the left. 19 | /// 1 20 | BottomLeft = 256, 21 | /// Content is vertically aligned at the bottom, and horizontally aligned on the right. 22 | /// 1 23 | BottomRight = 1024, 24 | /// Content is vertically aligned in the middle, and horizontally aligned at the center. 25 | /// 1 26 | MiddleCenter = 32, 27 | /// Content is vertically aligned in the middle, and horizontally aligned on the left. 28 | /// 1 29 | MiddleLeft = 16, 30 | /// Content is vertically aligned in the middle, and horizontally aligned on the right. 31 | /// 1 32 | MiddleRight = 64, 33 | /// Content is vertically aligned at the top, and horizontally aligned at the center. 34 | /// 1 35 | TopCenter = 2, 36 | /// Content is vertically aligned at the top, and horizontally aligned on the left. 37 | /// 1 38 | TopLeft = 1, 39 | /// Content is vertically aligned at the top, and horizontally aligned on the right. 40 | /// 1 41 | TopRight = 4 42 | } 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/yandax/YandaxTranslateProviderFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using ACT.FoxCommon.localization; 4 | 5 | namespace ACT.FFXIVTranslate.translate.yandax 6 | { 7 | internal class YandaxTranslateProviderFactory : ITranslateProviderFactory 8 | { 9 | private readonly List _allSupportedLanguages = new[] 10 | { 11 | LanguageDef.BuildLangFromCulture("zh"), 12 | LanguageDef.BuildLangFromCulture("en"), 13 | LanguageDef.BuildLangFromCulture("ja"), 14 | LanguageDef.BuildLangFromCulture("de"), 15 | LanguageDef.BuildLangFromCulture("fr"), 16 | LanguageDef.BuildLangFromCulture("ru"), 17 | LanguageDef.BuildLangFromCulture("ko"), 18 | }.ToList(); 19 | 20 | public string ProviderId => "Yandex Translate"; 21 | public string ProviderName => LocalizationBase.GetString("translateProviderNameYandex", ProviderId); 22 | 23 | public bool SupportAutoDetect => true; 24 | 25 | public List SupportedSrcLanguages => _allSupportedLanguages; 26 | public List GetSupportedDestLanguages(LanguageDef srcLanguage) 27 | { 28 | return _allSupportedLanguages.Where(it => it != srcLanguage).ToList(); 29 | } 30 | public ProviderLegalInfo LegalInfo { get; } = new ProviderLegalInfo 31 | { 32 | LabelMain = "Powered by Yandex.Translate", 33 | LabelResult = "Powered by Yandex.Translate", 34 | LabelMainLink = "http://translate.yandex.com", 35 | LabelResultLink = "http://translate.yandex.com", 36 | }; 37 | 38 | public string DefaultPublicKey => "trnsl.1.1.20170716T025951Z.13c73247084b012d.3404189299f91adf7792235bc7cf7fb7f3bd26a2"; 39 | 40 | public ITranslateProvider CreateProvider(string apiKey, LanguageDef src, LanguageDef dst) 41 | { 42 | return new YandaxTranslateProvider(apiKey, src, dst); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27004.2008 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACT.FFXIVTranslate", "ACT.FFXIVTranslate\ACT.FFXIVTranslate.csproj", "{7263A6D5-A142-4F3F-92A7-C3DFA9BA45B9}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACT.FFXIVTranslate.Test", "ACT.FFXIVTranslate.Test\ACT.FFXIVTranslate.Test.csproj", "{81906072-43FD-40D0-916A-F16D0EDA15BA}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RTFLib", "RTFLib\RTFLib.csproj", "{CD1CF51F-CDE9-4403-A07E-C8CA4C015C58}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ACT.FoxCommon", "ACT.FoxCommon\ACT.FoxCommon\ACT.FoxCommon.csproj", "{78342B89-6BAA-43E8-824A-A6FB06CDE993}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {7263A6D5-A142-4F3F-92A7-C3DFA9BA45B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {7263A6D5-A142-4F3F-92A7-C3DFA9BA45B9}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {7263A6D5-A142-4F3F-92A7-C3DFA9BA45B9}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {7263A6D5-A142-4F3F-92A7-C3DFA9BA45B9}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {81906072-43FD-40D0-916A-F16D0EDA15BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {81906072-43FD-40D0-916A-F16D0EDA15BA}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {81906072-43FD-40D0-916A-F16D0EDA15BA}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {81906072-43FD-40D0-916A-F16D0EDA15BA}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {CD1CF51F-CDE9-4403-A07E-C8CA4C015C58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {CD1CF51F-CDE9-4403-A07E-C8CA4C015C58}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {CD1CF51F-CDE9-4403-A07E-C8CA4C015C58}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {CD1CF51F-CDE9-4403-A07E-C8CA4C015C58}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {78342B89-6BAA-43E8-824A-A6FB06CDE993}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {78342B89-6BAA-43E8-824A-A6FB06CDE993}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {78342B89-6BAA-43E8-824A-A6FB06CDE993}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {78342B89-6BAA-43E8-824A-A6FB06CDE993}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {6CBEA01A-631F-40AE-9131-0FE099C906F9} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFBuilder.UnWrapped.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | namespace RTF 5 | { 6 | using System; 7 | 8 | partial class RTFBuilder 9 | { 10 | #region Nested type: RTFBuilderUnWrapped 11 | 12 | // ---------------------------------------------------------------------------------------- 13 | // _ ___ _..-._ Date: 12/11/08 23:38 14 | // \`.|\..----...-'` `-._.-'' _.-..' 15 | // / ' ` , __.-'' 16 | // )/` _/ \ `-_, / Solution: RTFLib 17 | // `-'" `"\_ ,_.-;_.-\_ ', Project : RTFLib 18 | // _.-'_./ {_.' ; / Author : Anton 19 | // {_.-``-' {_/ Assembly: 1.0.0.0 20 | // Copyright © 2005-2008, Rogue Trader/MWM 21 | // Project Item Name: RTFBuilder.UnWrapped.cs - Code 22 | // Purpose: Cancels persistent Formatting Changes on an unwrapped RtfBuilder 23 | // ---------------------------------------------------------------------------------------- 24 | /// 25 | /// Cancels persistent Formatting Changes on an unwrapped RtfBuilder 26 | /// Exposed by the FormatLock on RtfBuilderbase 27 | /// 28 | private class RTFBuilderUnWrapped : IDisposable 29 | { 30 | #region Fields 31 | 32 | private readonly RTFBuilder _builder; 33 | private readonly RTFFormatWrap wrapped; 34 | 35 | #endregion 36 | 37 | #region Constructor 38 | 39 | public RTFBuilderUnWrapped(RTFBuilder builder) 40 | { 41 | this.wrapped = new RTFFormatWrap(builder); 42 | this._builder = builder; 43 | this._builder._unwrapped = true; 44 | } 45 | 46 | #endregion 47 | 48 | #region Override Methods 49 | 50 | ~RTFBuilderUnWrapped() 51 | { 52 | this.Dispose(false); 53 | } 54 | 55 | #endregion 56 | 57 | #region Public Methods 58 | 59 | public void Dispose(bool disposing) 60 | { 61 | if (this._builder != null) 62 | { 63 | this.wrapped.Dispose(); 64 | this._builder._unwrapped = false; 65 | } 66 | if (disposing) 67 | { 68 | GC.SuppressFinalize(this); 69 | } 70 | } 71 | 72 | #endregion 73 | 74 | #region IDisposable Members 75 | 76 | public void Dispose() 77 | { 78 | this.Dispose(true); 79 | } 80 | 81 | #endregion 82 | } 83 | 84 | #endregion 85 | } 86 | } -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/ITranslateProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ACT.FoxCommon.localization; 4 | 5 | namespace ACT.FFXIVTranslate.translate 6 | { 7 | internal interface ITranslateProvider 8 | { 9 | void Translate(List chattingLines); 10 | 11 | /// 12 | /// False if this line can be ignored (usually is empty), True otherwise. 13 | bool PreprocessLine(ChattingLine chattingLine); 14 | } 15 | 16 | /// 17 | /// A provider that use TextProcessor.NaiveCleanText() as the content preprocessor 18 | /// and ignores all empty lines after that. 19 | /// 20 | internal abstract class DefaultTranslateProvider : ITranslateProvider 21 | { 22 | public abstract void Translate(List chattingLines); 23 | 24 | public virtual bool PreprocessLine(ChattingLine chattingLine) 25 | { 26 | chattingLine.CleanedContent = TextProcessor.NaiveCleanText(chattingLine.RawContent); 27 | 28 | return !string.IsNullOrEmpty(chattingLine.CleanedContent); 29 | } 30 | } 31 | 32 | internal interface ITranslateProviderFactory 33 | { 34 | string ProviderId { get; } 35 | 36 | string ProviderName { get; } 37 | 38 | bool SupportAutoDetect { get; } 39 | 40 | List SupportedSrcLanguages { get; } 41 | 42 | List GetSupportedDestLanguages(LanguageDef srcLanguage); 43 | 44 | 45 | ProviderLegalInfo LegalInfo { get; } 46 | 47 | /// 48 | /// The default API key for free public use. A gift from me :) 49 | /// 50 | string DefaultPublicKey { get; } 51 | 52 | ITranslateProvider CreateProvider(string apiKey, LanguageDef src, LanguageDef dst); 53 | } 54 | 55 | internal class TranslateException : Exception 56 | { 57 | public enum ExceptionReason 58 | { 59 | InvalidApiKey, 60 | ApiLimitExceed, 61 | DirectionNotSupported, 62 | GeneralServiceError, 63 | NetworkError, 64 | InternalError, 65 | UnknownError 66 | } 67 | 68 | public ExceptionReason Reason { get; } 69 | 70 | public TranslateException(ExceptionReason reason, string message, Exception innerException) 71 | : base(message, innerException) 72 | { 73 | Reason = reason; 74 | } 75 | 76 | public override string ToString() 77 | { 78 | return $"({Reason}){base.ToString()}"; 79 | } 80 | } 81 | 82 | public class ProviderLegalInfo 83 | { 84 | public string LabelMain { get; set; } 85 | public string LabelMainLink { get; set; } 86 | public string LabelResult { get; set; } 87 | public string LabelResultLink { get; set; } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTFLib.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.21022 7 | 2.0 8 | {CD1CF51F-CDE9-4403-A07E-C8CA4C015C58} 9 | Library 10 | Properties 11 | RTFLib 12 | RTFLib 13 | v2.0 14 | 512 15 | 16 | 17 | 18 | 19 | 3.5 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 77 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/ProxyFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using Extreme.Net; 5 | 6 | namespace ACT.FFXIVTranslate 7 | { 8 | class ProxyFactory : IPluginComponent 9 | { 10 | public const string TypeNone = "none"; 11 | public const string TypeHttp = "http"; 12 | public const string TypeSocks5 = "socks5"; 13 | 14 | public static ProxyFactory Instance { get; } = new ProxyFactory(); 15 | 16 | private ProxyFactory() 17 | { 18 | 19 | } 20 | 21 | private ProxySettings _currentSettings; 22 | 23 | public void AttachToAct(FFXIVTranslatePlugin plugin) 24 | { 25 | plugin.Controller.ProxyChanged += ControllerOnProxyChanged; 26 | } 27 | 28 | public void PostAttachToAct(FFXIVTranslatePlugin plugin) 29 | { 30 | } 31 | 32 | private void ControllerOnProxyChanged(bool fromView, string type, string server, int port, string user, 33 | string password, string domain) 34 | { 35 | if (!fromView) 36 | { 37 | return; 38 | } 39 | 40 | _currentSettings = new ProxySettings 41 | { 42 | Type = type, 43 | Server = server, 44 | Port = port, 45 | User = user, 46 | Password = password, 47 | Domain = domain 48 | }; 49 | } 50 | 51 | private class ProxySettings 52 | { 53 | public string Type { get; set; } 54 | public string Server { get; set; } 55 | public int Port { get; set; } 56 | public string User { get; set; } 57 | public string Password { get; set; } 58 | public string Domain { get; set; } 59 | } 60 | 61 | public HttpClient NewClient() 62 | { 63 | var settings = _currentSettings; 64 | if (settings == null || settings.Type == TypeNone) 65 | { 66 | return new HttpClient(new HttpClientHandler 67 | { 68 | UseProxy = false 69 | }); 70 | } 71 | switch (settings.Type) 72 | { 73 | case TypeHttp: 74 | { 75 | var proxyCreds = new NetworkCredential(settings.User, settings.Password, settings.Domain); 76 | var proxy = new WebProxy(new Uri($"http://{settings.Server}:{settings.Port}"), true) 77 | { 78 | UseDefaultCredentials = false, 79 | Credentials = proxyCreds 80 | }; 81 | var handler = new HttpClientHandler 82 | { 83 | Proxy = proxy, 84 | UseProxy = true, 85 | PreAuthenticate = true, 86 | UseDefaultCredentials = false 87 | }; 88 | return new HttpClient(handler); 89 | } 90 | case TypeSocks5: 91 | { 92 | var proxy = new Socks5ProxyClient(settings.Server, settings.Port, settings.User, settings.Password); 93 | var handler = new ProxyHandler(proxy); 94 | return new HttpClient(handler); 95 | } 96 | default: 97 | throw new Exception($"Proxy Error: Unknown proxy type: {settings.Type}"); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/ILMerge.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | true 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFUtil.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | namespace RTF 7 | { 8 | using System.Text; 9 | 10 | // ---------------------------------------------------------------------------------------- 11 | // _ ___ _..-._ Date: 12/11/08 23:50 12 | // \`.|\..----...-'` `-._.-'' _.-..' 13 | // / ' ` , __.-'' 14 | // )/` _/ \ `-_, / Solution: RTFLib 15 | // `-'" `"\_ ,_.-;_.-\_ ', Project : RTFLib 16 | // _.-'_./ {_.' ; / Author : Anton 17 | // {_.-``-' {_/ Assembly: 1.0.0.0 18 | // Copyright © 2005-2008, Rogue Trader/MWM 19 | // Project Item Name: RTFUtil.cs - Code 20 | // Purpose: A Work in Progress 21 | // ---------------------------------------------------------------------------------------- 22 | /// 23 | /// A Work in Progress 24 | /// 25 | public class RTFUtil 26 | { 27 | #region Public Methods 28 | 29 | public void ParagraphBorderSide(StringBuilder sb, RTFBorderSide rTFBorderSide) 30 | { 31 | if (rTFBorderSide == RTFBorderSide.None) 32 | { 33 | return; 34 | } 35 | if ((rTFBorderSide & RTFBorderSide.Left) == RTFBorderSide.Left) 36 | { 37 | sb.Append("\\brdrl"); 38 | } 39 | if ((rTFBorderSide & RTFBorderSide.Right) == RTFBorderSide.Right) 40 | { 41 | sb.Append("\\brdrr"); 42 | } 43 | if ((rTFBorderSide & RTFBorderSide.Top) == RTFBorderSide.Top) 44 | { 45 | sb.Append("\\brdrt"); 46 | } 47 | if ((rTFBorderSide & RTFBorderSide.Bottom) == RTFBorderSide.Bottom) 48 | { 49 | sb.Append("\\brdrb"); 50 | } 51 | 52 | if ((rTFBorderSide & RTFBorderSide.DoubleThickness) == RTFBorderSide.DoubleThickness) 53 | { 54 | sb.Append("\\brdrth"); 55 | } 56 | else 57 | { 58 | sb.Append("\\brdrs"); 59 | } 60 | if ((rTFBorderSide & RTFBorderSide.DoubleBorder) == RTFBorderSide.DoubleBorder) 61 | { 62 | sb.Append("\\brdrdb"); 63 | } 64 | sb.Append("\\brdrw10"); 65 | } 66 | 67 | public void TableRowBorderSide(StringBuilder sb, RTFBorderSide rTFBorderSide) 68 | { 69 | if (rTFBorderSide == RTFBorderSide.None) 70 | { 71 | return; 72 | } 73 | if ((rTFBorderSide & RTFBorderSide.Left) == RTFBorderSide.Left) 74 | { 75 | sb.Append("\\trbrdrl"); 76 | } 77 | if ((rTFBorderSide & RTFBorderSide.Right) == RTFBorderSide.Right) 78 | { 79 | sb.Append("\\trbrdrr"); 80 | } 81 | if ((rTFBorderSide & RTFBorderSide.Top) == RTFBorderSide.Top) 82 | { 83 | sb.Append("\\trbrdrt"); 84 | } 85 | if ((rTFBorderSide & RTFBorderSide.Bottom) == RTFBorderSide.Bottom) 86 | { 87 | sb.Append("\\trbrdrb"); 88 | } 89 | if ((rTFBorderSide & RTFBorderSide.DoubleThickness) == RTFBorderSide.DoubleThickness) 90 | { 91 | sb.Append("\\brdrth"); 92 | } 93 | else 94 | { 95 | sb.Append("\\brdrs"); 96 | } 97 | if ((rTFBorderSide & RTFBorderSide.DoubleBorder) == RTFBorderSide.DoubleBorder) 98 | { 99 | sb.Append("\\brdrdb"); 100 | } 101 | sb.Append("\\brdrw10"); 102 | } 103 | 104 | #endregion 105 | } 106 | } 107 | 108 | 109 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFBuilder.RTFParaWrap.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | namespace RTF 7 | { 8 | using System; 9 | using System.Drawing; 10 | 11 | public partial class RTFBuilder 12 | { 13 | #region Nested type: RTFParaWrap 14 | 15 | // ---------------------------------------------------------------------------------------- 16 | // _ ___ _..-._ Date: 12/11/08 23:36 17 | // \`.|\..----...-'` `-._.-'' _.-..' 18 | // / ' ` , __.-'' 19 | // )/` _/ \ `-_, / Solution: RTFLib 20 | // `-'" `"\_ ,_.-;_.-\_ ', Project : RTFLib 21 | // _.-'_./ {_.' ; / Author : Anton 22 | // {_.-``-' {_/ Assembly: 1.0.0.0 23 | // Copyright © 2005-2008, Rogue Trader/MWM 24 | // Project Item Name: RTFBuilder.RTFParaWrap.cs - Code 25 | // Purpose: Wraps RtfBuilderbase injecting appropriate rtf codes after paragraph append 26 | // ---------------------------------------------------------------------------------------- 27 | /// 28 | /// Wraps RtfBuilderbase injecting appropriate rtf codes after paragraph append 29 | /// 30 | private class RTFParaWrap : IDisposable 31 | { 32 | #region Fields 33 | 34 | private readonly RTFBuilder _builder; 35 | 36 | #endregion 37 | 38 | #region Constructor 39 | 40 | public RTFParaWrap(RTFBuilder builder) 41 | { 42 | this._builder = builder; 43 | int len = this._builder._sb.Length; 44 | if (this._builder._sf.Alignment == StringAlignment.Center) 45 | { 46 | this._builder._sb.Append("\\qc"); 47 | } 48 | else if (this._builder._sf.Alignment == StringAlignment.Far) 49 | { 50 | this._builder._sb.Append("\\qr"); 51 | } 52 | if (this._builder._firstLineIndent > 0) 53 | { 54 | this._builder._sb.Append("\\fi" + this._builder._firstLineIndent); 55 | } 56 | if (this._builder._lineIndent > 0) 57 | { 58 | this._builder._sb.Append("\\li" + this._builder._lineIndent); 59 | } 60 | 61 | 62 | if (this._builder._sb.Length > len) 63 | { 64 | this._builder._sb.Append(" "); 65 | } 66 | } 67 | 68 | #endregion 69 | 70 | #region Override Methods 71 | 72 | ~RTFParaWrap() 73 | { 74 | this.Dispose(false); 75 | } 76 | 77 | #endregion 78 | 79 | #region Methods 80 | 81 | protected void Dispose(bool disposing) 82 | { 83 | if ( this._builder != null && !this._builder._unwrapped) 84 | { 85 | if (this._builder._sf.Alignment != StringAlignment.Near || this._builder._lineIndent > 0 || this._builder._firstLineIndent > 0) 86 | { 87 | this._builder._firstLineIndent = 0; 88 | this._builder._lineIndent = 0; 89 | this._builder._sf.Alignment = StringAlignment.Near; 90 | this._builder._sb.Append("\\pard "); 91 | } 92 | } 93 | if (disposing) 94 | { 95 | GC.SuppressFinalize(this); 96 | } 97 | } 98 | 99 | #endregion 100 | 101 | #region IDisposable Members 102 | 103 | public void Dispose() 104 | { 105 | this.Dispose(true); 106 | } 107 | 108 | #endregion 109 | } 110 | 111 | #endregion 112 | } 113 | } 114 | 115 | 116 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFCell.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | namespace RTF 5 | { 6 | using System; 7 | 8 | public partial class RTFBuilder 9 | { 10 | #region Nested type: RTFCell 11 | 12 | // ---------------------------------------------------------------------------------------- 13 | // _ ___ _..-._ Date: 12/11/08 23:46 14 | // \`.|\..----...-'` `-._.-'' _.-..' 15 | // / ' ` , __.-'' 16 | // )/` _/ \ `-_, / Solution: RTFLib 17 | // `-'" `"\_ ,_.-;_.-\_ ', Project : RTFLib 18 | // _.-'_./ {_.' ; / Author : Anton 19 | // {_.-``-' {_/ Assembly: 1.0.0.0 20 | // Copyright © 2005-2008, Rogue Trader/MWM 21 | // Project Item Name: RTFCell.cs - Code 22 | // Purpose: Cell In Table Row 23 | // ---------------------------------------------------------------------------------------- 24 | /// 25 | /// Cell In Table Row 26 | /// 27 | public class RTFCell : IBuilderContent 28 | { 29 | #region Fields 30 | 31 | private RTFBuilder _builder; 32 | private RTFCellDefinition _cellDefinition; 33 | private bool _firstAccessContent; 34 | 35 | #endregion 36 | 37 | #region Constructor 38 | 39 | public RTFCell(RTFBuilder builder, RTFCellDefinition cellDefinition) 40 | { 41 | this._builder = builder; 42 | this._cellDefinition = cellDefinition; 43 | this._firstAccessContent = true; 44 | } 45 | 46 | #endregion 47 | 48 | #region Override Methods 49 | 50 | ~RTFCell() 51 | { 52 | this.Dispose(false); 53 | } 54 | 55 | #endregion 56 | 57 | #region Methods 58 | 59 | protected void Dispose(bool disposing) 60 | { 61 | if (disposing && this._builder != null) 62 | { 63 | this._builder._sb.AppendLine("\\cell "); 64 | } 65 | this._builder = null; 66 | if (disposing) 67 | { 68 | GC.SuppressFinalize(this); 69 | } 70 | } 71 | 72 | #endregion 73 | 74 | #region IBuilderContent Members 75 | 76 | public void Dispose() 77 | { 78 | this.Dispose(true); 79 | } 80 | 81 | public RTFBuilderbase Content 82 | { 83 | get 84 | { 85 | if (this._firstAccessContent) 86 | { 87 | //par in table 88 | switch (this._cellDefinition.Alignment) 89 | { 90 | case RTFAlignment.TopCenter: 91 | case RTFAlignment.BottomCenter: 92 | case RTFAlignment.MiddleCenter: 93 | this._builder._sb.Append("\\qc "); 94 | break; 95 | case RTFAlignment.TopLeft: 96 | case RTFAlignment.MiddleLeft: 97 | case RTFAlignment.BottomLeft: 98 | this._builder._sb.Append("\\ql "); 99 | break; 100 | case RTFAlignment.TopRight: 101 | case RTFAlignment.BottomRight: 102 | case RTFAlignment.MiddleRight: 103 | this._builder._sb.Append("\\qr "); 104 | break; 105 | } 106 | this._firstAccessContent = false; 107 | } 108 | return this._builder; 109 | } 110 | } 111 | 112 | #endregion 113 | } 114 | 115 | #endregion 116 | } 117 | } -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/google_unofficial/GoogleTranslateProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net.Http; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Web; 8 | using ACT.FoxCommon.localization; 9 | using Newtonsoft.Json.Linq; 10 | 11 | namespace ACT.FFXIVTranslate.translate.google_unofficial 12 | { 13 | internal class GoogleTranslateProvider : DefaultTranslateProvider 14 | { 15 | private const int MaxContentLength = 2048 - 50; 16 | 17 | private readonly string _langFrom; 18 | private readonly string _langTo; 19 | 20 | public GoogleTranslateProvider(LanguageDef src, LanguageDef dst) 21 | { 22 | _langFrom = src?.LangCode ?? "auto"; 23 | _langTo = dst.LangCode; 24 | } 25 | 26 | public override void Translate(List chattingLines) 27 | { 28 | try 29 | { 30 | var current = 0; 31 | while (current < chattingLines.Count) 32 | { 33 | var start = current; 34 | 35 | if (start > 0) 36 | { 37 | // Second call, make a short delay so google might like us :) 38 | Thread.Sleep(500); 39 | } 40 | 41 | // Build text 42 | var vaildText = string.Empty; 43 | var textBuilder = new StringBuilder(); 44 | for (current = start; current < chattingLines.Count; current++) 45 | { 46 | if (textBuilder.Length > 0) 47 | { 48 | textBuilder.Append('\n'); 49 | } 50 | textBuilder.Append(chattingLines[current].CleanedContent); 51 | var newText = HttpUtility.UrlEncode(textBuilder.ToString(), Encoding.UTF8); 52 | if (newText.Length > MaxContentLength) 53 | { 54 | break; 55 | } 56 | vaildText = newText; 57 | } 58 | if (current == start) 59 | { 60 | // The single line exceeds the limit, skip 61 | chattingLines[current].TranslatedContent = "Content too long for translating."; 62 | current++; 63 | continue; 64 | } 65 | 66 | // Send request 67 | var url = 68 | $"https://translate.googleapis.com/translate_a/single?client=gtx&sl={_langFrom}&tl={_langTo}&dt=t&ie=UTF-8&oe=UTF-8&q={vaildText}"; 69 | string responseBody; 70 | using (var client = ProxyFactory.Instance.NewClient()) 71 | using (var request = new HttpRequestMessage()) 72 | { 73 | request.Method = HttpMethod.Get; 74 | request.RequestUri = new Uri(url); 75 | var response = client.SendAsync(request).Result; 76 | response.EnsureSuccessStatusCode(); 77 | responseBody = response.Content.ReadAsStringAsync().Result; 78 | } 79 | // Parse result json 80 | var results = (JArray) JArray.Parse(responseBody)[0]; 81 | var full = string.Join(string.Empty, results.Select(it => (string) it[0])); 82 | var finalResults = full.Split('\n'); 83 | if (finalResults.Length >= current - start) 84 | { 85 | for (var i = start; i < current; i++) 86 | { 87 | chattingLines[i].TranslatedContent = finalResults[i - start]; 88 | } 89 | } 90 | else 91 | { 92 | // TODO: Oops! 93 | } 94 | } 95 | } 96 | catch (TranslateException) 97 | { 98 | throw; 99 | } 100 | catch (Exception ex) 101 | { 102 | throw new TranslateException(TranslateException.ExceptionReason.UnknownError, null, ex); 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFRowDefinition.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | namespace RTF 7 | { 8 | using System.Diagnostics; 9 | using System.Drawing; 10 | using System.Windows.Forms; 11 | 12 | /// 13 | /// 14 | /// 15 | // ---------------------------------------------------------------------------------------- 16 | // _ ___ _..-._ Date: 12/11/08 23:49 17 | // \`.|\..----...-'` `-._.-'' _.-..' 18 | // / ' ` , __.-'' 19 | // )/` _/ \ `-_, / Solution: RTFLib 20 | // `-'" `"\_ ,_.-;_.-\_ ', Project : RTFLib 21 | // _.-'_./ {_.' ; / Author : Anton 22 | // {_.-``-' {_/ Assembly: 1.0.0.0 23 | // Copyright © 2005-2008, Rogue Trader/MWM 24 | // Project Item Name: RTFRowDefinition.cs - Code 25 | // Purpose: Definition of Rich Table Row 26 | // ---------------------------------------------------------------------------------------- 27 | /// 28 | /// Definition of Rich Table Row 29 | /// 30 | public struct RTFRowDefinition 31 | { 32 | #region Fields 33 | 34 | private readonly RTFAlignment _alignment; 35 | private readonly Color _borderColor; 36 | private readonly int _borderWidth; 37 | private readonly Padding _padding; 38 | private readonly RTFBorderSide _rTFBorderSide; 39 | private int _rowWidth; 40 | 41 | #endregion 42 | 43 | #region Constructor 44 | 45 | /// 46 | /// Initializes a new instance of the struct. 47 | /// 48 | /// Width of the row. 49 | /// The alignment. 50 | /// The RTFBorderSide. 51 | /// Width of the border. 52 | /// Color of the border. 53 | public RTFRowDefinition(int rowWidth, RTFAlignment alignment, RTFBorderSide rTFBorderSide, int borderWidth, Color borderColor, Padding padding) 54 | { 55 | this._padding = padding; 56 | this._alignment = alignment; 57 | this._rTFBorderSide = rTFBorderSide; 58 | this._borderWidth = borderWidth; 59 | this._borderColor = borderColor; 60 | this._rowWidth = rowWidth * RTFBuilder.TWIPSA4 / 100; 61 | } 62 | 63 | #endregion 64 | 65 | #region Public Properties 66 | 67 | /// 68 | /// Gets the alignment. 69 | /// 70 | /// The alignment. 71 | public RTFAlignment Alignment 72 | { 73 | [DebuggerStepThrough] 74 | get { return this._alignment; } 75 | } 76 | 77 | public Padding Padding 78 | { 79 | [DebuggerStepThrough] 80 | get { return this._padding; } 81 | } 82 | 83 | /// 84 | /// Gets the RTF border side. 85 | /// 86 | /// The RTF border side. 87 | public RTFBorderSide RTFBorderSide 88 | { 89 | [DebuggerStepThrough] 90 | get { return this._rTFBorderSide; } 91 | } 92 | 93 | /// 94 | /// Gets the width of the border. 95 | /// 96 | /// The width of the border. 97 | public int BorderWidth 98 | { 99 | [DebuggerStepThrough] 100 | get { return this._borderWidth; } 101 | } 102 | 103 | /// 104 | /// Gets the color of the border. 105 | /// 106 | /// The color of the border. 107 | public Color BorderColor 108 | { 109 | [DebuggerStepThrough] 110 | get { return this._borderColor; } 111 | } 112 | 113 | /// 114 | /// Gets or sets the width of the cell. 115 | /// 116 | /// The width of the cell. 117 | public int RowWidth 118 | { 119 | [DebuggerStepThrough] 120 | get { return this._rowWidth; } 121 | [DebuggerStepThrough] 122 | set { this._rowWidth = value; } 123 | } 124 | 125 | #endregion 126 | } 127 | } 128 | 129 | 130 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/WindowsMessagePump.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Windows.Forms; 4 | using ACT.FoxCommon; 5 | 6 | namespace ACT.FFXIVTranslate 7 | { 8 | class WindowsMessagePump : WindowsMessagePumpBase 9 | { 10 | 11 | private MessageOnlyWindow _window; 12 | 13 | public override void AttachToAct(FFXIVTranslatePlugin plugin) 14 | { 15 | base.AttachToAct(plugin); 16 | 17 | _window = new MessageOnlyWindow(plugin); 18 | 19 | Controller.ChannelFilterChanged += ControllerOnChannelFilterChanged; 20 | } 21 | 22 | public override void Dispose() 23 | { 24 | Controller.ChannelFilterChanged -= ControllerOnChannelFilterChanged; 25 | 26 | _window?.Dispose(); 27 | _window = null; 28 | 29 | base.Dispose(); 30 | } 31 | 32 | private void ControllerOnChannelFilterChanged(bool fromView, EventCode code, bool show) 33 | { 34 | if (code != EventCode.Clipboard) 35 | { 36 | return; 37 | } 38 | 39 | _window.EnableClipboardMonitor(show); 40 | } 41 | } 42 | 43 | sealed class MessageOnlyWindow : NativeWindow, IDisposable 44 | { 45 | 46 | // Only works on VISTA+, but since we use .net 4.5, so it's guaranteed to work 47 | [DllImport("user32.dll", SetLastError = true)] 48 | private static extern bool AddClipboardFormatListener(IntPtr hwnd); 49 | 50 | [DllImport("user32.dll", SetLastError = true)] 51 | private static extern bool RemoveClipboardFormatListener(IntPtr hwnd); 52 | 53 | [DllImport("user32.dll", CharSet = CharSet.Auto)] 54 | public static extern bool PostMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam); 55 | 56 | private const int WM_CLIPBOARDUPDATE = 0x031D; 57 | 58 | private const int WM_USER = 0x0400; 59 | private const int WM_USER_POSTCLIPBOARDUPDATE = WM_USER + 1; 60 | 61 | private readonly FFXIVTranslatePlugin _plugin; 62 | 63 | private bool _clipboardEnabled = false; 64 | 65 | public MessageOnlyWindow(FFXIVTranslatePlugin plugin) 66 | { 67 | _plugin = plugin; 68 | 69 | var cp = new CreateParams(); 70 | var HWND_MESSAGE = new IntPtr(-3); 71 | cp.Parent = HWND_MESSAGE; 72 | CreateHandle(cp); 73 | } 74 | 75 | protected override void WndProc(ref Message m) 76 | { 77 | switch (m.Msg) 78 | { 79 | case WM_CLIPBOARDUPDATE: 80 | PostMessage(Handle, WM_USER_POSTCLIPBOARDUPDATE, IntPtr.Zero, IntPtr.Zero); 81 | m.Result = IntPtr.Zero; 82 | break; 83 | case WM_USER_POSTCLIPBOARDUPDATE: 84 | var text = Clipboard.GetText(TextDataFormat.UnicodeText); 85 | if (!string.IsNullOrWhiteSpace(text)) 86 | { 87 | _plugin.Controller.NotifyClipboardContentChanged(false, text); 88 | // _plugin.Controller.NotifyLogMessageAppend(false, $"Clipboard: {text}\n"); 89 | } 90 | break; 91 | default: 92 | base.WndProc(ref m); 93 | break; 94 | } 95 | } 96 | 97 | public void Dispose() 98 | { 99 | EnableClipboardMonitor(false); 100 | DestroyHandle(); 101 | } 102 | 103 | public void EnableClipboardMonitor(bool enable) 104 | { 105 | if (_clipboardEnabled == enable) 106 | { 107 | return; 108 | } 109 | 110 | if (enable) 111 | { 112 | if (!AddClipboardFormatListener(Handle)) 113 | { 114 | _plugin.Controller.NotifyLogMessageAppend(false, "AddClipboardFormatListener() failed! \n"); 115 | return; 116 | } 117 | } 118 | else 119 | { 120 | if (!RemoveClipboardFormatListener(Handle)) 121 | { 122 | _plugin.Controller.NotifyLogMessageAppend(false, "RemoveClipboardFormatListener() failed! \n"); 123 | return; 124 | } 125 | } 126 | 127 | _clipboardEnabled = enable; 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFCellDefinition.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | namespace RTF 7 | { 8 | using System.Diagnostics; 9 | using System.Drawing; 10 | using System.Windows.Forms; 11 | 12 | // ---------------------------------------------------------------------------------------- 13 | // _ ___ _..-._ Date: 12/11/08 23:47 14 | // \`.|\..----...-'` `-._.-'' _.-..' 15 | // / ' ` , __.-'' 16 | // )/` _/ \ `-_, / Solution: RTFLib 17 | // `-'" `"\_ ,_.-;_.-\_ ', Project : RTFLib 18 | // _.-'_./ {_.' ; / Author : Anton 19 | // {_.-``-' {_/ Assembly: 1.0.0.0 20 | // Copyright © 2005-2008, Rogue Trader/MWM 21 | // Project Item Name: RTFCellDefinition.cs - Code 22 | // Purpose: Definition Of Cell In Table Row 23 | // ---------------------------------------------------------------------------------------- 24 | /// 25 | /// Definition Of Cell In Table Row 26 | /// 27 | public struct RTFCellDefinition 28 | { 29 | #region Fields 30 | 31 | private RTFAlignment _alignment; 32 | private Color _borderColor; 33 | private int _borderWidth; 34 | private float _cellWidth; 35 | private Padding _padding; 36 | private RTFBorderSide _rTFBorderSide; 37 | 38 | private int _x; 39 | 40 | #endregion 41 | 42 | #region Constructor 43 | 44 | public RTFCellDefinition(int cellwidth, RTFAlignment alignment, RTFBorderSide rTFBorderSide, int borderWidth, Color borderColor, Padding padding) 45 | { 46 | this._x = 0; 47 | this._padding = padding; 48 | this._alignment = alignment; 49 | this._rTFBorderSide = rTFBorderSide; 50 | this._borderWidth = borderWidth; 51 | this._borderColor = borderColor; 52 | this._cellWidth = (float) cellwidth / 100; 53 | } 54 | 55 | #endregion 56 | 57 | #region Public Properties 58 | 59 | /// 60 | /// Gets the alignment. 61 | /// 62 | /// The alignment. 63 | public RTFAlignment Alignment 64 | { 65 | [DebuggerStepThrough] 66 | get { return this._alignment; } 67 | [DebuggerStepThrough] 68 | set { this._alignment = value; } 69 | } 70 | 71 | /// 72 | /// Gets the RTFborderside. 73 | /// 74 | /// The RTF border side. 75 | public RTFBorderSide RTFBorderSide 76 | { 77 | [DebuggerStepThrough] 78 | get { return this._rTFBorderSide; } 79 | [DebuggerStepThrough] 80 | set { this._rTFBorderSide = value; } 81 | } 82 | 83 | /// 84 | /// Gets the width of the border. 85 | /// 86 | /// The width of the border. 87 | public int BorderWidth 88 | { 89 | [DebuggerStepThrough] 90 | get { return this._borderWidth; } 91 | [DebuggerStepThrough] 92 | set { this._borderWidth = value; } 93 | } 94 | 95 | /// 96 | /// Gets the color of the border. 97 | /// 98 | /// The color of the border. 99 | public Color BorderColor 100 | { 101 | [DebuggerStepThrough] 102 | get { return this._borderColor; } 103 | [DebuggerStepThrough] 104 | set { this._borderColor = value; } 105 | } 106 | 107 | /// 108 | /// Gets or sets the width of the cell. 109 | /// 110 | /// The width of the cell. 111 | public float CellWidthRaw 112 | { 113 | [DebuggerStepThrough] 114 | get { return this._cellWidth; } 115 | [DebuggerStepThrough] 116 | set { this._cellWidth = value; } 117 | } 118 | 119 | /// 120 | /// Gets the X. 121 | /// 122 | /// The X. 123 | public int X 124 | { 125 | [DebuggerStepThrough] 126 | get { return this._x; } 127 | } 128 | 129 | public Padding Padding 130 | { 131 | [DebuggerStepThrough] 132 | get { return this._padding; } 133 | [DebuggerStepThrough] 134 | set { this._padding = value; } 135 | } 136 | 137 | #endregion 138 | 139 | #region Public Methods 140 | 141 | [DebuggerStepThrough] 142 | public void SetX(int value) 143 | { 144 | this._x = value; 145 | } 146 | 147 | #endregion 148 | } 149 | } 150 | 151 | 152 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate.Test/ACT.FFXIVTranslate.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {81906072-43FD-40D0-916A-F16D0EDA15BA} 8 | Library 9 | Properties 10 | ACT.FFXIVTranslate.Test 11 | ACT.FFXIVTranslate.Test 12 | v4.5.2 13 | 512 14 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 15.0 16 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 17 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 18 | False 19 | UnitTest 20 | 21 | 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\Debug\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | 40 | 41 | 42 | ..\packages\MSTest.TestFramework.1.1.11\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll 43 | 44 | 45 | ..\packages\MSTest.TestFramework.1.1.11\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | {7263a6d5-a142-4f3f-92a7-c3dfa9ba45b9} 68 | ACT.FFXIVTranslate 69 | 70 | 71 | 72 | 73 | 74 | 75 | 此项目引用这台计算机上缺少的 NuGet 程序包。使用 NuGet 程序包还原可下载这些程序包。有关详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFBuilder.RTFFonts.cs: -------------------------------------------------------------------------------- 1 | namespace RTF 2 | { 3 | using System; 4 | 5 | partial class RTFBuilder 6 | { 7 | #region Nested type: RawFonts 8 | 9 | 10 | // ---------------------------------------------------------------------------------------- 11 | // _ ___ _..-._ Date: 01/11/08 21:15 12 | // \`.|\..----...-'` `-._.-'' _.-..' 13 | // / ' ` , __.-'' 14 | // )/` _/ \ `-_, / Solution: RTFLib 15 | // `-'" `"\_ ,_.-;_.-\_ ', Project : RTFLib 16 | // _.-'_./ {_.' ; / Author : Anton 17 | // {_.-``-' {_/ Assembly: 1.3.0.2499 18 | // Copyright © 2005-2008, Rogue Trader 19 | // Project Item Name: RTFBuilder.RTFFonts.cs - [PROJECT_ITEM_KIND] 20 | // Purpose: Used internally by RtfBuilderBase 21 | // ---------------------------------------------------------------------------------------- 22 | 23 | 24 | internal static class RawFonts 25 | { 26 | #region Fields 27 | 28 | public const string Arial = @"{{\f{0}\fswiss\fprq2\fcharset0 Arial;}}"; 29 | public const string ArialBlack = @"{{\f{0}\fswiss\fprq2\fcharset0 Arial Black;}}"; 30 | public const string BookmanOldStyle = @"{{\f{0}\froman\fprq2\fcharset0 Bookman Old Style;}}"; 31 | public const string Broadway = @"{{\f{0}\fdecor\fprq2\fcharset0 Broadway;}}"; 32 | public const string CenturyGothic = @"{{\f{0}\fswiss\fprq2\fcharset0 Century Gothic;}}"; 33 | public const string Consolas = @"{{\f{0}\fmodern\fprq1\fcharset0 Consolas;}}"; 34 | public const string CordiaNew = @"{{\f{0}\fswiss\fprq2\fcharset0 Cordia New;}}"; 35 | public const string CourierNew = @"{{\f{0}\fmodern\fprq1\fcharset0 Courier New;}}"; 36 | public const string FontTimesNewRoman = @"{{\f{0}\froman\fcharset0 Times New Roman;}}"; 37 | public const string Garamond = @"{{\f{0}\froman\fprq2\fcharset0 Garamond;}}"; 38 | public const string Georgia = @"{{\f{0}\froman\fprq2\fcharset0 Georgia;}}"; 39 | public const string Impact = @"{{\f{0}\fswiss\fprq2\fcharset0 Impact;}}"; 40 | public const string LucidaConsole = @"{{\f{0}\fmodern\fprq1\fcharset0 Lucida Console;}}"; 41 | public const string MSSansSerif = "{{\f{0}\fswiss\fprq2\fcharset0 MS Reference Sans Serif;}}"; 42 | public const string Symbol = @"{{\f{0}\ftech\fcharset0 Symbol;}}"; 43 | public const string WingDings = "{{\f{0}\fnil\fprq2\fcharset2 Wingdings;}}"; 44 | 45 | #endregion 46 | 47 | #region Static Methods 48 | 49 | public static string GetKnownFontstring(RTFFont font) 50 | { 51 | string value = string.Empty; 52 | switch (font) 53 | { 54 | case RTFFont.Arial: 55 | value = Arial; 56 | break; 57 | case RTFFont.ArialBlack: 58 | value = ArialBlack; 59 | break; 60 | case RTFFont.BookmanOldStyle: 61 | value = BookmanOldStyle; 62 | break; 63 | case RTFFont.Broadway: 64 | value = Broadway; 65 | break; 66 | case RTFFont.CenturyGothic: 67 | value = CenturyGothic; 68 | break; 69 | case RTFFont.Consolas: 70 | value = Consolas; 71 | break; 72 | case RTFFont.CordiaNew: 73 | value = CordiaNew; 74 | break; 75 | case RTFFont.CourierNew: 76 | value = CourierNew; 77 | break; 78 | case RTFFont.FontTimesNewRoman: 79 | value = FontTimesNewRoman; 80 | break; 81 | case RTFFont.Garamond: 82 | value = Garamond; 83 | break; 84 | case RTFFont.Georgia: 85 | value = Georgia; 86 | break; 87 | case RTFFont.Impact: 88 | value = Impact; 89 | break; 90 | case RTFFont.LucidaConsole: 91 | value = LucidaConsole; 92 | break; 93 | case RTFFont.Symbol: 94 | value = Symbol; 95 | break; 96 | case RTFFont.WingDings: 97 | value = WingDings; 98 | break; 99 | case RTFFont.MSSansSerif: 100 | value = MSSansSerif; 101 | break; 102 | default: 103 | throw new ArgumentOutOfRangeException("font"); 104 | } 105 | return value; 106 | } 107 | 108 | #endregion 109 | } 110 | 111 | #endregion 112 | } 113 | } -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/microsoft/AzureAuthToken.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | 5 | namespace ACT.FFXIVTranslate.translate.microsoft 6 | { 7 | /// 8 | /// Client to call Cognitive Services Azure Auth Token service in order to get an access token. 9 | /// Exposes asynchronous as well as synchronous methods. 10 | /// 11 | public class AzureAuthToken 12 | { 13 | /// URL of the token service 14 | private static readonly Uri ServiceUrl = new Uri("https://api.cognitive.microsoft.com/sts/v1.0/issueToken"); 15 | 16 | /// Name of header used to pass the subscription key to the token service 17 | private const string OcpApimSubscriptionKeyHeader = "Ocp-Apim-Subscription-Key"; 18 | 19 | /// After obtaining a valid token, this class will cache it for this duration. 20 | /// Use a duration of 5 minutes, which is less than the actual token lifetime of 10 minutes. 21 | private static readonly TimeSpan TokenCacheDuration = new TimeSpan(0, 5, 0); 22 | 23 | /// Cache the value of the last valid token obtained from the token service. 24 | private string _storedTokenValue = string.Empty; 25 | 26 | /// When the last valid token was obtained. 27 | private DateTime _storedTokenTime = DateTime.MinValue; 28 | 29 | /// Gets the subscription key. 30 | public string SubscriptionKey { get; } 31 | 32 | /// Gets the HTTP status code for the most recent request to the token service. 33 | public HttpStatusCode RequestStatusCode { get; private set; } 34 | 35 | /// 36 | /// Creates a client to obtain an access token. 37 | /// 38 | /// Subscription key to use to get an authentication token. 39 | public AzureAuthToken(string key) 40 | { 41 | if (string.IsNullOrEmpty(key)) 42 | { 43 | throw new ArgumentNullException(nameof(key), "A subscription key is required"); 44 | } 45 | 46 | this.SubscriptionKey = key; 47 | this.RequestStatusCode = HttpStatusCode.InternalServerError; 48 | } 49 | 50 | /// 51 | /// Gets a token for the specified subscription. 52 | /// 53 | /// The encoded JWT token prefixed with the string "Bearer ". 54 | /// 55 | /// This method uses a cache to limit the number of request to the token service. 56 | /// A fresh token can be re-used during its lifetime of 10 minutes. After a successful 57 | /// request to the token service, this method caches the access token. Subsequent 58 | /// invocations of the method return the cached token for the next 5 minutes. After 59 | /// 5 minutes, a new token is fetched from the token service and the cache is updated. 60 | /// 61 | public string GetAccessToken() 62 | { 63 | if (string.IsNullOrWhiteSpace(this.SubscriptionKey)) 64 | { 65 | return string.Empty; 66 | } 67 | 68 | // Re-use the cached token if there is one. 69 | if ((DateTime.Now - _storedTokenTime) < TokenCacheDuration) 70 | { 71 | return _storedTokenValue; 72 | } 73 | 74 | using (var client = ProxyFactory.Instance.NewClient()) 75 | using (var request = new HttpRequestMessage()) 76 | { 77 | request.Method = HttpMethod.Post; 78 | request.RequestUri = ServiceUrl; 79 | request.Content = new StringContent(string.Empty); 80 | request.Headers.TryAddWithoutValidation(OcpApimSubscriptionKeyHeader, this.SubscriptionKey); 81 | client.Timeout = TimeSpan.FromSeconds(2); 82 | var response = client.SendAsync(request).Result; 83 | this.RequestStatusCode = response.StatusCode; 84 | response.EnsureSuccessStatusCode(); 85 | var token = response.Content.ReadAsStringAsync().Result; 86 | _storedTokenTime = DateTime.Now; 87 | _storedTokenValue = "Bearer " + token; 88 | return _storedTokenValue; 89 | } 90 | } 91 | 92 | // /// 93 | // /// Gets a token for the specified subscription. Synchronous version. 94 | // /// Use of async version preferred 95 | // /// 96 | // /// The encoded JWT token prefixed with the string "Bearer ". 97 | // /// 98 | // /// This method uses a cache to limit the number of request to the token service. 99 | // /// A fresh token can be re-used during its lifetime of 10 minutes. After a successful 100 | // /// request to the token service, this method caches the access token. Subsequent 101 | // /// invocations of the method return the cached token for the next 5 minutes. After 102 | // /// 5 minutes, a new token is fetched from the token service and the cache is updated. 103 | // /// 104 | // public string GetAccessToken() 105 | // { 106 | // // Re-use the cached token if there is one. 107 | // if ((DateTime.Now - _storedTokenTime) < TokenCacheDuration) 108 | // { 109 | // return _storedTokenValue; 110 | // } 111 | // 112 | // string accessToken = null; 113 | // var task = Task.Factory.StartNew(async () => 114 | // { 115 | // accessToken = this.GetAccessTokenAsync().Result; 116 | // }); 117 | // 118 | // while (!task.IsCompleted) 119 | // { 120 | // System.Threading.Thread.Yield(); 121 | // } 122 | // if (task.IsFaulted) 123 | // { 124 | // throw task.Exception; 125 | // } 126 | // if (task.IsCanceled) 127 | // { 128 | // throw new Exception("Timeout obtaining access token."); 129 | // } 130 | // return accessToken; 131 | // } 132 | 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/microsoft/MicrosoftTranslateProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Text; 6 | using ACT.FoxCommon.localization; 7 | using Newtonsoft.Json; 8 | 9 | namespace ACT.FFXIVTranslate.translate.microsoft 10 | { 11 | internal class RequestItem 12 | { 13 | public readonly string Text; 14 | 15 | public RequestItem(string text) 16 | { 17 | Text = text; 18 | } 19 | } 20 | 21 | internal class ResponseItem 22 | { 23 | internal class TranslationItem 24 | { 25 | public string text; 26 | } 27 | 28 | public List translations; 29 | } 30 | 31 | internal class MicrosoftTranslateProvider : DefaultTranslateProvider 32 | { 33 | private readonly AzureAuthToken _token; 34 | private readonly string _langFrom; 35 | private readonly string _langTo; 36 | 37 | 38 | public MicrosoftTranslateProvider(string apiKey, LanguageDef src, LanguageDef dst) 39 | { 40 | _token = new AzureAuthToken(apiKey); 41 | _langFrom = src?.LangCode; 42 | _langTo = dst.LangCode; 43 | } 44 | 45 | private const string ServiceUri = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0"; 46 | 47 | public override void Translate(List chattingLines) 48 | { 49 | try 50 | { 51 | // Build text 52 | var uri = $"{ServiceUri}&from={_langFrom}&to={_langTo}"; 53 | Console.WriteLine("Uri is: {0}.", uri); 54 | 55 | var bodyContent = new List(); 56 | foreach (var line in chattingLines) 57 | { 58 | bodyContent.Add(new RequestItem(line.CleanedContent)); 59 | } 60 | var requestBody = JsonConvert.SerializeObject(bodyContent, Formatting.None); 61 | Console.WriteLine("Request body is: {0}.", requestBody); 62 | 63 | // Call Microsoft translate API. 64 | DoRequest(GetAuthToken(), uri, requestBody, out string responseBody, out HttpStatusCode statusCode); 65 | 66 | // Parse result 67 | switch (statusCode) 68 | { 69 | case HttpStatusCode.OK: 70 | Console.WriteLine("Request status is OK. Response body is:"); 71 | Console.WriteLine(responseBody); 72 | Console.WriteLine("Result of translate array method is:"); 73 | 74 | var response = JsonConvert.DeserializeObject>(responseBody); 75 | var sourceTextCounter = 0; 76 | foreach (var line in response) 77 | { 78 | var result = line.translations[0].text; 79 | Console.WriteLine("\n\nSource text: {0}\nTranslated Text: {1}", chattingLines[sourceTextCounter].RawContent, result); 80 | chattingLines[sourceTextCounter].TranslatedContent = result; 81 | sourceTextCounter++; 82 | } 83 | break; 84 | case HttpStatusCode.Unauthorized: 85 | throw new TranslateException(TranslateException.ExceptionReason.InvalidApiKey, 86 | "Invalid API key", 87 | null); 88 | default: 89 | Console.WriteLine("Request status code is: {0}.", statusCode); 90 | Console.WriteLine("Request error message: {0}.", responseBody); 91 | throw new TranslateException(TranslateException.ExceptionReason.GeneralServiceError, 92 | responseBody, null); 93 | } 94 | } 95 | catch (TranslateException) 96 | { 97 | throw; 98 | } 99 | catch (Exception ex) 100 | { 101 | throw new TranslateException(TranslateException.ExceptionReason.UnknownError, null, ex); 102 | } 103 | } 104 | 105 | private string GetAuthToken() 106 | { 107 | string authToken; 108 | try 109 | { 110 | authToken = _token.GetAccessToken(); 111 | } 112 | catch (HttpRequestException ex) 113 | { 114 | switch (_token.RequestStatusCode) 115 | { 116 | case HttpStatusCode.Unauthorized: 117 | throw new TranslateException(TranslateException.ExceptionReason.InvalidApiKey, 118 | "Invalid API key", 119 | null); 120 | case HttpStatusCode.Forbidden: 121 | throw new TranslateException(TranslateException.ExceptionReason.ApiLimitExceed, 122 | "Account quota has been exceeded.", 123 | null); 124 | default: 125 | throw new TranslateException(TranslateException.ExceptionReason.GeneralServiceError, 126 | null, ex); 127 | } 128 | } 129 | 130 | return authToken; 131 | } 132 | 133 | private void DoRequest(string authToken, string uri, string requestBody, 134 | out string responseBody, out HttpStatusCode statusCode) 135 | { 136 | string _responseBody; 137 | HttpStatusCode _statusCode; 138 | 139 | ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; 140 | 141 | using (var client = ProxyFactory.Instance.NewClient()) 142 | using (var request = new HttpRequestMessage()) 143 | { 144 | request.Method = HttpMethod.Post; 145 | request.RequestUri = new Uri(uri); 146 | request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json"); 147 | request.Headers.Add("Authorization", authToken); 148 | var response = client.SendAsync(request).Result; 149 | _responseBody = response.Content.ReadAsStringAsync().Result; 150 | _statusCode = response.StatusCode; 151 | } 152 | 153 | responseBody = _responseBody; 154 | statusCode = _statusCode; 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/youdao/YoudaoTranslateProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Text; 7 | using ACT.FoxCommon; 8 | using ACT.FoxCommon.localization; 9 | using Newtonsoft.Json.Linq; 10 | 11 | namespace ACT.FFXIVTranslate.translate.youdao 12 | { 13 | internal class YoudaoTranslateProvider : DefaultTranslateProvider 14 | { 15 | private readonly string _appKey; 16 | private readonly string _secret; 17 | private readonly string _langFrom; 18 | private readonly string _langTo; 19 | 20 | 21 | public YoudaoTranslateProvider(string apiKey, LanguageDef src, LanguageDef dst) 22 | { 23 | var kp = apiKey.Split(':'); 24 | if (kp.Length < 2) 25 | { 26 | _appKey = ""; 27 | _secret = ""; 28 | } 29 | else 30 | { 31 | _appKey = kp[0]; 32 | _secret = kp[1]; 33 | } 34 | _langFrom = src?.LangCode ?? "auto"; 35 | _langTo = dst.LangCode; 36 | } 37 | 38 | public override void Translate(List chattingLines) 39 | { 40 | try 41 | { 42 | // Build text 43 | var query = string.Join("\n", chattingLines.Select(it => it.CleanedContent)); 44 | 45 | // 1. Generate a salt 46 | var salt = Utils.TimestampMillisFromDateTime(DateTime.Now).ToString(); 47 | // 2. Calculate sign 48 | var md5Input = Encoding.UTF8.GetBytes($"{_appKey}{query}{salt}{_secret}"); 49 | var md5 = System.Security.Cryptography.MD5.Create(); 50 | var hash = md5.ComputeHash(md5Input); 51 | var sign = string.Join("", hash.Select(it => it.ToString("x2"))); 52 | 53 | string textResponse; 54 | using (var client = ProxyFactory.Instance.NewClient()) 55 | using (var request = new HttpRequestMessage(HttpMethod.Post, "/api")) 56 | { 57 | client.BaseAddress = new Uri("https://openapi.youdao.com"); 58 | 59 | var keyValues = new List>(); 60 | keyValues.Add(new KeyValuePair("q", query)); 61 | keyValues.Add(new KeyValuePair("from", _langFrom)); 62 | keyValues.Add(new KeyValuePair("to", _langTo)); 63 | keyValues.Add(new KeyValuePair("appKey", _appKey)); 64 | keyValues.Add(new KeyValuePair("salt", salt)); 65 | keyValues.Add(new KeyValuePair("sign", sign)); 66 | 67 | request.Content = new FormUrlEncodedContent(keyValues); 68 | var response = client.SendAsync(request).Result; 69 | response.EnsureSuccessStatusCode(); 70 | textResponse = response.Content.ReadAsStringAsync().Result; 71 | Debug.WriteLine(textResponse); 72 | } 73 | 74 | // read json 75 | var result = JObject.Parse(textResponse); 76 | var errorCode = (string) result["errorCode"]; 77 | switch (errorCode) 78 | { 79 | case "0": // This one is good 80 | break; 81 | case "102": 82 | throw new TranslateException(TranslateException.ExceptionReason.DirectionNotSupported, 83 | "Language Not Supported", null); 84 | case "103": 85 | throw new TranslateException(TranslateException.ExceptionReason.GeneralServiceError, 86 | "Query Content Too Long", null); 87 | case "101": 88 | case "104": 89 | case "105": 90 | case "106": 91 | case "107": 92 | case "109": 93 | case "110": 94 | case "201": 95 | throw new TranslateException(TranslateException.ExceptionReason.InternalError, 96 | $"Error {errorCode}.", null); 97 | case "108": 98 | throw new TranslateException(TranslateException.ExceptionReason.InvalidApiKey, 99 | "AppId Error", null); 100 | case "111": 101 | case "401": 102 | throw new TranslateException(TranslateException.ExceptionReason.ApiLimitExceed, 103 | "Insufficient Account Balance", null); 104 | case "202": 105 | throw new TranslateException(TranslateException.ExceptionReason.InvalidApiKey, 106 | "Secret Error", null); 107 | case "203": 108 | throw new TranslateException(TranslateException.ExceptionReason.GeneralServiceError, 109 | "Limited IP", 110 | null); 111 | 112 | case "301": 113 | case "302": 114 | case "303": 115 | throw new TranslateException(TranslateException.ExceptionReason.GeneralServiceError, 116 | $"Error {errorCode}.", null); 117 | default: 118 | throw new TranslateException(TranslateException.ExceptionReason.GeneralServiceError, 119 | (string) result["error_msg"], null); 120 | } 121 | 122 | var translation = ((string) result["translation"][0]).Split('\n'); 123 | if (translation.Length >= chattingLines.Count) 124 | { 125 | for (var i = 0; i < chattingLines.Count; i++) 126 | { 127 | chattingLines[i].TranslatedContent = translation[i]; 128 | } 129 | } 130 | else 131 | { 132 | // TODO: Oops! 133 | } 134 | } 135 | catch (TranslateException) 136 | { 137 | throw; 138 | } 139 | catch (Exception ex) 140 | { 141 | throw new TranslateException(TranslateException.ExceptionReason.UnknownError, null, ex); 142 | } 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/baidu/BaiduTranslateProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Text; 7 | using ACT.FFXIVTranslate.localization; 8 | using ACT.FoxCommon; 9 | using ACT.FoxCommon.localization; 10 | using Newtonsoft.Json.Linq; 11 | 12 | namespace ACT.FFXIVTranslate.translate.baidu 13 | { 14 | internal class BaiduTranslateProvider : DefaultTranslateProvider 15 | { 16 | private readonly string _appId; 17 | private readonly string _secret; 18 | private readonly string _langFrom; 19 | private readonly string _langTo; 20 | 21 | 22 | public BaiduTranslateProvider(string apiKey, LanguageDef src, LanguageDef dst) 23 | { 24 | var kp = apiKey.Split(':'); 25 | if (kp.Length < 2) 26 | { 27 | _appId = ""; 28 | _secret = ""; 29 | } 30 | else 31 | { 32 | _appId = kp[0]; 33 | _secret = kp[1]; 34 | } 35 | _langFrom = src?.LangCode ?? "auto"; 36 | _langTo = dst.LangCode; 37 | } 38 | 39 | public override void Translate(List chattingLines) 40 | { 41 | try 42 | { 43 | // Build text 44 | var query = string.Join("\n", chattingLines.Select(it => it.CleanedContent)); 45 | 46 | // 1. Generate a salt 47 | var salt = Utils.TimestampMillisFromDateTime(DateTime.Now).ToString(); 48 | // 2. Calculate sign 49 | var md5Input = Encoding.UTF8.GetBytes($"{_appId}{query}{salt}{_secret}"); 50 | var md5 = System.Security.Cryptography.MD5.Create(); 51 | var hash = md5.ComputeHash(md5Input); 52 | var sign = string.Join("", hash.Select(it => it.ToString("x2"))); 53 | 54 | string textResponse; 55 | using (var client = ProxyFactory.Instance.NewClient()) 56 | using (var request = new HttpRequestMessage(HttpMethod.Post, "/api/trans/vip/translate")) 57 | { 58 | client.BaseAddress = new Uri("https://fanyi-api.baidu.com"); 59 | 60 | var keyValues = new List>(); 61 | keyValues.Add(new KeyValuePair("q", query)); 62 | keyValues.Add(new KeyValuePair("from", _langFrom)); 63 | keyValues.Add(new KeyValuePair("to", _langTo)); 64 | keyValues.Add(new KeyValuePair("appid", _appId)); 65 | keyValues.Add(new KeyValuePair("salt", salt)); 66 | keyValues.Add(new KeyValuePair("sign", sign)); 67 | 68 | request.Content = new FormUrlEncodedContent(keyValues); 69 | var response = client.SendAsync(request).Result; 70 | response.EnsureSuccessStatusCode(); 71 | textResponse = response.Content.ReadAsStringAsync().Result; 72 | Debug.WriteLine(textResponse); 73 | } 74 | 75 | // read json 76 | var result = JObject.Parse(textResponse); 77 | if (((IDictionary) result).ContainsKey("error_code")) 78 | { 79 | // Something wrong 80 | var errorCode = (string)result["error_code"]; 81 | switch (errorCode) 82 | { 83 | case "52000": // This one is good 84 | break; 85 | case "52001": 86 | case "52002": 87 | throw new TranslateException(TranslateException.ExceptionReason.GeneralServiceError, 88 | (string) result["error_msg"], null); 89 | case "52003": 90 | throw new TranslateException(TranslateException.ExceptionReason.InvalidApiKey, 91 | "AppId Error", null); 92 | case "54000": 93 | throw new TranslateException(TranslateException.ExceptionReason.InternalError, 94 | "API Params Missing", null); 95 | case "54001": 96 | throw new TranslateException(TranslateException.ExceptionReason.InvalidApiKey, 97 | "Secret Error", null); 98 | case "54003": 99 | case "54005": 100 | throw new TranslateException(TranslateException.ExceptionReason.ApiLimitExceed, 101 | "Too Frequently", null); 102 | case "54004": 103 | throw new TranslateException(TranslateException.ExceptionReason.ApiLimitExceed, 104 | "Insufficient Account Balance", null); 105 | case "58000": 106 | throw new TranslateException(TranslateException.ExceptionReason.GeneralServiceError, 107 | "Limited IP", 108 | null); 109 | case "58001": 110 | throw new TranslateException(TranslateException.ExceptionReason.DirectionNotSupported, 111 | (string) result["error_msg"], null); 112 | default: 113 | throw new TranslateException(TranslateException.ExceptionReason.GeneralServiceError, 114 | (string) result["error_msg"], null); 115 | } 116 | } 117 | 118 | var transResult = (JArray) result["trans_result"]; 119 | if (transResult.Count >= chattingLines.Count) 120 | { 121 | for (var i = 0; i < chattingLines.Count; i++) 122 | { 123 | chattingLines[i].TranslatedContent = (string) transResult[i]["dst"]; 124 | } 125 | } 126 | else 127 | { 128 | // TODO: Oops! 129 | } 130 | } 131 | catch (TranslateException) 132 | { 133 | throw; 134 | } 135 | catch (Exception ex) 136 | { 137 | throw new TranslateException(TranslateException.ExceptionReason.UnknownError, null, ex); 138 | } 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/yandax/YandaxTranslateProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Runtime.Serialization; 7 | using System.Runtime.Serialization.Json; 8 | using System.Text; 9 | using System.Xml; 10 | using ACT.FoxCommon.localization; 11 | 12 | namespace ACT.FFXIVTranslate.translate.yandax 13 | { 14 | internal class YandaxTranslateProvider : DefaultTranslateProvider 15 | { 16 | private readonly string _apiKey; 17 | private readonly string _lang; 18 | 19 | public YandaxTranslateProvider(string apiKey, LanguageDef src, LanguageDef dst) 20 | { 21 | _apiKey = apiKey; 22 | _lang = src == null ? dst.LangCode : $"{src.LangCode}-{dst.LangCode}"; 23 | } 24 | 25 | public override void Translate(List chattingLines) 26 | { 27 | try 28 | { 29 | // Build text 30 | var textBuilder = new StringBuilder(); 31 | var settings = new XmlWriterSettings {OmitXmlDeclaration = true}; 32 | var textWriter = XmlWriter.Create(textBuilder, settings); 33 | textWriter.WriteStartElement("lines"); 34 | foreach (var line in chattingLines) 35 | { 36 | textWriter.WriteElementString("line", line.CleanedContent); 37 | } 38 | textWriter.WriteEndElement(); 39 | textWriter.Flush(); 40 | textWriter.Close(); 41 | 42 | var text = textBuilder.ToString(); 43 | string textResponse; 44 | using (var client = ProxyFactory.Instance.NewClient()) 45 | using (var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1.5/tr.json/translate")) 46 | { 47 | client.BaseAddress = new Uri("https://translate.yandex.net"); 48 | 49 | var keyValues = new List>(); 50 | keyValues.Add(new KeyValuePair("key", _apiKey)); 51 | keyValues.Add(new KeyValuePair("text", text)); 52 | keyValues.Add(new KeyValuePair("lang", _lang)); 53 | keyValues.Add(new KeyValuePair("options", "1")); 54 | keyValues.Add(new KeyValuePair("format", "html")); 55 | 56 | request.Content = new FormUrlEncodedContent(keyValues); 57 | var response = client.SendAsync(request).Result; 58 | textResponse = response.Content.ReadAsStringAsync().Result; 59 | } 60 | 61 | // read json 62 | var ser = new DataContractJsonSerializer(typeof(YandexTranslateResult)); 63 | var result = 64 | (YandexTranslateResult) 65 | ser.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(textResponse))); 66 | 67 | switch (result.Code) 68 | { 69 | case 200: 70 | break; 71 | 72 | // Faild 73 | case 401: 74 | throw new TranslateException(TranslateException.ExceptionReason.InvalidApiKey, 75 | "Invalid API key", 76 | null); 77 | case 402: 78 | throw new TranslateException(TranslateException.ExceptionReason.InvalidApiKey, 79 | "Blocked API key", 80 | null); 81 | case 404: 82 | throw new TranslateException(TranslateException.ExceptionReason.ApiLimitExceed, 83 | "Exceeded the daily limit on the amount of translated text", null); 84 | case 413: 85 | throw new TranslateException(TranslateException.ExceptionReason.ApiLimitExceed, 86 | "Exceeded the maximum text size", null); 87 | case 422: 88 | throw new TranslateException(TranslateException.ExceptionReason.GeneralServiceError, 89 | "The text cannot be translated", null); 90 | case 501: 91 | throw new TranslateException(TranslateException.ExceptionReason.DirectionNotSupported, 92 | "The specified translation direction is not supported", null); 93 | default: 94 | throw new TranslateException(TranslateException.ExceptionReason.GeneralServiceError, 95 | $"Code = {result.Code}, Msg = {result.Message}", null); 96 | } 97 | 98 | 99 | var translatedLinesXml = result.Text[0]; 100 | // Parse xml 101 | var doc = new XmlDocument(); 102 | doc.LoadXml(translatedLinesXml); 103 | var nodes = doc.SelectNodes("lines/line"); 104 | if (nodes == null || nodes.Count != chattingLines.Count) 105 | { 106 | // Error 107 | throw new TranslateException(TranslateException.ExceptionReason.InternalError, 108 | "Translate result count mismatch.", null); 109 | } 110 | 111 | foreach (var p in chattingLines.Zip(nodes.Cast(), 112 | (a, b) => new KeyValuePair(a, b))) 113 | { 114 | p.Key.TranslatedContent = p.Value.InnerText; 115 | } 116 | } 117 | catch (TranslateException) 118 | { 119 | throw; 120 | } 121 | catch (Exception ex) 122 | { 123 | throw new TranslateException(TranslateException.ExceptionReason.UnknownError, null, ex); 124 | } 125 | } 126 | 127 | [DataContract] 128 | internal class YandexTranslateResult 129 | { 130 | [DataContract] 131 | internal class DetectedLang 132 | { 133 | [DataMember(Name = "lang", IsRequired = true)] internal string Lang; 134 | } 135 | 136 | [DataMember(Name = "code", IsRequired = true)] internal int Code; 137 | 138 | [DataMember(Name = "message", IsRequired = false)] internal string Message; 139 | 140 | [DataMember(Name = "detected", IsRequired = false)] internal DetectedLang Detected; 141 | 142 | [DataMember(Name = "lang", IsRequired = false)] internal string Lang; 143 | 144 | [DataMember(Name = "text", IsRequired = false)] internal string[] Text; 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/TranslateProviderPanel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows.Forms; 4 | using ACT.FFXIVTranslate.localization; 5 | using ACT.FoxCommon.dpi; 6 | using ACT.FoxCommon.localization; 7 | 8 | namespace ACT.FFXIVTranslate.translate 9 | { 10 | public partial class TranslateProviderPanel : UserControl, IPluginComponent 11 | { 12 | private FFXIVTranslatePlugin _plugin; 13 | private TranslateService _service; 14 | 15 | public TranslateProviderPanel() 16 | { 17 | InitializeComponent(); 18 | comboBoxProvider.DisplayMember = nameof(ITranslateProviderFactory.ProviderName); 19 | comboBoxProvider.ValueMember = nameof(ITranslateProviderFactory.ProviderId); 20 | comboBoxLangFrom.DisplayMember = nameof(LanguageDef.DisplayName); 21 | comboBoxLangFrom.ValueMember = nameof(LanguageDef.LangCode); 22 | comboBoxLangTo.DisplayMember = nameof(LanguageDef.DisplayName); 23 | comboBoxLangTo.ValueMember = nameof(LanguageDef.LangCode); 24 | 25 | this.AdjustForDpiScaling(); 26 | } 27 | 28 | public void AttachToAct(FFXIVTranslatePlugin plugin) 29 | { 30 | _service = plugin.TranslateService; 31 | _plugin = plugin; 32 | 33 | var controller = plugin.Controller; 34 | controller.TranslateProviderChanged += ControllerOnTranslateProviderChanged; 35 | controller.LegalInfoChanged += ControllerOnLegalInfoChanged; 36 | } 37 | 38 | public void PostAttachToAct(FFXIVTranslatePlugin plugin) 39 | { 40 | } 41 | 42 | public void DoLocalization() 43 | { 44 | comboBoxProvider.DataSource = _service.AllProviders; 45 | } 46 | 47 | private void comboBoxProvider_SelectedIndexChanged(object sender, EventArgs e) 48 | { 49 | var selectedProvider = (ITranslateProviderFactory) comboBoxProvider.SelectedItem; 50 | 51 | if (selectedProvider != null) 52 | { 53 | // Update language selector 54 | comboBoxLangFrom.DataSource = selectedProvider.SupportAutoDetect 55 | ? new[] { new LanguageDef(LanguageDef.CodeAuto, strings.LangAutoDetect, string.Empty) }.Concat(selectedProvider.SupportedSrcLanguages).ToList() 56 | : selectedProvider.SupportedSrcLanguages; 57 | 58 | UpdateSupportedDestLanguage(); 59 | } 60 | } 61 | 62 | private void UpdateSupportedDestLanguage() 63 | { 64 | var selectedProvider = (ITranslateProviderFactory) comboBoxProvider.SelectedItem; 65 | var selectedFromLang = (LanguageDef) comboBoxLangFrom.SelectedItem; 66 | 67 | if (selectedProvider != null && selectedFromLang != null) 68 | { 69 | comboBoxLangTo.DataSource = selectedProvider.GetSupportedDestLanguages(selectedFromLang); 70 | } 71 | } 72 | 73 | private void ControllerOnTranslateProviderChanged(bool fromView, string provider, string apiKey, string langFrom, string langTo) 74 | { 75 | if (fromView) 76 | { 77 | return; 78 | } 79 | 80 | if (string.IsNullOrEmpty(provider)) 81 | { 82 | comboBoxProvider.SelectedIndex = 0; 83 | } 84 | else 85 | { 86 | comboBoxProvider.SelectedValue = provider; 87 | if (comboBoxProvider.SelectedIndex == -1) 88 | { 89 | comboBoxProvider.SelectedIndex = 0; 90 | } 91 | } 92 | 93 | if (!string.IsNullOrEmpty(apiKey)) 94 | { 95 | textBoxApiKey.Text = apiKey; 96 | } 97 | 98 | if (string.IsNullOrEmpty(langFrom)) 99 | { 100 | comboBoxLangFrom.SelectedIndex = 0; 101 | } 102 | else 103 | { 104 | comboBoxLangFrom.SelectedValue = langFrom; 105 | if (comboBoxLangFrom.SelectedIndex == -1) 106 | { 107 | comboBoxLangFrom.SelectedIndex = 0; 108 | } 109 | } 110 | 111 | if (string.IsNullOrEmpty(langTo)) 112 | { 113 | comboBoxLangTo.SelectedIndex = 0; 114 | } 115 | else 116 | { 117 | comboBoxLangTo.SelectedValue = langTo; 118 | if (comboBoxLangTo.SelectedIndex == -1) 119 | { 120 | comboBoxLangTo.SelectedIndex = 0; 121 | } 122 | } 123 | _plugin.Controller.NotifyTranslateProviderChanged(true, (string)comboBoxProvider.SelectedValue, 124 | textBoxApiKey.Text, (string)comboBoxLangFrom.SelectedValue, (string)comboBoxLangTo.SelectedValue); 125 | } 126 | 127 | private void ControllerOnLegalInfoChanged(bool fromView, ProviderLegalInfo info) 128 | { 129 | var label = info?.LabelResult; 130 | if (label != null) 131 | { 132 | linkLabelPowered.Visible = true; 133 | linkLabelPowered.Text = label; 134 | linkLabelPowered.Tag = info.LabelResultLink; 135 | } 136 | else 137 | { 138 | linkLabelPowered.Visible = false; 139 | linkLabelPowered.Tag = null; 140 | } 141 | } 142 | 143 | private void buttonProviderApply_Click(object sender, EventArgs e) 144 | { 145 | _plugin.Controller.NotifyTranslateProviderChanged(true, (string) comboBoxProvider.SelectedValue, 146 | textBoxApiKey.Text, (string) comboBoxLangFrom.SelectedValue, (string) comboBoxLangTo.SelectedValue); 147 | } 148 | 149 | private void linkLabelPowered_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 150 | { 151 | if (linkLabelPowered.Tag is string link) 152 | { 153 | System.Diagnostics.Process.Start(link); 154 | } 155 | } 156 | 157 | private void buttonFreeKey_Click(object sender, EventArgs e) 158 | { 159 | 160 | var selectedProvider = (ITranslateProviderFactory) comboBoxProvider.SelectedItem; 161 | 162 | if (selectedProvider != null) 163 | { 164 | var key = selectedProvider.DefaultPublicKey; 165 | if (string.IsNullOrEmpty(key)) 166 | { 167 | key = string.Empty; 168 | MessageBox.Show(strings.messageNoFreeKey, strings.actPanelTitle, 169 | MessageBoxButtons.OK, MessageBoxIcon.Asterisk); 170 | } 171 | textBoxApiKey.Text = key; 172 | } 173 | } 174 | 175 | private void ComboBoxLangFrom_SelectedIndexChanged(object sender, EventArgs e) 176 | { 177 | UpdateSupportedDestLanguage(); 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFCellDefinitionBuilder.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | namespace RTF 7 | { 8 | using System.Text; 9 | using System.Windows.Forms; 10 | 11 | public partial class RTFBuilder 12 | { 13 | #region Nested type: RTFCellDefinitionBuilder 14 | 15 | // ---------------------------------------------------------------------------------------- 16 | // _ ___ _..-._ Date: 12/11/08 23:47 17 | // \`.|\..----...-'` `-._.-'' _.-..' 18 | // / ' ` , __.-'' 19 | // )/` _/ \ `-_, / Solution: RTFLib 20 | // `-'" `"\_ ,_.-;_.-\_ ', Project : RTFLib 21 | // _.-'_./ {_.' ; / Author : Anton 22 | // {_.-``-' {_/ Assembly: 1.0.0.0 23 | // Copyright © 2005-2008, Rogue Trader/MWM 24 | // Project Item Name: RTFCellDefinitionBuilder.cs - Code 25 | // Purpose: Injects Cell Rtf Codes 26 | // ---------------------------------------------------------------------------------------- 27 | /// 28 | /// Injects Cell Rtf Codes 29 | /// 30 | private class RTFCellDefinitionBuilder 31 | { 32 | #region Fields 33 | 34 | private readonly RTFBuilder _builder; 35 | private readonly StringBuilder _definitionBuilder; 36 | private RTFCellDefinition _cellDefinition; 37 | 38 | #endregion 39 | 40 | #region Constructor 41 | 42 | internal RTFCellDefinitionBuilder(RTFBuilder builder, StringBuilder definitionBuilder, RTFCellDefinition cellDefinition) 43 | { 44 | this._builder = builder; 45 | 46 | this._definitionBuilder = definitionBuilder; 47 | this._cellDefinition = cellDefinition; 48 | 49 | 50 | this.AppendDefinition(); 51 | } 52 | 53 | #endregion 54 | 55 | #region Public Properties 56 | 57 | public RTFCellDefinition CellDefinition 58 | { 59 | get { return this._cellDefinition; } 60 | } 61 | 62 | #endregion 63 | 64 | #region Methods 65 | 66 | private void AppendDefinition() 67 | { 68 | this.CellAlignment(); 69 | this.TableCellBorderSide(); 70 | //Pad(); 71 | 72 | 73 | this._definitionBuilder.AppendFormat("\\cellx{0}", (int) (this._cellDefinition.CellWidthRaw * TWIPSA4) + this._cellDefinition.X); 74 | //_definitionBuilder.AppendFormat("\\clwWidth{0}", _cellDefinition.CellWidth); 75 | 76 | //_definitionBuilder.Append("\\cltxlrtb\\clFitText"); 77 | 78 | this._definitionBuilder.AppendLine(); 79 | 80 | 81 | //Cell text flow 82 | } 83 | 84 | private string BorderDef() 85 | { 86 | StringBuilder sb = new StringBuilder(); 87 | RTFBorderSide _rTFBorderSide = this._cellDefinition.RTFBorderSide; 88 | if ((_rTFBorderSide & RTFBorderSide.DoubleThickness) == RTFBorderSide.DoubleThickness) 89 | { 90 | sb.Append("\\brdrth"); 91 | } 92 | else 93 | { 94 | sb.Append("\\brdrs"); 95 | } 96 | if ((_rTFBorderSide & RTFBorderSide.DoubleBorder) == RTFBorderSide.DoubleBorder) 97 | { 98 | sb.Append("\\brdrdb"); 99 | } 100 | sb.Append("\\brdrw"); 101 | sb.Append(this._cellDefinition.BorderWidth); 102 | 103 | sb.Append("\\brdrcf"); 104 | sb.Append(this._builder.IndexOf(this._cellDefinition.BorderColor)); 105 | 106 | return sb.ToString(); 107 | } 108 | 109 | private void CellAlignment() 110 | { 111 | switch (this._cellDefinition.Alignment) 112 | { 113 | case RTFAlignment.BottomCenter: 114 | case RTFAlignment.BottomLeft: 115 | case RTFAlignment.BottomRight: 116 | this._definitionBuilder.Append("\\clvertalb"); //\\qr 117 | break; 118 | case RTFAlignment.MiddleCenter: 119 | case RTFAlignment.MiddleLeft: 120 | case RTFAlignment.MiddleRight: 121 | this._definitionBuilder.Append("\\clvertalc"); //\\qr 122 | break; 123 | case RTFAlignment.TopCenter: 124 | case RTFAlignment.TopLeft: 125 | case RTFAlignment.TopRight: 126 | this._definitionBuilder.Append("\\clvertalt"); //\\qr 127 | break; 128 | } 129 | } 130 | 131 | private void Pad() 132 | { 133 | if (this._cellDefinition.Padding != Padding.Empty) 134 | { 135 | StringBuilder sb = this._definitionBuilder; 136 | sb.AppendFormat("\\clpadfl3\\clpadl{0}", this._cellDefinition.Padding.Left); 137 | sb.AppendFormat("\\clpadlr3\\clpadr{0}", this._cellDefinition.Padding.Right); 138 | sb.AppendFormat("\\clpadlt3\\clpadt{0}", this._cellDefinition.Padding.Top); 139 | sb.AppendFormat("\\clpadlb3\\clpadb{0}", this._cellDefinition.Padding.Bottom); 140 | } 141 | } 142 | 143 | private void TableCellBorderSide() 144 | { 145 | RTFBorderSide _rTFBorderSide = this._cellDefinition.RTFBorderSide; 146 | 147 | if (_rTFBorderSide != RTFBorderSide.None) 148 | { 149 | StringBuilder sb = this._definitionBuilder; 150 | string bd = this.BorderDef(); 151 | if (_rTFBorderSide == RTFBorderSide.None) 152 | { 153 | sb.Append("\\brdrnil"); 154 | } 155 | else 156 | { 157 | if ((_rTFBorderSide & RTFBorderSide.Left) == RTFBorderSide.Left) 158 | { 159 | sb.Append("\\clbrdrl").Append(bd); 160 | } 161 | if ((_rTFBorderSide & RTFBorderSide.Right) == RTFBorderSide.Right) 162 | { 163 | sb.Append("\\clbrdrr").Append(bd); 164 | } 165 | if ((_rTFBorderSide & RTFBorderSide.Top) == RTFBorderSide.Top) 166 | { 167 | sb.Append("\\clbrdrt").Append(bd); 168 | } 169 | if ((_rTFBorderSide & RTFBorderSide.Bottom) == RTFBorderSide.Bottom) 170 | { 171 | sb.Append("\\clbrdrb").Append(bd); 172 | } 173 | } 174 | } 175 | } 176 | 177 | #endregion 178 | } 179 | 180 | #endregion 181 | } 182 | } 183 | 184 | 185 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://ci.appveyor.com/api/projects/status/oy9yi49qnsk26hiq/branch/master?svg=true)](https://ci.appveyor.com/project/Noisyfox/act-ffxivtranslate/branch/master) 2 | 3 | # Help Translate this Plugin! 4 | The plugin UI now support Chinese (Simplified) and English (US) only. 5 | 6 | If you speak another language or speak English / Chinese better than me and wanna help, 7 | please visit: 8 | https://noisyfox.oneskyapp.com/collaboration/project/128830 9 | 10 | Currently I plan to support Russian and Chinese (Tranditional). If you'd like this plugin 11 | to support your language, please raise an Issue and I can add more languages! 12 | 13 | Any help would be appreciated! 14 | 15 | # ACT.FFXIVTranslate 16 | 这是一个用来翻译最终幻想14国际服玩家聊天内容的ACT插件。 17 | 18 | # 如何使用 19 | 1. 从这里下载最新版的插件 https://github.com/Noisyfox/ACT.FFXIVTranslate/releases 20 | 2. 将7z压缩包内的全部文件解压并覆盖到ACT的安装目录(ACT可执行文件本体所在目录)下。 21 | 3. 打开ACT,在 功能插件 -> 插件列表 选项卡中添加并启用ACT.FFXIVTranslate.dll插件。 22 | 4. 将悬浮窗拖拽到合适的位置,大功告成! 23 | 24 | # 翻译来源设置 25 | 目前本插件支持以下六个在线翻译API: 26 | - Yandax.Translate 27 | - 百度翻译 28 | - Microsoft Translator 29 | - 谷歌翻译非官方版 30 | - 有道翻译 31 | - 腾讯翻译 32 | 33 | 其中谷歌翻译非官方版不需要输入API key就可以使用。 34 | 我为 Yandax.Translate 与 Microsoft Translator 这两个翻译API都申请了免费的API key提供给大家测试或者临时使用。 35 | 由于百度翻译和有道翻译会在免费额度用完后立刻开始收费,因此在此不提供试用key,大家可以按照下文的说明自行申请API key(非常简单)。 36 | 37 | 插件默认使用Yandax.Translate(老毛子的翻译),你可以在**来源**下拉列表中选择不同的翻译API。 38 | 39 | 点击**使用公用密钥**按钮会自动根据当前选择的翻译API设置我提供的免费公开的API密钥,或者你可以输入自己申请的API密钥。 40 | 41 | 请记得每次更改完翻译来源设置后要点击**应用**按钮,否则更改不会被保存。 42 | 43 | # 获取你自己的API密钥 44 | 我提供的免费密钥是有各种使用限制的,你可以申请属于自己的API密钥来避免遇到这些限制。 45 | 46 | ## Yandax.Translate 47 | 1. 访问 https://tech.yandex.com/translate/ 48 | 2. 点击页面中的 **Get a free API key.** 链接。 49 | 3. 遵循网页的提示一步步操作,你就可以拥有属于你自己的API密钥啦。 50 | 51 | ## Microsoft Translator 52 | 1. 访问 https://portal.azure.com/ 53 | 2. 注册并登录Azure门户。(中国用户请注意,请不要切换到中国区,我不知道中国区的Key能不能用) 54 | 3. 创建一个 **Translator 文本 API** 实例: 55 | 1. 点击页面左上角的加号按钮。 56 | 2. 搜索 **Translator 文本 API**,在搜索结果中点击该项,然后点击 **创建**。 57 | 3. 输入所有必填项,然后点击 **创建** 按钮。记得**定价层**这一项要选**F0**不然不是免费的。 58 | 4. 创建完实例后,在Azure门户中找到这个实例并进入详情页面。 59 | 5. 点击页面左侧的 **Keys** 链接,你会看到两个KEY。这两个KEY你可以任选一个来用。 60 | 61 | ## 百度翻译 62 | 1. 访问 http://api.fanyi.baidu.com/api/trans/product/index 63 | 2. 点击页面中的 **申请接入** 按钮,如有需要请登录你的百度账号。 64 | 3. 根据页面提示输入所需资料并继续。 65 | 4. 当提示**API接口权限申请成功**时,你可以在下方找到你的 *APP ID* 和 *密钥*。 66 | 5. 在本插件的**API密钥**框中输入 *APP ID*:*密钥*,注意分隔符为半角冒号,前后均无任何空格,请不要输错。 67 | 68 | ## 有道翻译 69 | 1. 访问 http://ai.youdao.com/index.s 并登录。 70 | 2. 页面左侧选择**自然语言翻译**->**翻译实例**。 71 | 3. 点击**创建实例**按钮,按照提示创建好一个实例。 72 | 4. 页面左侧选择**应用管理**->**我的应用**。 73 | 5. 点击**创建应用**按钮,按照提示创建好一个应用。在创建应用完成后的**应用实例添加**对话框中,选中刚才创建好的**自然语言翻译服务**的实例,并点击**提交更改**按钮。 74 | 6. 在**应用详情**页面可以看到应用的 *应用ID* 以及 *应用密钥*。 75 | 7. 在本插件的**API密钥**框中输入 *应用ID*:*应用密钥*,注意分隔符为半角冒号,前后均无任何空格,请不要输错。 76 | 77 | ## 腾讯翻译 78 | 1. 访问 https://cloud.tencent.com/product/tmt/, 点击 **立即使用** 按钮。按提示登录腾讯云。 79 | 2. 首次使用会提示要开通机器翻译功能,按提示开通免费版本(当然你愿意可以开收费版:P)。 80 | 3. 开通机器翻译后,访问 https://console.cloud.tencent.com/cam/capi 并点击 **新建密钥** 创建一组 *SecretId* 和 *SecretKey*。 81 | - 可能会看到一个关于使用子用户的高风险提示,如果你只是个人使用那么可以点击 **继续使用**。 82 | - **请勿将自己的密钥共享给他人!该密钥拥有完整的腾讯云权限,随意共享可能导致您的财产损失!** 83 | - **请勿将自己的密钥共享给他人!该密钥拥有完整的腾讯云权限,随意共享可能导致您的财产损失!** 84 | - **请勿将自己的密钥共享给他人!该密钥拥有完整的腾讯云权限,随意共享可能导致您的财产损失!** 85 | - *如果您熟悉腾讯云的权限管理机制的话,您是可以创建出一个能够安全共享的密钥,然而该操作比较复杂,除非您知道自己在做什么,否则***绝对不要***与任何人共享您的密钥!* 86 | - **共享密钥导致的任何损失,本人不承担任何责任!** 87 | - **本插件在任何情况下都不会与任何人共享您输入的密钥。** 88 | - **如果您要截图分享您的插件设置,请注意给自己的密钥打码!** 89 | 4. 在本插件的 **API密钥** 框中输入 *SecretId*:*SecretKey*,注意分隔符为半角冒号,前后均无任何空格,请不要输错。 90 | 91 | # Q & A 92 | > Q: 启用该插件后我的ACT窗口变小了! 93 | 94 | 请参考 [https://github.com/Noisyfox/ACT.FFXIVTranslate/wiki/Fix-ACT-window-scale-down](https://github.com/Noisyfox/ACT.FFXIVTranslate/wiki/Fix-ACT-window-scale-down) 95 | 96 | > Q: 我用的无猫整合版 ACT,没有办法加载插件,肿么办 97 | 98 | 访问 [http://bbs.nga.cn/read.php?tid=12526945](http://bbs.nga.cn/read.php?tid=12526945),找到 __ACT插件自整合__ 分类的百度网盘地址,选择 __单体插件自整合 > 6. 其他工具__,使用其中的 99 | __ACT绿色化工具(配置移根目录).rar__ 处理即可。 100 | 101 | # TODO 102 | - ~~加入显示开关~~ 103 | - 加入谷歌翻译官方API支持。 104 | - ~~加入谷歌翻译非官方API支持。~~ 105 | - ~~加入http代理支持。~~ 106 | - ~~加入socks5代理支持。~~ 107 | - ~~在翻译窗口中显示聊天的频道。不同频道的聊天内容会用不同颜色来显示,就和游戏里一样。~~ 108 | - (不太容易) 正常显示 *定型文字*。 109 | - ~~显示消息时间。~~ 110 | - ~~当游戏窗口非激活状态时自动隐藏悬浮窗。~~ 111 | - 以后想到啥再加咯 :P 112 | 113 | ------- 114 | # ACT.FFXIVTranslate 115 | An ACT plugin to translate palyers' chatting for Final Fantasy XIV 116 | 117 | # How To 118 | 1. Download the latest version of the plugin from https://github.com/Noisyfox/ACT.FFXIVTranslate/releases 119 | 2. Extract all files inside the .7z archive, put them under your ACT installation directry, 120 | where you can find the ACT's main executable file. Replace any existing files. 121 | 3. Open ACT, add and enable ACT.FFXIVTranslate.dll in Plugins -> Plugin Listing page. 122 | 4. Drag the translation window to your prefer location and enjoy! 123 | 124 | # Translator Provider Settings 125 | Currently this plugin support 6 providers: 126 | - Yandax.Translate 127 | - Baidu Translator 128 | - Microsoft Translator 129 | - Unofficial Google Translate 130 | - Youdao Translator 131 | - Tencent Translator 132 | 133 | The unofficial Google Translate doesn't require an API key. 134 | Yandax.Translate and Microsoft Translator providers are bundled with a free public API key for testing / temporarily using. 135 | For Baidu & Youdao Translator users, I assume you are Chinese people so please read the Chinese part above :P 136 | 137 | The default provider is Yandax.Translate, you could change it by selecting a different one from the **Provider** combo box. 138 | 139 | Click **Use Free Key** button to set the API key to current provider's default key or you could enter your own API key. 140 | 141 | Remember to click **Apply** button if you changed any provider settings otherwise the change won't take effect until next time 142 | the ACT starts. 143 | 144 | # Obtain your own API key 145 | The free api keys have limits on words per month. You may want to get your own personal API keys to avoid those limits. 146 | 147 | ## Yandax.Translate 148 | 1. Visit https://tech.yandex.com/translate/ 149 | 2. Click the link **Get a free API key.** on that page. 150 | 3. Follow the instructions and you will get your own API key for free. 151 | 152 | ## Microsoft Translator 153 | 1. Visit https://portal.azure.com/ 154 | 2. Register and / or login to Azure portal. 155 | 3. Create a **Translator Text API** instance: 156 | 1. Click the Add icon at the top left bar. 157 | 2. Search **Translator Text API**, click the item from search result, and click **Create**. 158 | 3. Fill in all the request information and click **Click**. Remember to select **F0** plan for 159 | pricing otherwise you will be charged. 160 | 4. After creating the instance, find it in Azure protal and go to the detail page. 161 | 5. Click the link **Keys** at the left side, and you will find two keys. Any of those should be work. 162 | 163 | # Q & A 164 | > Q: My ACT window get resized after enable this plugin 165 | 166 | See https://github.com/Noisyfox/ACT.FFXIVTranslate/wiki/Fix-ACT-window-scale-down 167 | 168 | # TODO 169 | - ~~Add switch to show / hide the overlay~~ 170 | - Add official Google Translate support. 171 | - ~~Add unofficial Google Translate support.~~ 172 | - ~~Add http proxy support.~~ 173 | - ~~Add socks5 proxy support.~~ 174 | - ~~Display chatting channel. Have different colors for different channels, just like the game does.~~ 175 | - (unlikely) Display *Auto-Translate*. 176 | - ~~Show message time.~~ 177 | - ~~Hide overlay when game window is not active.~~ 178 | - More to come :P 179 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/MainController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Drawing; 3 | using ACT.FFXIVTranslate.translate; 4 | using ACT.FoxCommon.core; 5 | 6 | namespace ACT.FFXIVTranslate 7 | { 8 | public class MainController : MainControllerBase 9 | { 10 | 11 | public delegate void OnOverlayMovedDelegate(bool fromView, int x, int y); 12 | 13 | public event OnOverlayMovedDelegate OverlayMoved; 14 | 15 | public void NotifyOverlayMoved(bool fromView, int x, int y) 16 | { 17 | OverlayMoved?.Invoke(fromView, x, y); 18 | } 19 | 20 | public delegate void OnOverlayResizedDelegate(bool fromView, int w, int h); 21 | 22 | public event OnOverlayResizedDelegate OverlayResized; 23 | 24 | public void NotifyOverlayResized(bool fromView, int w, int h) 25 | { 26 | OverlayResized?.Invoke(fromView, w, h); 27 | } 28 | 29 | public delegate void OnOpacityChangedDelegate(bool fromView, double value); 30 | 31 | public event OnOpacityChangedDelegate OpacityChanged; 32 | 33 | public void NotifyOpacityChanged(bool fromView, double value) 34 | { 35 | OpacityChanged?.Invoke(fromView, value); 36 | } 37 | 38 | public delegate void OnClickthroughChangedDelegate(bool fromView, bool clickthrough); 39 | 40 | public event OnClickthroughChangedDelegate ClickthroughChanged; 41 | 42 | public void NotifyClickthroughChanged(bool fromView, bool clickthrough) 43 | { 44 | ClickthroughChanged?.Invoke(fromView, clickthrough); 45 | } 46 | 47 | public delegate void OnBatchTranslateCompletedDelegate(bool fromView, List chattingLines); 48 | 49 | public event OnBatchTranslateCompletedDelegate BatchTranslateCompleted; 50 | 51 | public void NotifyBatchTranslateCompleted(bool fromView, List chattingLines) 52 | { 53 | BatchTranslateCompleted?.Invoke(fromView, chattingLines); 54 | } 55 | 56 | public delegate void OnOverlayContentUpdatedDelegate(bool fromView, string content); 57 | 58 | public event OnOverlayContentUpdatedDelegate OverlayContentUpdated; 59 | 60 | public void NotifyOverlayContentUpdated(bool fromView, string content) 61 | { 62 | OverlayContentUpdated?.Invoke(fromView, content); 63 | } 64 | 65 | public delegate void OnProviderChangedDelegate( 66 | bool fromView, string provider, string apiKey, string langFrom, string langTo); 67 | 68 | public event OnProviderChangedDelegate TranslateProviderChanged; 69 | 70 | public void NotifyTranslateProviderChanged(bool fromView, string provider, string apiKey, string langFrom, 71 | string langTo) 72 | { 73 | TranslateProviderChanged?.Invoke(fromView, provider, apiKey, langFrom, langTo); 74 | } 75 | 76 | public delegate void OnChannelFilterChangedDelegate(bool fromView, EventCode code, bool show); 77 | 78 | public event OnChannelFilterChangedDelegate ChannelFilterChanged; 79 | 80 | public void NotifyChannelFilterChanged(bool fromView, EventCode code, bool show) 81 | { 82 | ChannelFilterChanged?.Invoke(fromView, code, show); 83 | } 84 | 85 | public delegate void OnChannelLabelChangedDelegate(bool fromView, EventCode code, bool show); 86 | 87 | public event OnChannelLabelChangedDelegate ChannelLabelChanged; 88 | 89 | public void NotifyChannelLabelChanged(bool fromView, EventCode code, bool show) 90 | { 91 | ChannelLabelChanged?.Invoke(fromView, code, show); 92 | } 93 | 94 | public delegate void OnChannelColorChangedDelegate(bool fromView, EventCode code, Color color); 95 | 96 | public event OnChannelColorChangedDelegate ChannelColorChanged; 97 | 98 | public void NotifyChannelColorChanged(bool fromView, EventCode code, Color color) 99 | { 100 | ChannelColorChanged?.Invoke(fromView, code, color); 101 | } 102 | 103 | public delegate void OnLanguageChangedDelegate(bool fromView, string lang); 104 | 105 | public event OnLanguageChangedDelegate LanguageChanged; 106 | 107 | public void NotifyLanguageChanged(bool fromView, string lang) 108 | { 109 | LanguageChanged?.Invoke(fromView, lang); 110 | } 111 | 112 | public delegate void OnOverlayFontChangedDelegate(bool fromView, Font font); 113 | 114 | public event OnOverlayFontChangedDelegate OverlayFontChanged; 115 | 116 | public void NotifyOverlayFontChanged(bool fromView, Font font) 117 | { 118 | OverlayFontChanged?.Invoke(fromView, font); 119 | } 120 | 121 | public delegate void OnLegalInfoChangedDelegate(bool fromView, ProviderLegalInfo info); 122 | 123 | public event OnLegalInfoChangedDelegate LegalInfoChanged; 124 | 125 | public void NotifyLegalInfoChanged(bool fromView, ProviderLegalInfo info) 126 | { 127 | LegalInfoChanged?.Invoke(fromView, info); 128 | } 129 | 130 | public delegate void OnShowOverlayChangedDelegate(bool fromView, bool showOverlay); 131 | 132 | public event OnShowOverlayChangedDelegate ShowOverlayChanged; 133 | 134 | public void NotifyShowOverlayChanged(bool fromView, bool showOverlay) 135 | { 136 | ShowOverlayChanged?.Invoke(fromView, showOverlay); 137 | } 138 | 139 | public delegate void OnOverlayAutoHideChangedDelegate(bool fromView, bool autoHide); 140 | 141 | public event OnOverlayAutoHideChangedDelegate OverlayAutoHideChanged; 142 | 143 | public void NotifyOverlayAutoHideChanged(bool fromView, bool autoHide) 144 | { 145 | OverlayAutoHideChanged?.Invoke(fromView, autoHide); 146 | } 147 | 148 | public delegate void OnAddTimestampChangedDelegate(bool fromView, bool show); 149 | 150 | public event OnAddTimestampChangedDelegate AddTimestampChanged; 151 | 152 | public void NotifyAddTimestampChanged(bool fromView, bool show) 153 | { 154 | AddTimestampChanged?.Invoke(fromView, show); 155 | } 156 | 157 | public delegate void OnTimestampFormatChangedDelegate(bool fromView, bool is24Hour); 158 | 159 | public event OnTimestampFormatChangedDelegate TimestampFormatChanged; 160 | 161 | public void NotifyTimestampFormatChanged(bool fromView, bool is24Hour) 162 | { 163 | TimestampFormatChanged?.Invoke(fromView, is24Hour); 164 | } 165 | 166 | public delegate void OnClipboardContentChangedDelegate(bool fromView, string newContent); 167 | 168 | public event OnClipboardContentChangedDelegate ClipboardContentChanged; 169 | 170 | public void NotifyClipboardContentChanged(bool fromView, string newContent) 171 | { 172 | ClipboardContentChanged?.Invoke(fromView, newContent); 173 | } 174 | 175 | public delegate void OnProxyChangedDelegate(bool fromView, string type, string server, int port, 176 | string user, string password, string domain); 177 | 178 | public event OnProxyChangedDelegate ProxyChanged; 179 | 180 | public void NotifyProxyChanged(bool fromView, string type, string server, int port, 181 | string user, string password, string domain) 182 | { 183 | ProxyChanged?.Invoke(fromView, type, server, port, user, password, domain); 184 | } 185 | 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /MSBuild.ILMerge.Task.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(SolutionDir)packages 6 | $(MSBuildProjectDirectory)\ILMergeOrder.txt 7 | $(AssemblyOriginatorKeyFile) 8 | 9 | 10 | 11 | false 12 | false 13 | false 14 | true 15 | false 16 | 512 17 | false 18 | 19 | false 20 | true 21 | true 22 | 23 | false 24 | 40 25 | 26 | 27 | $(MSBuildThisFileDirectory)..\tools\ 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | @(IntermediateAssembly->'%(FullPath)');@(MergedAssemblies->'%(FullPath)');@(MergedDependencies->'%(FullPath)') 57 | 58 | 59 | @(IntermediateAssembly->'%(FullPath)');@(MergedAssemblies->'%(FullPath)') 60 | 61 | 62 | 63 | 64 | 65 | @(UnmergedAssemblies->'%(FullPath)') 66 | $(TargetPath) 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFBuilder.RTFFormatWrap.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | namespace RTF 7 | { 8 | using System; 9 | using System.Drawing; 10 | using System.Text; 11 | 12 | partial class RTFBuilder 13 | { 14 | #region Nested type: RTFFormatWrap 15 | 16 | // ---------------------------------------------------------------------------------------- 17 | // _ ___ _..-._ Date: 12/11/08 23:35 18 | // \`.|\..----...-'` `-._.-'' _.-..' 19 | // / ' ` , __.-'' 20 | // )/` _/ \ `-_, / Solution: RTFLib 21 | // `-'" `"\_ ,_.-;_.-\_ ', Project : RTFLib 22 | // _.-'_./ {_.' ; / Author : Anton 23 | // {_.-``-' {_/ Assembly: 1.0.0.0 24 | // Copyright © 2005-2008, Rogue Trader/MWM 25 | // Project Item Name: RTFBuilder.RTFFormatWrap.cs - Code 26 | // Purpose: Wraps RTFBuilderbase for formatting changes allowing injection of appropriate rtf codes to revert format after each Append (string) call 27 | // ---------------------------------------------------------------------------------------- 28 | /// 29 | /// Wraps RTFBuilderbase for formatting changes allowing injection of appropriate rtf codes to revert format after each Append (string) call 30 | /// 31 | private class RTFFormatWrap : IDisposable 32 | { 33 | #region Fields 34 | 35 | private readonly RTFBuilder _builder; 36 | 37 | #endregion 38 | 39 | #region Constructor 40 | 41 | public RTFFormatWrap(RTFBuilder builder) 42 | { 43 | this._builder = builder; 44 | if (this._builder._unwrapped) 45 | { 46 | return; 47 | } 48 | 49 | StringBuilder sb = this._builder._sb; 50 | 51 | int len = this._builder._sb.Length; 52 | 53 | if (this._builder._sf.Alignment == StringAlignment.Center) 54 | { 55 | sb.Append("\\qc"); 56 | } 57 | else if (this._builder._sf.Alignment == StringAlignment.Far) 58 | { 59 | sb.Append("\\qr"); 60 | } 61 | if ((this._builder._fontStyle & System.Drawing.FontStyle.Bold) == System.Drawing.FontStyle.Bold) 62 | { 63 | sb.Append("\\b"); 64 | } 65 | if ((this._builder._fontStyle & System.Drawing.FontStyle.Italic) == System.Drawing.FontStyle.Italic) 66 | { 67 | sb.Append("\\i"); 68 | } 69 | if ((this._builder._fontStyle & System.Drawing.FontStyle.Underline) == System.Drawing.FontStyle.Underline) 70 | { 71 | sb.Append("\\ul"); 72 | } 73 | if ((this._builder._fontStyle & System.Drawing.FontStyle.Strikeout) == System.Drawing.FontStyle.Strikeout) 74 | { 75 | sb.Append("\\strike"); 76 | } 77 | 78 | if (this._builder._fontSize != this._builder.DefaultFontSize) 79 | { 80 | sb.AppendFormat("\\fs{0}", this._builder._fontSize); 81 | } 82 | if (this._builder._font != 0) 83 | { 84 | sb.AppendFormat("\\f{0}", this._builder._font); 85 | } 86 | if (this._builder._forecolor != this._builder.Defaultforecolor) 87 | { 88 | sb.AppendFormat("\\cf{0}", this._builder.IndexOf(this._builder._forecolor)); 89 | } 90 | if (this._builder._backcolor != this._builder.DefaultBackColor) 91 | { 92 | sb.AppendFormat("\\highlight{0}", this._builder.IndexOf(this._builder._backcolor)); 93 | } 94 | 95 | 96 | if (sb.Length > len) 97 | { 98 | sb.Append(" "); 99 | } 100 | } 101 | 102 | #endregion 103 | 104 | #region Override Methods 105 | 106 | ~RTFFormatWrap() 107 | { 108 | this.Dispose(false); 109 | } 110 | 111 | #endregion 112 | 113 | #region Methods 114 | 115 | protected void Dispose(bool disposing) 116 | { 117 | if (this._builder != null && !this._builder._unwrapped) 118 | { 119 | 120 | StringBuilder sb = this._builder._sb; 121 | 122 | int len = sb.Length; 123 | if ((this._builder._fontStyle & System.Drawing.FontStyle.Bold) == System.Drawing.FontStyle.Bold) 124 | { 125 | sb.Append("\\b0"); 126 | } 127 | if ((this._builder._fontStyle & System.Drawing.FontStyle.Italic) == System.Drawing.FontStyle.Italic) 128 | { 129 | sb.Append("\\i0"); 130 | } 131 | if ((this._builder._fontStyle & System.Drawing.FontStyle.Underline) == System.Drawing.FontStyle.Underline) 132 | { 133 | sb.Append("\\ulnone"); 134 | } 135 | if ((this._builder._fontStyle & System.Drawing.FontStyle.Strikeout) == System.Drawing.FontStyle.Strikeout) 136 | { 137 | sb.Append("\\strike0"); 138 | } 139 | 140 | this._builder._fontStyle = System.Drawing.FontStyle.Regular; 141 | 142 | if (this._builder._fontSize != this._builder.DefaultFontSize) 143 | { 144 | this._builder._fontSize = this._builder.DefaultFontSize; 145 | sb.AppendFormat("\\fs{0} ", this._builder.DefaultFontSize); 146 | } 147 | if (this._builder._font != 0) 148 | { 149 | sb.Append("\\f0"); 150 | this._builder._font = 0; 151 | } 152 | 153 | if (this._builder._forecolor != this._builder.Defaultforecolor) 154 | { 155 | this._builder._forecolor = this._builder.Defaultforecolor; 156 | sb.Append("\\cf0"); 157 | } 158 | if (this._builder._backcolor != this._builder.DefaultBackColor) 159 | { 160 | this._builder._backcolor = this._builder.DefaultBackColor; 161 | sb.Append("\\highlight0"); 162 | } 163 | //if (_builder._alignment != StringAlignment.Near ) 164 | //{ 165 | // _builder._alignment = StringAlignment.Near; 166 | // sb.Append("\\ql"); 167 | //} 168 | if (sb.Length > len) 169 | { 170 | sb.Append(" "); 171 | } 172 | } 173 | if (disposing) 174 | { 175 | GC.SuppressFinalize(this); 176 | } 177 | } 178 | 179 | #endregion 180 | 181 | #region IDisposable Members 182 | 183 | public void Dispose() 184 | { 185 | this.Dispose(true); 186 | } 187 | 188 | #endregion 189 | } 190 | 191 | #endregion 192 | } 193 | } 194 | 195 | 196 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/SettingsHolder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Drawing; 4 | using System.Windows.Forms; 5 | using ACT.FoxCommon.core; 6 | using ACT.FoxCommon.shortcut; 7 | using Advanced_Combat_Tracker; 8 | 9 | namespace ACT.FFXIVTranslate 10 | { 11 | /// 12 | /// https://github.com/anoyetta/act_timeline/blob/origin/src/PluginSettings.cs 13 | /// 14 | internal class PluginSettings : SettingsSerializer 15 | { 16 | private readonly SettingsIO _settingsIo = new SettingsIO("ACT.FFXIVTranslate"); 17 | 18 | public PluginSettings(object ParentSettingsClass) : base(ParentSettingsClass) 19 | { 20 | _settingsIo.WriteSettings = writer => 21 | { 22 | writer.WriteStartElement("SettingsSerializer"); 23 | ExportToXml(writer); 24 | writer.WriteEndElement(); 25 | }; 26 | 27 | _settingsIo.ReadSettings = reader => 28 | { 29 | switch (reader.LocalName) 30 | { 31 | case "SettingsSerializer": 32 | ImportFromXml(reader); 33 | break; 34 | } 35 | }; 36 | } 37 | 38 | public void Load() 39 | { 40 | _settingsIo.Load(); 41 | } 42 | 43 | public void Save() 44 | { 45 | _settingsIo.Save(); 46 | } 47 | } 48 | 49 | public class SettingsHolder : IPluginComponent 50 | { 51 | #region Proxy methods 52 | 53 | private PluginSettings Settings { get; } 54 | 55 | public SettingsHolder() 56 | { 57 | Settings = new PluginSettings(this); 58 | } 59 | 60 | public void AddControlSetting(Control controlToSerialize) 61 | { 62 | Settings.AddControlSetting(controlToSerialize.Name, controlToSerialize); 63 | } 64 | 65 | public void Load() 66 | { 67 | Settings.Load(); 68 | } 69 | 70 | public void Save() 71 | { 72 | Settings.Save(); 73 | } 74 | 75 | #endregion 76 | 77 | #region Settings 78 | 79 | public string TranslateProvider { get; set; } 80 | 81 | public string TranslateApiKey { get; set; } 82 | 83 | public string TranslateLangFrom { get; set; } 84 | 85 | public string TranslateLangTo { get; set; } 86 | 87 | public string Language { get; set; } 88 | 89 | public string OverlayFont { get; set; } 90 | 91 | public string ProxyType { get; set; } 92 | 93 | public string ProxyServer { get; set; } 94 | 95 | public int ProxyPort { get; set; } 96 | 97 | public string ProxyUser { get; set; } 98 | 99 | public string ProxyPassword { get; set; } 100 | 101 | public string ProxyDomain { get; set; } 102 | 103 | public string VersionIgnored { get; set; } 104 | 105 | public string ShortcutHide { get; set; } 106 | 107 | #endregion 108 | 109 | #region Controller notify 110 | 111 | private MainController _controller; 112 | 113 | public void AttachToAct(FFXIVTranslatePlugin plugin) 114 | { 115 | Settings.AddStringSetting(nameof(TranslateProvider)); 116 | Settings.AddStringSetting(nameof(TranslateApiKey)); 117 | Settings.AddStringSetting(nameof(TranslateLangFrom)); 118 | Settings.AddStringSetting(nameof(TranslateLangTo)); 119 | Settings.AddStringSetting(nameof(Language)); 120 | Settings.AddStringSetting(nameof(OverlayFont)); 121 | Settings.AddStringSetting(nameof(ProxyType)); 122 | Settings.AddStringSetting(nameof(ProxyServer)); 123 | Settings.AddIntSetting(nameof(ProxyPort)); 124 | Settings.AddStringSetting(nameof(ProxyUser)); 125 | Settings.AddStringSetting(nameof(ProxyPassword)); 126 | Settings.AddStringSetting(nameof(ProxyDomain)); 127 | Settings.AddStringSetting(nameof(VersionIgnored)); 128 | Settings.AddStringSetting(nameof(ShortcutHide)); 129 | 130 | _controller = plugin.Controller; 131 | 132 | _controller.LanguageChanged += ControllerOnLanguageChanged; 133 | _controller.OverlayFontChanged += ControllerOnOverlayFontChanged; 134 | _controller.TranslateProviderChanged += ControllerOnTranslateProviderChanged; 135 | _controller.ProxyChanged += ControllerOnProxyChanged; 136 | _controller.NewVersionIgnored += ControllerOnNewVersionIgnored; 137 | _controller.ShortcutChanged += ControllerOnShortcutChanged; 138 | } 139 | 140 | public void PostAttachToAct(FFXIVTranslatePlugin plugin) 141 | { 142 | 143 | } 144 | 145 | public void NotifySettingsLoaded() 146 | { 147 | try 148 | { 149 | _controller.NotifyOverlayFontChanged(false, 150 | (Font)TypeDescriptor.GetConverter(typeof(Font)).ConvertFromString(OverlayFont)); 151 | } 152 | catch (Exception) 153 | { 154 | _controller.NotifyOverlayFontChanged(true, new Font(FontFamily.GenericSansSerif, 12, FontStyle.Regular)); 155 | } 156 | 157 | _controller.NotifyTranslateProviderChanged(false, TranslateProvider, TranslateApiKey, TranslateLangFrom, TranslateLangTo); 158 | _controller.NotifyProxyChanged(false, ProxyType, ProxyServer, ProxyPort, ProxyUser, ProxyPassword, ProxyDomain); 159 | _controller.NotifyShortcutChanged(false, PluginShortcut.HideOverlay, ShortkeyUtils.StringToKey(ShortcutHide)); 160 | 161 | _controller.NotifySettingsLoaded(); 162 | } 163 | 164 | private void ControllerOnLanguageChanged(bool fromView, string lang) 165 | { 166 | if (!fromView) 167 | { 168 | return; 169 | } 170 | 171 | Language = lang; 172 | } 173 | 174 | private void ControllerOnOverlayFontChanged(bool fromView, Font font) 175 | { 176 | if (!fromView) 177 | { 178 | return; 179 | } 180 | 181 | OverlayFont = TypeDescriptor.GetConverter(typeof(Font)).ConvertToString(font); 182 | } 183 | 184 | private void ControllerOnTranslateProviderChanged(bool fromView, string provider, string apiKey, string langFrom, string langTo) 185 | { 186 | if (!fromView) 187 | { 188 | return; 189 | } 190 | 191 | TranslateProvider = provider; 192 | TranslateApiKey = apiKey; 193 | TranslateLangFrom = langFrom; 194 | TranslateLangTo = langTo; 195 | } 196 | 197 | private void ControllerOnProxyChanged(bool fromView, string type, string server, int port, string user, 198 | string password, string domain) 199 | { 200 | if (!fromView) 201 | { 202 | return; 203 | } 204 | 205 | ProxyType = type; 206 | ProxyServer = server; 207 | ProxyPort = port; 208 | ProxyUser = user; 209 | ProxyPassword = password; 210 | ProxyDomain = domain; 211 | } 212 | 213 | private void ControllerOnNewVersionIgnored(bool fromView, string ignoredVersion) 214 | { 215 | VersionIgnored = ignoredVersion; 216 | } 217 | 218 | private void ControllerOnShortcutChanged(bool fromView, PluginShortcut shortcut, Keys key) 219 | { 220 | if (!fromView) 221 | { 222 | return; 223 | } 224 | 225 | var ks = ShortkeyUtils.KeyToString(key); 226 | switch (shortcut) 227 | { 228 | case PluginShortcut.HideOverlay: 229 | ShortcutHide = ks; 230 | break; 231 | } 232 | } 233 | 234 | #endregion 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/TranslateService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading; 5 | using ACT.FFXIVTranslate.translate.baidu; 6 | using ACT.FFXIVTranslate.translate.google_unofficial; 7 | using ACT.FFXIVTranslate.translate.microsoft; 8 | using ACT.FFXIVTranslate.translate.tencent; 9 | using ACT.FFXIVTranslate.translate.yandax; 10 | using ACT.FFXIVTranslate.translate.youdao; 11 | using ACT.FoxCommon.core; 12 | using ACT.FoxCommon.localization; 13 | 14 | namespace ACT.FFXIVTranslate.translate 15 | { 16 | 17 | class TranslateService : IPluginComponent 18 | { 19 | private FFXIVTranslatePlugin _plugin; 20 | private MainController _controller; 21 | 22 | private readonly object _mainLock = new object(); 23 | private readonly List _pendingLines = new List(); 24 | private readonly TranslateThread _workingThread = new TranslateThread(); 25 | 26 | private string TranslateProvider { get; set; } 27 | private string TranslateApiKey { get; set; } 28 | private string TranslateLangFrom { get; set; } 29 | private string TranslateLangTo { get; set; } 30 | private bool AddTimestamp { get; set; } 31 | private bool Timestamp24Hour { get; set; } 32 | 33 | public List AllProviders { get; } = 34 | new ITranslateProviderFactory[] 35 | { 36 | new YandaxTranslateProviderFactory(), 37 | new BaiduTranslateProviderFactory(), 38 | new MicrosoftTranslateProviderFactory(), 39 | new GoogleTranslateProviderFactory(), 40 | new YoudaoTranslateProviderFactory(), 41 | new TencentTranslateProviderFactory(), 42 | }.ToList(); 43 | 44 | public void AttachToAct(FFXIVTranslatePlugin plugin) 45 | { 46 | _plugin = plugin; 47 | _controller = plugin.Controller; 48 | _controller.TranslateProviderChanged += ControllerOnTranslateProviderChanged; 49 | _controller.AddTimestampChanged += ControllerOnAddTimestampChanged; 50 | _controller.TimestampFormatChanged += ControllerOnTimestampFormatChanged; 51 | } 52 | 53 | public void PostAttachToAct(FFXIVTranslatePlugin plugin) 54 | { 55 | } 56 | 57 | private void ControllerOnTranslateProviderChanged(bool fromView, string provider, string apiKey, 58 | string langFrom, 59 | string langTo) 60 | { 61 | if (!fromView) 62 | { 63 | return; 64 | } 65 | 66 | TranslateProvider = provider; 67 | TranslateApiKey = apiKey; 68 | TranslateLangFrom = langFrom; 69 | TranslateLangTo = langTo; 70 | 71 | var factory = AllProviders.First(it => it.ProviderId == provider); 72 | var lF = langFrom == LanguageDef.CodeAuto 73 | ? null 74 | : factory.SupportedSrcLanguages.First(it => it.LangCode == langFrom); 75 | var lT = factory.GetSupportedDestLanguages(lF).First(it => it.LangCode == langTo); 76 | var context = new TranslateContext 77 | { 78 | Service = this, 79 | Provider = factory.CreateProvider(apiKey, lF, lT) 80 | }; 81 | 82 | _controller.NotifyLegalInfoChanged(false, factory.LegalInfo); 83 | _workingThread.StartWorkingThread(context); 84 | } 85 | 86 | private void ControllerOnAddTimestampChanged(bool fromView, bool show) 87 | { 88 | AddTimestamp = show; 89 | } 90 | 91 | private void ControllerOnTimestampFormatChanged(bool fromView, bool is24Hour) 92 | { 93 | Timestamp24Hour = is24Hour; 94 | } 95 | 96 | public void SubmitNewLine(ChattingLine line) 97 | { 98 | lock (_mainLock) 99 | { 100 | _pendingLines.Add(line); 101 | Monitor.PulseAll(_mainLock); 102 | } 103 | } 104 | 105 | public void Stop() 106 | { 107 | _workingThread.StopWorkingThread(); 108 | } 109 | 110 | private class TranslateContext 111 | { 112 | public TranslateService Service; 113 | public ITranslateProvider Provider; 114 | } 115 | 116 | private class TranslateThread : BaseThreading 117 | { 118 | private const int BatchThreshold = 10; 119 | 120 | protected override void DoWork(TranslateContext context) 121 | { 122 | var service = context.Service; 123 | var provider = context.Provider; 124 | int batchWait = 0; 125 | var batchWorkingList = new List(); 126 | var availableLines = new List(); 127 | 128 | while (!WorkingThreadStopping) 129 | { 130 | if (batchWorkingList.Count > BatchThreshold || (batchWait > 1 && batchWorkingList.Count > 0)) 131 | { 132 | batchWait = 0; 133 | // Invoke translate service 134 | 135 | try 136 | { 137 | // Make sure we do have something to translate 138 | batchWorkingList.ForEach(it => 139 | { 140 | if (provider.PreprocessLine(it)) 141 | { 142 | availableLines.Add(it); 143 | } 144 | else 145 | { 146 | it.TranslatedContent = string.Empty; 147 | } 148 | }); 149 | if (availableLines.Count != 0) 150 | { 151 | provider.Translate(availableLines); 152 | availableLines.Clear(); 153 | } 154 | 155 | service._controller.NotifyBatchTranslateCompleted(false, new List(batchWorkingList)); 156 | 157 | // Render overlay text 158 | var overlayContentRendered = TextProcessor.BuildRTF( 159 | service._plugin, 160 | service.AddTimestamp, 161 | service.Timestamp24Hour, 162 | batchWorkingList.Where(it => it.EventCode <= EventCode.Clipboard)); // Don't display test channels 163 | 164 | service._controller.NotifyOverlayContentUpdated(false, overlayContentRendered); 165 | } 166 | catch (Exception ex) 167 | { 168 | service._controller.NotifyLogMessageAppend(false, ex + "\n"); 169 | } 170 | finally 171 | { 172 | batchWorkingList.Clear(); 173 | } 174 | } 175 | else 176 | { 177 | lock (service._mainLock) 178 | { 179 | if (service._pendingLines.Count > 0) 180 | { 181 | batchWait = 0; 182 | batchWorkingList.AddRange(service._pendingLines); 183 | service._pendingLines.Clear(); 184 | } 185 | else 186 | { 187 | if (batchWorkingList.Count > 0) 188 | { 189 | batchWait++; 190 | } 191 | Monitor.Wait(service._mainLock, 500); 192 | } 193 | } 194 | } 195 | } 196 | } 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/RTFLib/RTF/RTFBuilder.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | //using CurrentPatient.Properties; 5 | 6 | 7 | namespace RTF 8 | { 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Drawing; 12 | using System.Text; 13 | 14 | 15 | // ---------------------------------------------------------------------------------------- 16 | // _ ___ _..-._ Date: 12/11/08 23:34 17 | // \`.|\..----...-'` `-._.-'' _.-..' 18 | // / ' ` , __.-'' 19 | // )/` _/ \ `-_, / Solution: RTFLib 20 | // `-'" `"\_ ,_.-;_.-\_ ', Project : RTFLib 21 | // _.-'_./ {_.' ; / Author : Anton 22 | // {_.-``-' {_/ Assembly: 1.0.0.0 23 | // Copyright © 2005-2008, Rogue Trader/MWM 24 | // Project Item Name: RTFBuilder.cs - Code 25 | // Purpose: Rich Text Generator 26 | // ---------------------------------------------------------------------------------------- 27 | /// 28 | /// Rich Text Generator 29 | /// 30 | public partial class RTFBuilder : RTFBuilderbase 31 | { 32 | #region Fields 33 | 34 | private static readonly char[] slashable = new[] {'{', '}', '\\'}; 35 | 36 | private readonly StringBuilder _sb; 37 | 38 | #endregion 39 | 40 | #region Constructor 41 | 42 | public RTFBuilder() 43 | : base(RTFFont.Arial , 20F) 44 | { 45 | this._sb = new StringBuilder(); 46 | } 47 | 48 | public RTFBuilder(RTFFont defaultFont) : base(defaultFont, 20F) 49 | { 50 | this._sb = new StringBuilder(); 51 | } 52 | 53 | public RTFBuilder(float defaultFontSize) : base(RTFFont.Arial, defaultFontSize) 54 | { 55 | this._sb = new StringBuilder(); 56 | } 57 | 58 | public RTFBuilder(RTFFont defaultFont, float defaultFontSize) : base(defaultFont, defaultFontSize) 59 | { 60 | this._sb = new StringBuilder(); 61 | } 62 | 63 | #endregion 64 | 65 | #region Override Methods 66 | 67 | protected override void AppendInternal(string value) 68 | { 69 | if (!string.IsNullOrEmpty(value)) 70 | { 71 | using (new RTFFormatWrap(this)) 72 | { 73 | value = this.CheckChar(value); 74 | if (value.IndexOf(Environment.NewLine) >= 0) 75 | { 76 | string[] lines = value.Split(new[] {Environment.NewLine}, StringSplitOptions.None); 77 | foreach (string line in lines) 78 | { 79 | this._sb.Append(line); 80 | this._sb.Append("\\line "); 81 | } 82 | } 83 | else 84 | { 85 | this._sb.Append(value); 86 | } 87 | } 88 | } 89 | } 90 | 91 | protected override void AppendLevelInternal(int level) 92 | { 93 | this._sb.AppendFormat("\\level{0} ", level); 94 | } 95 | 96 | protected override void AppendLineInternal(string value) 97 | { 98 | using (new RTFParaWrap(this)) 99 | { 100 | Append(value); 101 | this._sb.AppendLine("\\line"); 102 | } 103 | } 104 | 105 | protected override void AppendLineInternal() 106 | { 107 | this._sb.AppendLine("\\line"); 108 | } 109 | 110 | protected override void AppendPageInternal() 111 | { 112 | using (new RTFParaWrap(this)) 113 | { 114 | this._sb.AppendLine("\\page"); 115 | } 116 | } 117 | 118 | protected override void AppendParaInternal() 119 | { 120 | using (new RTFParaWrap(this)) 121 | { 122 | this._sb.AppendLine("\\par "); 123 | } 124 | } 125 | 126 | protected override void AppendRTFInternal(string rtf) 127 | { 128 | if (!string.IsNullOrEmpty(rtf)) 129 | { 130 | this._sb.Append(rtf); 131 | } 132 | } 133 | 134 | protected override IEnumerable EnumerateCellsInternal(RTFRowDefinition rowDefinition, RTFCellDefinition[] cellDefinitions) 135 | { 136 | using (IRTFRow ie = this.CreateRow(rowDefinition, cellDefinitions)) 137 | { 138 | IEnumerator ie2 = ie.GetEnumerator(); 139 | while (ie2.MoveNext()) 140 | { 141 | using (IBuilderContent item = ie2.Current) 142 | { 143 | yield return item.Content; 144 | } 145 | } 146 | } 147 | } 148 | 149 | public override IDisposable FormatLock() 150 | { 151 | return new RTFBuilderUnWrapped(this); 152 | } 153 | 154 | protected override void InsertImageInternal(Image image) 155 | { 156 | try 157 | { 158 | RTFImage rti = new RTFImage(this); 159 | rti.InsertImage(image); 160 | } 161 | catch 162 | { 163 | this._sb.AppendLine("[Insert image error]"); 164 | } 165 | } 166 | 167 | protected override int LengthInternal() 168 | { 169 | throw new NotImplementedException(); 170 | } 171 | 172 | protected override void ResetInternal() 173 | { 174 | this._sb.AppendLine("\\pard"); 175 | } 176 | 177 | public override string ToString() 178 | { 179 | StringBuilder sb = new StringBuilder(); 180 | sb.Append("{\\rtf1\\ansi\\ansicpg1252\\deff0\\deflang3081"); 181 | // sb.Append("{\\fonttbl"); 182 | // 183 | // 184 | // for (int i = 0; i < _rawFonts.Count; i++) 185 | // { 186 | // 187 | // try 188 | // { 189 | // sb.Append(string.Format(_rawFonts[i], i)); 190 | // } 191 | // catch (Exception ex ) 192 | // { 193 | // 194 | // Console.WriteLine(ex.Message ); 195 | // } 196 | // 197 | // } 198 | // 199 | // sb.AppendLine("}"); 200 | 201 | sb.Append("{\\colortbl ;"); 202 | 203 | foreach (Color item in _colortbl) 204 | { 205 | sb.AppendFormat("\\red{0}\\green{1}\\blue{2};", item.R, item.G, item.B); 206 | } 207 | 208 | sb.AppendLine("}"); 209 | 210 | 211 | // sb.Append("\\viewkind4\\uc1\\pard\\plain\\f0"); 212 | 213 | // sb.AppendFormat("\\fs{0} ", DefaultFontSize); 214 | sb.AppendLine(); 215 | 216 | sb.Append(this._sb.ToString()); 217 | sb.Append("}"); 218 | 219 | 220 | return sb.ToString(); 221 | } 222 | 223 | #endregion 224 | 225 | #region Public Methods 226 | 227 | public IRTFRow CreateRow(RTFRowDefinition rowDefinition, RTFCellDefinition[] cellDefinitions) 228 | { 229 | return new RTFRow(this, rowDefinition, cellDefinitions); 230 | } 231 | 232 | #endregion 233 | 234 | #region Methods 235 | 236 | /// 237 | /// Checks the char. 238 | /// 239 | /// The value. 240 | /// 241 | private string CheckChar(string value) 242 | { 243 | if (!string.IsNullOrEmpty(value)) 244 | { 245 | if (value.IndexOfAny(slashable) >= 0) 246 | { 247 | value = value.Replace("{", "\\{").Replace("}", "\\}").Replace("\\", "\\\\"); 248 | } 249 | bool replaceuni = false; 250 | for (int i = 0; i < value.Length; i++) 251 | { 252 | if (value[i] > 255) 253 | { 254 | replaceuni = true; 255 | break; 256 | } 257 | } 258 | if (replaceuni) 259 | { 260 | StringBuilder sb = new StringBuilder(); 261 | 262 | for (int i = 0; i < value.Length; i++) 263 | { 264 | if (value[i] <= 255) 265 | { 266 | sb.Append(value[i]); 267 | } 268 | else 269 | { 270 | sb.Append("\\u"); 271 | sb.Append((int) value[i]); 272 | sb.Append("?"); 273 | } 274 | } 275 | value = sb.ToString(); 276 | } 277 | } 278 | 279 | 280 | return value; 281 | } 282 | 283 | #endregion 284 | } 285 | } 286 | 287 | -------------------------------------------------------------------------------- /ACT.FFXIVTranslate/ACT.FFXIVTranslate/translate/tencent/TencentTranslateProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Security.Cryptography; 7 | using System.Text; 8 | using ACT.FoxCommon.localization; 9 | using Newtonsoft.Json; 10 | using Newtonsoft.Json.Linq; 11 | 12 | namespace ACT.FFXIVTranslate.translate.tencent 13 | { 14 | internal class TencentTranslateProvider : DefaultTranslateProvider 15 | { 16 | private const string SERVICE = "tmt"; 17 | private const string HOST = SERVICE + ".tencentcloudapi.com"; 18 | private const string ALGORITHM = "TC3-HMAC-SHA256"; 19 | 20 | private readonly string _secretId; 21 | private readonly string _secretKey; 22 | private readonly string _langFrom; 23 | private readonly string _langTo; 24 | 25 | public TencentTranslateProvider(string apiKey, LanguageDef src, LanguageDef dst) 26 | { 27 | var kp = apiKey.Split(':'); 28 | if (kp.Length < 2) 29 | { 30 | _secretId = ""; 31 | _secretKey = ""; 32 | } 33 | else 34 | { 35 | _secretId = kp[0]; 36 | _secretKey = kp[1]; 37 | } 38 | 39 | _langFrom = src?.LangCode ?? "auto"; 40 | _langTo = dst.LangCode; 41 | } 42 | 43 | public override void Translate(List chattingLines) 44 | { 45 | try 46 | { 47 | // Build request json 48 | var query = new Dictionary(); 49 | query["Source"] = _langFrom; 50 | query["Target"] = _langTo; 51 | query["ProjectId"] = 0; 52 | query["SourceTextList"] = chattingLines.Select(it => it.CleanedContent).ToList(); 53 | 54 | var queryBody = JsonConvert.SerializeObject(query, Formatting.None); 55 | 56 | // Calculate signature, based on https://cloud.tencent.com/document/api/551/30636 57 | // and https://github.com/TencentCloud/tencentcloud-sdk-dotnet/blob/376182fa16beefb19d09cd6bceae536689b62978/TencentCloud/Common/AbstractClient.cs#L234 58 | 59 | var timestamp = ToTimestamp() / 1000; 60 | var date = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(timestamp) 61 | .ToString("yyyy-MM-dd"); 62 | var credentialScope = $"{date}/{SERVICE}/tc3_request"; 63 | 64 | // 1. Build CanonicalRequest 65 | var canonicalRequest = 66 | $"POST\n" + 67 | $"/\n" + 68 | $"\n" + 69 | $"content-type:application/json; charset=utf-8\n" + 70 | $"host:{HOST}\n" + 71 | $"\n" + 72 | $"content-type;host\n" + 73 | $"{SHA256Hex(queryBody)}"; 74 | 75 | // 2. Build StringToSign 76 | var stringToSign = 77 | $"{ALGORITHM}\n" + 78 | $"{timestamp}\n" + 79 | $"{credentialScope}\n" + 80 | $"{SHA256Hex(canonicalRequest)}"; 81 | 82 | // 3. Calculate Signature 83 | var tc3SecretKey = Encoding.UTF8.GetBytes("TC3" + _secretKey); 84 | var secretDate = HmacSHA256(tc3SecretKey, Encoding.UTF8.GetBytes(date)); 85 | var secretService = HmacSHA256(secretDate, Encoding.UTF8.GetBytes(SERVICE)); 86 | var secretSigning = HmacSHA256(secretService, Encoding.UTF8.GetBytes("tc3_request")); 87 | var signatureBytes = HmacSHA256(secretSigning, Encoding.UTF8.GetBytes(stringToSign)); 88 | var signature = BitConverter.ToString(signatureBytes).Replace("-", "").ToLower(); 89 | 90 | // 4. Build Authorization 91 | var authorization = 92 | $"{ALGORITHM} Credential={_secretId}/{credentialScope}, SignedHeaders=content-type;host, Signature={signature}"; 93 | 94 | // Build headers 95 | var headers = new Dictionary(); 96 | headers["Authorization"] = authorization; 97 | headers["Host"] = HOST; 98 | headers["Content-Type"] = "application/json; charset=utf-8"; 99 | headers["X-TC-Action"] = "TextTranslateBatch"; 100 | headers["X-TC-Timestamp"] = timestamp.ToString(); 101 | headers["X-TC-Version"] = "2018-03-21"; 102 | headers["X-TC-Region"] = "ap-shanghai"; 103 | 104 | // Make http request 105 | string textResponse; 106 | using (var client = ProxyFactory.Instance.NewClient()) 107 | using (var request = new HttpRequestMessage(HttpMethod.Post, "/")) 108 | { 109 | client.BaseAddress = new Uri($"https://{HOST}"); 110 | 111 | request.Content = new StringContent(queryBody, Encoding.UTF8, "application/json"); 112 | foreach (var header in headers) 113 | { 114 | request.Headers.TryAddWithoutValidation(header.Key, header.Value); 115 | } 116 | 117 | var response = client.SendAsync(request).Result; 118 | response.EnsureSuccessStatusCode(); 119 | textResponse = response.Content.ReadAsStringAsync().Result; 120 | Debug.WriteLine(textResponse); 121 | } 122 | 123 | // read json 124 | var result = (JObject) JObject.Parse(textResponse)["Response"]; 125 | var error = (JObject) result["Error"]; 126 | if (error != null) 127 | { 128 | // handle error 129 | var errorCode = (string) error["Code"]; 130 | var errorMessage = (string) error["Message"]; 131 | 132 | if (errorCode.StartsWith("AuthFailure.")) 133 | { 134 | throw new TranslateException(TranslateException.ExceptionReason.InvalidApiKey, 135 | errorCode + ": " + errorMessage, 136 | null); 137 | } 138 | 139 | switch (errorCode) 140 | { 141 | case "LimitExceeded": 142 | case "RequestLimitExceeded": 143 | throw new TranslateException(TranslateException.ExceptionReason.ApiLimitExceed, 144 | errorCode + ": " + errorMessage, null); 145 | 146 | case "UnsupportedOperation.UnSupportedTargetLanguage": 147 | case "UnsupportedOperation.UnsupportedLanguage": 148 | case "UnsupportedOperation.UnsupportedSourceLanguage": 149 | throw new TranslateException(TranslateException.ExceptionReason.DirectionNotSupported, 150 | errorCode + ": " + errorMessage, null); 151 | 152 | case "UnsupportedOperation.TextTooLong": 153 | throw new TranslateException(TranslateException.ExceptionReason.InternalError, 154 | errorCode + ": " + errorMessage, null); 155 | 156 | default: 157 | throw new TranslateException(TranslateException.ExceptionReason.GeneralServiceError, 158 | textResponse, null); 159 | } 160 | } 161 | 162 | var translation = (JArray) result["TargetTextList"]; 163 | if (translation.Count >= chattingLines.Count) 164 | { 165 | for (var i = 0; i < chattingLines.Count; i++) 166 | { 167 | chattingLines[i].TranslatedContent = (string) translation[i]; 168 | } 169 | } 170 | else 171 | { 172 | // TODO: Oops! 173 | } 174 | } 175 | catch (TranslateException) 176 | { 177 | throw; 178 | } 179 | catch (Exception ex) 180 | { 181 | throw new TranslateException(TranslateException.ExceptionReason.UnknownError, null, ex); 182 | } 183 | } 184 | 185 | #region Helper functions 186 | 187 | // https://github.com/TencentCloud/tencentcloud-sdk-dotnet/blob/376182fa16beefb19d09cd6bceae536689b62978/TencentCloud/Common/Sign.cs 188 | 189 | private static string SHA256Hex(string s) 190 | { 191 | using (SHA256 algo = SHA256.Create()) 192 | { 193 | byte[] hashbytes = algo.ComputeHash(Encoding.UTF8.GetBytes(s)); 194 | StringBuilder builder = new StringBuilder(); 195 | for (int i = 0; i < hashbytes.Length; ++i) 196 | { 197 | builder.Append(hashbytes[i].ToString("x2")); 198 | } 199 | return builder.ToString(); 200 | } 201 | } 202 | 203 | private static byte[] HmacSHA256(byte[] key, byte[] msg) 204 | { 205 | using (HMACSHA256 mac = new HMACSHA256(key)) 206 | { 207 | return mac.ComputeHash(msg); 208 | } 209 | } 210 | 211 | // https://github.com/TencentCloud/tencentcloud-sdk-dotnet/blob/376182fa16beefb19d09cd6bceae536689b62978/TencentCloud/Common/AbstractClient.cs#L472 212 | private static long ToTimestamp() 213 | { 214 | DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1, 0, 0, 0, 0)); 215 | DateTime nowTime = DateTime.Now; 216 | long unixTime = (long)Math.Round((nowTime - startTime).TotalMilliseconds, MidpointRounding.AwayFromZero); 217 | return unixTime; 218 | } 219 | 220 | #endregion 221 | 222 | } 223 | } 224 | --------------------------------------------------------------------------------