main
zggsong 2 weeks ago
parent 44ee45df5b
commit 970e408aa9

@ -0,0 +1,80 @@
using System.Text.Json.Serialization;
namespace STranslateDLL;
public class Response
{
public int Code { get; set; }
public string Data { get; set; } = "";
}
public class DeepLRequest
{
[JsonPropertyName("jsonrpc")] public string Jsonrpc { get; set; } = "";
[JsonPropertyName("method")] public string Method { get; set; } = "";
[JsonPropertyName("params")] public ReqParams? Params { get; set; }
[JsonPropertyName("id")] public long Id { get; set; }
}
public class ReqParams
{
[JsonPropertyName("commonJobParams")] public ReqParamsCommonJobParams? CommonJobParams { get; set; }
[JsonPropertyName("lang")] public ReqParamsLang? Lang { get; set; }
[JsonPropertyName("texts")] public string[]? Texts { get; set; }
[JsonPropertyName("textType")] public string? TextType { get; set; }
[JsonPropertyName("jobs")] public Job[]? Jobs { get; set; }
[JsonPropertyName("priority")] public int Priority { get; set; }
[JsonPropertyName("timestamp")] public long Timestamp { get; set; }
}
public class ReqParamsLang
{
[JsonPropertyName("source_lang_user_selected")]
public string? SourceLangUserSelected { get; set; }
[JsonPropertyName("source_lang_computed")]
public string? SourceLangComputed { get; set; }
[JsonPropertyName("target_lang")] public string TargetLang { get; set; } = "";
}
public class ReqParamsCommonJobParams
{
[JsonPropertyName("mode")] public string Mode { get; set; } = "";
[JsonPropertyName("regionalVariant")] public string? RegionalVariant { get; set; }
}
public class Job
{
[JsonPropertyName("kind")] public string Kind { get; set; } = "";
[JsonPropertyName("preferred_num_beams")]
public int PreferredNumBeams { get; set; }
[JsonPropertyName("raw_en_context_before")]
public string[]? RawEnContextBefore { get; set; }
[JsonPropertyName("raw_en_context_after")]
public string[]? RawEnContextAfter { get; set; }
[JsonPropertyName("sentences")] public Sentence[]? Sentences { get; set; }
}
public class Sentence
{
[JsonPropertyName("id")] public int Id { get; set; }
[JsonPropertyName("prefix")] public string Prefix { get; set; } = "";
[JsonPropertyName("text")] public string Text { get; set; } = "";
}

@ -1,41 +1,12 @@
using System.IO.Compression;
using System.Net;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace STranslateDLL;
public static class LocalMode
public static class Program
{
private static long _nextId;
private static bool _hasInit;
private static readonly Dictionary<bool, string> TextTypeDic = new()
{
{ true, "richtext" },
{ false, "plaintext" }
};
private static JsonSerializerOptions GetOptions =>
new()
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
};
private static void Initial()
{
var rand = new Random();
var number = rand.NextInt64(99999) + 8300000;
_nextId = number * 1000;
_hasInit = true;
}
public static async Task<string> ExecuteAsync(
string content,
string sourceLang = "auto",
@ -43,35 +14,37 @@ public static class LocalMode
CancellationToken? token = null
)
{
if (!_hasInit) Initial();
Utilities.Initial();
var getToken = token ?? CancellationToken.None;
var splitResult = await SplitTextAsync(content, getToken);
//TODO: Error handling
var splitObj = JsonSerializer.Deserialize<JsonObject>(splitResult);
if (sourceLang.Equals("auto", StringComparison.CurrentCultureIgnoreCase) || sourceLang.Equals(""))
var splitData = JsonNode.Parse(splitResult);
var splitError = splitData?["error"]?["message"]?.ToString();
if (splitError != null)
{
sourceLang = splitObj?["result"]?["lang"]?["detected"]?.ToString()?.ToUpper() ?? "auto";
return Utilities.Serialize(new Response
{
Code = 500,
Data = splitError
});
}
if (sourceLang.Equals("auto", StringComparison.CurrentCultureIgnoreCase) || sourceLang.Equals(""))
sourceLang = splitData?["result"]?["lang"]?["detected"]?.ToString()?.ToUpper() ?? "auto";
var jobs = new List<Job>();
var chunks = splitObj?["result"]?["texts"]?[0]?["chunks"];
var chunks = splitData?["result"]?["texts"]?[0]?["chunks"];
if (chunks is JsonArray chunkArray)
{
for (var i = 0; i < chunkArray.Count; i++)
{
var sentence = chunkArray[i]?["sentences"]?[0];
var contextBefore = Array.Empty<string>();
var contextAfter = Array.Empty<string>();
if (i > 0)
{
contextBefore = [chunkArray[i - 1]?["sentences"]?[0]?["text"]?.ToString() ?? ""];
}
if (i > 0) contextBefore = [chunkArray[i - 1]?["sentences"]?[0]?["text"]?.ToString() ?? ""];
if (i < chunkArray.Count - 1)
{
contextAfter = [chunkArray[i + 1]?["sentences"]?[0]?["text"]?.ToString() ?? ""];
}
var job = new Job
{
@ -85,13 +58,12 @@ public static class LocalMode
{
Prefix = sentence?["prefix"]?.ToString() ?? "",
Text = sentence?["text"]?.ToString() ?? "",
Id = i + 1,
Id = i + 1
}
]
};
jobs.Add(job);
}
}
var hasRegionalVariant = false;
var targetLangCode = targetLang;
@ -102,7 +74,7 @@ public static class LocalMode
hasRegionalVariant = true;
}
var id = CreateId();
var id = Utilities.CreateId();
var reqData = new DeepLRequest
{
Jsonrpc = "2.0",
@ -123,12 +95,12 @@ public static class LocalMode
},
Jobs = jobs.ToArray(),
Priority = 1,
Timestamp = GenerateTimestamp(content)
Timestamp = Utilities.GenerateTimestamp(content)
}
};
var json = JsonSerializer.Serialize(reqData, GetOptions);
json = AdjustJsonContent(json, id);
var json = Utilities.Serialize(reqData);
json = Utilities.AdjustJsonContent(json, id);
using var client = new HttpClient();
@ -174,7 +146,7 @@ public static class LocalMode
{
responseBody = await resp.Content.ReadAsStringAsync(getToken);
}
var jNode = JsonNode.Parse(responseBody);
var data = jNode?["result"]?["translations"]?[0]?["beams"]?[0]?["sentences"]?[0]?["text"]?.ToString();
// data = UnicodeToString(data);
@ -189,17 +161,17 @@ public static class LocalMode
Data = data ?? error
};
return JsonSerializer.Serialize(response, GetOptions);
return Utilities.Serialize(response);
}
private static async Task<string> SplitTextAsync(string text, CancellationToken? token)
{
var id = CreateId();
var id = Utilities.CreateId();
var getToken = token ?? CancellationToken.None;
var requestData = new DeepLRequest
{
Jsonrpc = "2.0",
Method = "LMT_split_text",
Method = "LMT_split_text2",
Id = id,
Params = new ReqParams
{
@ -212,12 +184,12 @@ public static class LocalMode
SourceLangUserSelected = "auto"
},
Texts = [text],
TextType = TextTypeDic[ /*tagHandling*/false || IsRichText(text)]
TextType = Utilities.TextTypeDic[ /*tagHandling*/false || Utilities.IsRichText(text)]
}
};
var json = JsonSerializer.Serialize(requestData, GetOptions);
json = AdjustJsonContent(json, id);
var json = Utilities.Serialize(requestData);
json = Utilities.AdjustJsonContent(json, id);
using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post,
"https://www2.deepl.com/jsonrpc?client=chrome-extension,1.28.0&method=LMT_split_text")
@ -262,139 +234,4 @@ public static class LocalMode
return responseBody;
}
private static string? UnicodeToString(string? srcText)
{
if (srcText == null) return default;
var dst = "";
var src = srcText;
var len = srcText.Length / 6;
for (var i = 0; i <= len - 1; i++)
{
var str = "";
str = src[..6][2..];
src = src[6..];
var bytes = new byte[2];
bytes[1] = byte.Parse(int.Parse(str[..2], System.Globalization.NumberStyles.HexNumber).ToString());
bytes[0] = byte.Parse(int.Parse(str.Substring(2, 2), System.Globalization.NumberStyles.HexNumber).ToString());
dst += Encoding.Unicode.GetString(bytes);
}
return dst;
}
private static bool IsRichText(string text)
{
return text.Contains('<') && text.Contains('>');
}
private static long GenerateTimestamp(string texts)
{
long iCount = texts.Split('i').Length - 1;
var ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
return iCount != 0 ? ts - ts % (iCount + 1) + iCount + 1 : ts;
}
private static long CreateId()
{
return Interlocked.Increment(ref _nextId);
}
private static string AdjustJsonContent(string json, long id)
{
string method;
if ((id + 3) % 13 == 0 || (id + 5) % 29 == 0)
method = "\"method\" : \"";
else
method = "\"method\": \"";
return json.Replace("\"method\":\"", method);
}
}
public class Response
{
public int Code { get; set; }
public string Data { get; set; } = "";
}
#region Request
public class DeepLRequest
{
[JsonPropertyName("jsonrpc")] public string Jsonrpc { get; set; } = "";
[JsonPropertyName("method")] public string Method { get; set; } = "";
[JsonPropertyName("params")] public ReqParams? Params { get; set; }
[JsonPropertyName("id")] public long Id { get; set; }
}
public class ReqParams
{
[JsonPropertyName("commonJobParams")] public ReqParamsCommonJobParams? CommonJobParams { get; set; }
[JsonPropertyName("lang")] public ReqParamsLang? Lang { get; set; }
[JsonPropertyName("texts")] public string[]? Texts { get; set; }
[JsonPropertyName("textType")] public string? TextType { get; set; }
[JsonPropertyName("jobs")] public Job[]? Jobs { get; set; }
[JsonPropertyName("priority")] public int Priority { get; set; }
[JsonPropertyName("timestamp")] public long Timestamp { get; set; }
}
public class ReqParamsLang
{
[JsonPropertyName("source_lang_user_selected")]
public string? SourceLangUserSelected { get; set; }
[JsonPropertyName("source_lang_computed")]
public string? SourceLangComputed { get; set; }
[JsonPropertyName("target_lang")] public string TargetLang { get; set; } = "";
}
public class ReqParamsCommonJobParams
{
[JsonPropertyName("mode")] public string Mode { get; set; } = "";
[JsonPropertyName("regionalVariant")] public string? RegionalVariant { get; set; }
}
public class Job
{
[JsonPropertyName("kind")] public string Kind { get; set; } = "";
[JsonPropertyName("preferred_num_beams")]
public int PreferredNumBeams { get; set; }
[JsonPropertyName("raw_en_context_before")]
public string[]? RawEnContextBefore { get; set; }
[JsonPropertyName("raw_en_context_after")]
public string[]? RawEnContextAfter { get; set; }
[JsonPropertyName("sentences")] public Sentence[]? Sentences { get; set; }
}
public class Sentence
{
[JsonPropertyName("id")] public int Id { get; set; }
[JsonPropertyName("prefix")] public string Prefix { get; set; } = "";
[JsonPropertyName("text")] public string Text { get; set; } = "";
}
#endregion
public class RespError
{
[JsonPropertyName("code")] public int Code { get; set; }
[JsonPropertyName("message")] public string Message { get; set; } = "";
}

@ -3,16 +3,16 @@
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net8.0\publish\win-x64\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<PublishSingleFile>false</PublishSingleFile>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net8.0\publish\win-x64\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<PublishSingleFile>false</PublishSingleFile>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>
</Project>

@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>localmode</AssemblyName>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>localmode</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>none</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>none</DebugType>
</PropertyGroup>
</Project>

@ -0,0 +1,67 @@
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace STranslateDLL;
public static class Utilities
{
private static long _nextId;
private static bool _hasInit;
public static readonly Dictionary<bool, string> TextTypeDic = new()
{
{ true, "richtext" },
{ false, "plaintext" }
};
private static JsonSerializerOptions GetOptions =>
new()
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
};
public static string Serialize(object data)
{
return JsonSerializer.Serialize(data, GetOptions);
}
public static void Initial()
{
if (_hasInit) return;
var rand = new Random();
var number = rand.NextInt64(99999) + 8300000;
_nextId = number * 1000;
_hasInit = true;
}
public static bool IsRichText(string text)
{
return text.Contains('<') && text.Contains('>');
}
public static long GenerateTimestamp(string texts)
{
long iCount = texts.Split('i').Length - 1;
var ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
return iCount != 0 ? ts - ts % (iCount + 1) + iCount + 1 : ts;
}
public static long CreateId()
{
return Interlocked.Increment(ref _nextId);
}
public static string AdjustJsonContent(string json, long id)
{
string method;
if ((id + 3) % 13 == 0 || (id + 5) % 29 == 0)
method = "\"method\" : \"";
else
method = "\"method\": \"";
return json.Replace("\"method\":\"", method);
}
}

@ -5,7 +5,7 @@ using Xunit.Abstractions;
namespace STranslateDLLTests;
public class LocalModeTests(ITestOutputHelper testOutputHelper)
public class ProgramTests(ITestOutputHelper testOutputHelper)
{
[Fact()]
public async Task ExecuteAsyncTestAsync()
@ -13,7 +13,7 @@ public class LocalModeTests(ITestOutputHelper testOutputHelper)
try
{
var cts = new CancellationTokenSource();
var ret = await LocalMode.ExecuteAsync("Hello World", "EN", "ZH", cts.Token);
var ret = await Program.ExecuteAsync("Hello World", "EN", "ZH", cts.Token);
testOutputHelper.WriteLine(ret);
}
catch (Exception ex)
Loading…
Cancel
Save