ترجمة الملاحظات بشكل تلقائي مع EasyNMT
المقدمة
EasyNMT هو خدمة قابلة للتركيب محليا توفر واجهة بسيطة لمجموعة من خدمات الترجمة الآلية.
يمكنكم العثور على جميع الملفات لهذا الدروس في مخزن GitHub لهذا المشروع.
ناتج هذا ينتج بطاقة من الملفات الجديدة للانقراض في اللغات المستهدفة. هذه طريقة بسيطة جدا للحصول على تعليق مدوني تمت ترجمته إلى لغات متعددة.
المتطلبات الأساسية
install of EasyNMT is required to follow this tutorial. I usually run it as a Docker service. You can find the installation instructions هنا الذي يوضح كيفية تشغيله كخدمة Docker.
docker run -d -p 24080:80 --env MAX_WORKERS_BACKEND=6 --env MAX_WORKERS_FRONTEND=6 easynmt/api:2.0-cpu
أو إذا كان لديك GPU NVIDIA متوفراً
docker run -d -p 24080:80 --env MAX_WORKERS_BACKEND=6 --env MAX_WORKERS_FRONTEND=6 easynmt/api:2.0.2-cuda11.3
العوامل البيئية MAX_WORKERSM SK1BACKEND وMAX_WOSKERS _FRONTEND تحدد عدد العمال الذين سيستخدم EasyNMT
لاحظوا : EasyNMT هو ' ليس خدمة SMOOTHEST لتشغيله, لكنه ' أفضل ما وجدته لـ ' لتلك الغرض . إنه نوع من الضجيج حول سلسلة الإدخال التي تم إدخالها مSK6 تم تحويلها إلى , لذا قد تحتاجون إلى القيام ببعض عمليات قبل إعادة معالجة النص الإدخال الخاص بك قبل أن تنقله إلى EasyN MT
أوه ولقد ترجم أيضاً ' "إتفاق" ' إلى بعض الهراء حول تقديم الاقتراح إلى الاتحاد الاوروبي"... تزويرها"' مجموعة التدريب.
المقاربة السخيفة لموازنة الشحن
Easy NMT هي حيوانات جائعة عندما يتعلق الأمر بالموارد, لذلك في خدمتي MarkdownTranslatorService لدي اختيار IP عشوائي بسيط للغاية يدور فقط خلال قائمة IP لمجموعة من الآلات التي أستخدمها لتشغيل EasyNMT.
في البداية هذا يحصل على model_name الطريقة على خدمة EasyNMT, هذه طريقة سريعة, بسيطة للتأكد من أن الخدمة مرتفعةM SK2 إذا كانت كذلكMSC3 فهي تضيف IP إلى قائمة من IPs العاملة . إذا لم يكن ذلكMST5M stk6 فهو لا يضيفه إلى القائمةMstk7 لا يضافه إلى قائمة
private string[] IPs = translateServiceConfig.IPs;
public async ValueTask<bool> IsServiceUp(CancellationToken cancellationToken)
{
var workingIPs = new List<string>();
try
{
foreach (var ip in IPs)
{
logger.LogInformation("Checking service status at {IP}", ip);
var response = await client.GetAsync($"{ip}/model_name", cancellationToken);
if (response.IsSuccessStatusCode)
{
workingIPs.Add(ip);
}
}
IPs = workingIPs.ToArray();
if (!IPs.Any()) return false;
return true;
}
catch (Exception e)
{
logger.LogError(e, "Error checking service status");
return false;
}
}
ثم في داخل Post طريقة MarkdownTranslatorService ندور عبر IPs العملية لنجد واحداً لاحقاً
if(!IPs.Any())
{
logger.LogError("No IPs available for translation");
throw new Exception("No IPs available for translation");
}
var ip = IPs[currentIPIndex];
logger.LogInformation("Sending request to {IP}", ip);
// Update the index for the next request
currentIPIndex = (currentIPIndex + 1) % IPs.Length;
هذه طريقة بسيطة جدا لشحن توازن الطلبات عبر عدد من الآلات
المزاح currentIPIndex = (currentIPIndex + 1) % IPs.Length; فقط يدور من خلال قائمة ال IP بدءا من 0 ويذهب إلى طول القائمة.
ترجمة دوتنه العلامات
هذا هو الرمز الذي أملكه في "MarkdownTranslatorService" . "cs" . إنه'is a simple service that takes a markdown string and a target language and returns the translated Markdown string"
public async Task<string> TranslateMarkdown(string markdown, string targetLang, CancellationToken cancellationToken)
{
var document = Markdig.Markdown.Parse(markdown);
var textStrings = ExtractTextStrings(document);
var batchSize = 10;
var stringLength = textStrings.Count;
List<string> translatedStrings = new();
for (int i = 0; i < stringLength; i += batchSize)
{
var batch = textStrings.Skip(i).Take(batchSize).ToArray();
translatedStrings.AddRange(await Post(batch, targetLang, cancellationToken));
}
ReinsertTranslatedStrings(document, translatedStrings.ToArray());
return document.ToMarkdownString();
}
كما ترون لديه عدد من الخطوات:
var document = Markdig.Markdown.Parse(markdown);- هذا يفكّر سلسلة العلامات المنخفضة في مستندvar textStrings = ExtractTextStrings(document);- هذا يستخرج سلسلة النص من الوثيقة. هذا يستخدم الطريقة
private bool IsWord(string text)
{
var imageExtensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg" };
if (imageExtensions.Any(text.Contains)) return false;
return text.Any(char.IsLetter);
}
هذا يتأكد من أن 'كلمة' هي حقاً عمل ; أسماء الصور يمكن أن تسبب فوضى في وظائف تقسيم الجملة في EasyNMT
var batchSize = 10;- هذا يحدد حجم القطعة لخدمة الترجمة. EasyNMT لديه حد على عدد الكلمات التي يمكنه ترجمتها في وقت واحد (حول | | 500, لذلك |csharp await Post(batch, targetLang, cancellationToken)هذا يتصل بالأسلوب الذي يرسل batch إلى خدمة EasyNMT.
private async Task<string[]> Post(string[] elements, string targetLang, CancellationToken cancellationToken)
{
try
{
var postObject = new PostRecord(targetLang, elements);
var response = await client.PostAsJsonAsync("/translate", postObject, cancellationToken);
var phrase = response.ReasonPhrase;
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<PostResponse>(cancellationToken: cancellationToken);
return result.translated;
}
catch (Exception e)
{
logger.LogError(e, "Error translating markdown: {Message} for strings {Strings}", e.Message, string.Concat( elements, Environment.NewLine));
throw;
}
}
ReinsertTranslatedStrings(document, translatedStrings.ToArray());- هذا يعيد تضمين الأوتار المترجمة مرة أخرى إلى الوثيقة. باستخدام MarkDigM SK2 القدرة على المشي في الوثيق وبديل الأوتار النصية .
private void ReinsertTranslatedStrings(MarkdownDocument document, string[] translatedStrings)
{
int index = 0;
foreach (var node in document.Descendants())
{
if (node is LiteralInline literalInline && index < translatedStrings.Length)
{
var content = literalInline.Content.ToString();
if (!IsWord(content)) continue;
literalInline.Content = new Markdig.Helpers.StringSlice(translatedStrings[index]);
index++;
}
}
}
الخدمات المضيفة
لتشغيل كل هذا أستخدم IHostedLifetimeService الذي يبدأ في البرنامج.cs دوتنه. هذه الخدمة تقرأ في دوتن تراجعيM SK2 تترجمه إلى عدد من اللغات وتكتب الឯកសារ المترجمة إلى القرص
public async Task StartedAsync(CancellationToken cancellationToken)
{
if(!await blogService.IsServiceUp(cancellationToken))
{
logger.LogError("Translation service is not available");
return;
}
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = blogService.IPCount, CancellationToken = cancellationToken};
var files = Directory.GetFiles(markdownConfig.MarkdownPath, "*.md");
var outDir = markdownConfig.MarkdownTranslatedPath;
var languages = translateServiceConfig.Languages;
foreach(var language in languages)
{
await Parallel.ForEachAsync(files, parallelOptions, async (file,ct) =>
{
var fileChanged = await file.IsFileChanged(outDir);
var outName = Path.GetFileNameWithoutExtension(file);
var outFileName = $"{outDir}/{outName}.{language}.md";
if (File.Exists(outFileName) && !fileChanged)
{
return;
}
var text = await File.ReadAllTextAsync(file, cancellationToken);
try
{
logger.LogInformation("Translating {File} to {Language}", file, language);
var translatedMarkdown = await blogService.TranslateMarkdown(text, language, ct);
await File.WriteAllTextAsync(outFileName, translatedMarkdown, cancellationToken);
}
catch (Exception e)
{
logger.LogError(e, "Error translating {File} to {Language}", file, language);
}
});
}
كما ترون، يقوم أيضاً بفحص هش الملف لمعرفة ما إذا كان قد تغير قبل ترجمته.
هذا يتم عن طريق حساب هش سريع للملف original markdown ثم اختبار لمعرفة ما إذا كان هذا الملف قد تغير قبل محاولة ترجمته.
private static async Task<string> ComputeHash(string filePath)
{
await using var stream = File.OpenRead(filePath);
stream.Position = 0;
var bytes = new byte[stream.Length];
await stream.ReadAsync(bytes);
stream.Position = 0;
var hash = XxHash64.Hash(bytes);
var hashString = Convert.ToBase64String(hash);
hashString = InvalidCharsRegex.Replace(hashString, "_");
return hashString;
}
الإعداد في البرنامج.cs بسيط جداً:
builder.Services.AddHostedService<BackgroundTranslateService>();
services.AddHttpClient<MarkdownTranslatorService>(options =>
{
options.Timeout = TimeSpan.FromMinutes(15);
});
لقد قمت بإعداد خدمة مضيفة (BackgroundTranslateService) وម៉ាស៊ីន HttpClient لخدمة MarkdownTranslatorService . "خدمات مضيفة" هي خدمة طويلة - تعمل في الخلفية . هي مكان جيد لوضع الخدمات التي تحتاج للتشغيل المستمر في الخلفي أو تأخذ وقتاً طويلاً لتكمله "M SK3" إن واجهة "IHostedLifetimeService" الجديدة أكثر مرونة من واجهات "IhostedService" القديمة وتسمح لنا بتشغيل المهام بشكل كامل في الخلفيات ببساطة أكثر من تلك القديمة
هنا يمكنكم أن تروا I'm يضبط وقت الاستراحة لـ HttpClient إلى 15 دقائقM SK2 هذا لأن EasyNMT يمكن أن يكون بطيئاً قليلاً في الإجابة ( خصوصاً في المرة الأولى باستخدام نموذج لغة
في الختام
هذه طريقة بسيطة جداً لترجمة ឯកសារ العلامات إلى لغات متعددة. MarkdownBlogService لسحب الأسماء المترجمة لكل مدونة post.