This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
الترجمة الخلفية Pt. 3
Sunday, 25 August 2024
المقدمة
في المقالات السابقة, لقد ناقشنا أهمية الترجمة في سياق تطبيقات الإنترنت. لقد بحثنا أيضاً عن استخدام مكتبة EasyNMT لإجراء الترجمات في برنامج ASPM SK2NET Core application . في هذا المقال سوف أشرح كيف قمت بإضافة خدمة خلفية للتطبيق لتسمح لك بتقديم طلب الترجمهMSC5 التي تتم معالجتها في الخلفيةMST6
مرة أخرى, يمكنكم رؤية كل شفرة المصدر لهذا على GitHub صفحة.
المقالات السابقة
هنا أضفنا أداة صغيرة تقوم بإرسال الوظائف الخلفية إلى الخدمة التي شرحناها في جزء. 2. هذه الأداة هي نموذج بسيط يسمح لك بطرح طلب للترجمة إلى الخدمة.
هذا يضيف وظائف حيث عند اختيار 'new' سند يمكنكم ترجمته

رمز الترجمة
مرسل الترجمة
في صفحة محرر الملاحظات خاصتنا قمت بإضافة بعض البرمجة التي تحتوي على قطرة صغيرة (in _LanguageDropDown.cshtml) التي تسمح لك بإختيار اللغة التي تريد ترجمتها إلى.
@if (Model.IsNew)
{
var translationHidden = Model.TranslationTasks.Any() ? "" : "hidden";
<p class="text-blue-dark dark:text-blue-light inline-flex items-center justify-center space-x-2">
<partial name="_LanguageDropDown" for="Languages"/>
<button class="btn btn-outline btn-sm mt-1" x-on:click="window.mostlylucid.translations.submitTranslation"><i class='bx bx-send'></i>Translate</button>
</p>
<div id="translations" class="@translationHidden">
<partial name="_GetTranslations" model="Model.TranslationTasks" />
</div>
<div id="translatedcontent" class="hidden">
<textarea class="hidden" id="translatedcontentarea"></textarea>
</div>
}
_LanguageDropDown
لدينا _LanguageDropDown المشهد الجزئي هو سحب بسيط يسمح لك بتحديد اللغة التي تريد أن تترجمها إلى. هذا مألوف من قائمة اللغات في Languages خصائص النموذج.
يمكنكم أن تروا أنه يستخدم Alpine.js للتعامل مع الهبوط و لتحديد اللغة المختارة والعلم الذي يجب أن يظهر في الجزء الرئيسي من الخيار. إنه أيضاً يحدد الرمز القصير للغة التي تستخدم عند تقديم طلب الترجمةM SK2
باستخدام آلبينغ يعني أننا نحافظ على الحد الأدنى , JavaScript المشار إليه محليا في نظرتنا . هذه طريقة عظيمة لجعل نظرتك نظيفة وسهلة القراءة
@using Mostlylucid.Helpers
@model List<string>
<div id="LanguageDropDown" x-data="{
open: false,
selectedLanguage: 'Select Language',
selectedFlag: '' ,
selectedShortCode:''
}" class="relative inline-block mt-3">
<!-- Dropdown Button -->
<button x-on:click="open = !open" class="btn btn-sm btn-outline flex items-center space-x-2">
<!-- Dynamically Show the Flag Icon -->
<template x-if="selectedFlag">
<img :src="selectedFlag" class="h-4 w-4 rounded outline outline-1 outline-green-dark dark:outline-white" alt="Selected Language Flag">
</template>
<span x-text="selectedLanguage"></span>
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
<!-- Dropdown Menu -->
<div x-show="open" x-on:click.away="open = false"
class="absolute left-0 mt-2 w-64 rounded-md shadow-lg dark:bg-custom-dark-bg bg-white ring-1 ring-black ring-opacity-5 z-50">
<ul class="p-2">
@foreach (var language in Model)
{
<li>
<a href="#"
x-on:click.prevent="selectedLanguage = '@(language.ConvertCodeToLanguage())'; selectedFlag = '/img/flags/@(language).svg'; selectedShortCode='@language'; open = false"
class="flex dark:text-white text-black items-center p-2 hover:bg-gray-100">
<img src="/img/flags/@(language).svg" asp-append-version="true" class="ml-2 h-4 w-4 mr-4 rounded outline outline-1 outline-green-dark dark:outline-white" alt="@language"> @language.ConvertCodeToLanguage()
</a>
</li>
}
</ul>
</div>
</div>
أرسلوا الترجمة
ستلاحظون أن هذا يحتوي على بعض شفرة Apline.js التي تتصل إلى window.mostlylucid.translations.submitTranslation وظيفة. هذه الوظيفة تم تعريفها في translations.js وهو مضمن في _Layout.cshtml فايل.
export function submitTranslation() {
const languageDropDown = document.getElementById('LanguageDropDown');
// Access Alpine.js data using Apline.$data (Alpine.js internal structure)
const alpineData = Alpine.$data(languageDropDown);
const shortCode = alpineData.selectedShortCode;
const markdown = simplemde.value();
if (shortCode === '' || markdown === '') return;
// Create the data object that matches your model
const model = {
Language: shortCode,
OriginalMarkdown: markdown
};
// Perform the fetch request to start the translation using POST
fetch('/api/translate/start-translation', {
method: 'POST',
headers: {
'Content-Type': 'application/json' // The content type should be JSON
},
body: JSON.stringify(model) // Send the data object as JSON
})
.then(function(response) {
if (response.ok) {
// Process the returned task ID
return response.json(); // Parse the JSON response (assuming the task ID is returned in JSON)
} else {
console.error('Failed to start the translation');
}
})
.then(function(taskId) {
if (taskId) {
console.log("Task ID:", taskId);
// Trigger an HTMX request to get the translations after saving
htmx.ajax('get', "/editor/get-translations", {
target: '#translations', // Update this element with the response
swap: 'innerHTML', // Replace the content inside the target
}).then(function () {
// Remove the hidden class after the content is updated
document.getElementById('translations').classList.remove('hidden');
});
}
})
.catch(function(error) {
// Handle any errors that occur during the fetch
console.error('An error occurred:', error);
});
رسم السلسلة
هذا الرمز وصف في رسم التسلسل التالي:
sequenceDiagram
autonumber
actor User
participant JS as Client JS
participant Alpine as Alpine.js
participant FetchAPI as Fetch API
participant HTMX as HTMX
User->>JS: Trigger submitTranslation()
JS->>Alpine: Access data from LanguageDropDown via Alpine.js
Alpine-->>JS: Return selectedShortCode and markdown
JS->>JS: Check if shortCode and markdown are not empty
alt shortCode or markdown is empty
JS->>User: Do nothing
else
JS->>FetchAPI: Send POST request with model {Language, OriginalMarkdown}
FetchAPI-->>JS: Return taskId in JSON response
alt response is ok
JS->>HTMX: Send GET request to /editor/get-translations via HTMX
HTMX-->>JS: Return updated translations
JS->>DOM: Remove 'hidden' class from translations element
else
JS->>Console: Log "Failed to start the translation"
end
end
JS->>Console: Handle any fetch errors
على الرغم من أن هذا يبدو مثل الكثير من البرمجة
نرسل طلباً POST إلى сервер مع المحتوى اللغوي والتقليل . هذا يذهب إلى نقطة نهاية تسمى
start-translationالذي هو محدد فيTranslationAPI. هذا يبدأ من مهمة الترجمة و يضيف هذه المهمة إلى المخزن لهذا المستخدميستجيب сервер بطاقة مهمة ( التي نسجلها لكننا لا نستخدمها
ومن ثم نقوم بإرسال طلب GET إلى сервер للحصول على الترجمات. يتم ذلك باستخدام HTMX الذي هو مكتبة تسمح لك ب تحديث أجزاء من الصفحة دون تحديث صفحة كاملة. هذا أداة قوية جداً و تستخدم في العديد من الأماكن في هذه التطبيقM SK2
نقطة نهاية الترجمة البداية
هذا جهاز تحكم WebAPI الذي يأخذ الطلبات التي تحتوي على إشارة ورمز لغة. ومن ثم يرسل الطلب إلى خادم الترجمة الخلفية لدينا, يقوم بحفظ المهمة مرة أخرى مع ID المستخدم M SK2 مكون في cookie ) ويعيد ID المهمة إلى المتصل ( قمت بإيقاف خاصية ValidateAntiForgeryToken لحد الآن لأنني ' أنا لا أستخدمه
[HttpPost("start-translation")]
// [ValidateAntiForgeryToken]
public async Task<Results<Ok<string>, BadRequest<string>>> StartTranslation([FromBody] MarkdownTranslationModel model)
{
if(ModelState.IsValid == false)
{
return TypedResults.BadRequest("Invalid model");
}
if(!backgroundTranslateService.TranslationServiceUp)
{
return TypedResults.BadRequest("Translation service is down");
}
// Create a unique identifier for this translation task
var taskId = Guid.NewGuid().ToString("N");
var userId = Request.GetUserId(Response);
// Trigger translation and store the associated task
var translationTask = await backgroundTranslateService.Translate(model);
var translateTask = new TranslateTask(taskId, DateTime.Now, model.Language, translationTask);
translateCacheService.AddTask(userId, translateTask);
// Return the task ID to the client
return TypedResults.Ok(taskId);
}
نقطة النهاية للحصول على الترجمات
هذا يتم طلبه باستخدام HTMX ويعيد الترجمات للمستخدم الحالي
[HttpGet]
[Route("get-translations")]
public IActionResult GetTranslations()
{
var userId = Request.GetUserId(Response);
var tasks = translateCacheService.GetTasks(userId);
var translations = tasks.Select(x=> new TranslateResultTask(x, false)).ToList();
return PartialView("_GetTranslations", translations);
}
رؤية الحصول على الترجمات الجزئية
هذه نظرة بسيطة تستخدم HTMX لإجراء إستطلاع على сервер كل 5 ثوان للحصول على الترجمات للمستخدم الحالي. تظهر جدولا من الترجمة مع رابط لرؤية الترجمه
ولديه أيضاً معالجة عندما تكون جميع الترجمات مكتملة لإيقاف الإنتخابات ( عن طريق وضع المحفز إلى none). و لإظهار رسالة بديلة عندما لا توجدترجمات.
أستخدم أيضا مكتبة Humanizer لعرض الوقت الذي يستغرقه إنهاء الترجمة في شكل بشري قابل للقراءة.
هذا ينتج عن هذه الرؤية:

@using Humanizer
@using Mostlylucid.Helpers
@model List<Mostlylucid.MarkdownTranslator.Models.TranslateResultTask>
@{
var allCompleted = Model.All(x => x.Completed);
var noTranslations = Model.Count == 0;
var trigger = allCompleted ? "none" : "every 5s";
if (noTranslations)
{
<div class="alert alert-info" role="alert">
No translations have been requested yet.
</div>
}
else
{
<div class="translationpoller" hx-controller="Editor" hx-action="GetTranslations" hx-get hx-swap="outerHTML" hx-trigger="@trigger">
<table class="table">
<thead>
<th>
@Html.DisplayNameFor(model => model[0].TaskId)
</th>
<th>
@Html.DisplayNameFor(model => model[0].Completed)
</th>
<th >
@Html.DisplayNameFor(model => model[0].Language)
</th>
<th>
@Html.DisplayNameFor(model => model[0].TotalMilliseconds)
</th>
</thead>
@foreach (var item in Model)
{
<tr>
<td> <a href="#" x-on:click.prevent="window.mostlylucid.translations.viewTranslation('@item.TaskId')">View</a></td>
<td>@if (item.Completed)
{
<i class='bx bx-check text-green'></i>
}
else
{
<i class='bx bx-loader-alt animate-spin dark:text-white text-black'></i>
}
</td>
<td>
<p class="flex items-center">
<img src="/img/flags/@(item.Language).svg" asp-append-version="true" class="ml-2 h-4 w-4 mr-4 rounded outline outline-1 outline-green-dark dark:outline-white" alt="@item.Language">
@item.Language.ConvertCodeToLanguage()
</p>
</td>
<td>@(TimeSpan.FromMilliseconds(item.TotalMilliseconds).Humanize())</td>
</tr>
}
</table>
</div>
}
}
وظيفة الرؤية للترجمة
كما ترون في المشهد الأعلى، نتصل إلى آلبينز قليلاً على النقر لرؤية الترجمة. هذه أداة بسيطة تحصل على الترجمات من сервер وتظهرها في حوار موادي
<a href="#" x-on:click.prevent="window.mostlylucid.translations.viewTranslation('@item.TaskId')">View</a>
الذي يتصل إلى هذا . كل ما يفعله هو الحصول على التحويل المكتظم من сервер ويعرضه في الصفحة .
export function viewTranslation(taskId) {
// Construct the URL with the query parameters
const url = `/api/translate/get-translation/${taskId}`;
// Fetch call to the API endpoint
fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json' // Indicate that we expect a JSON response
}
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data =>
{
let translatedContentArea = document.getElementById("translatedcontent")
translatedContentArea.classList.remove("hidden");
let textArea = document.getElementById('translatedcontentarea');
textArea.classList.remove('hidden');
textArea.value = data.originalMarkdown;
simplemde.value(data.translatedMarkdown);
}) // Log the successful response data
.catch(error => console.error('Error:', error)); // Handle any errors
}
نقطة النهاية للحصول على الترجمة
هذا يشبه الطريقة السابقة للحصول على قائمة من الترجمات إلا أنه يحصل على ترجمة واحدة مع OriginalMarkdown و TranslatedMarkdown مأهولة:
[HttpGet]
[Route("get-translation/{taskId}")]
public Results<JsonHttpResult<TranslateResultTask>, BadRequest<string>> GetTranslation(string taskId)
{
var userId = Request.GetUserId(Response);
var tasks = translateCacheService.GetTasks(userId);
var translationTask = tasks.FirstOrDefault(t => t.TaskId == taskId);
if (translationTask == null) return TypedResults.BadRequest("Task not found");
var result = new TranslateResultTask(translationTask, true);
return TypedResults.Json(result);
}
في الختام
نتيجة كل هذا هو أنه يمكنك الآن تقديم طلب ترجمة ورؤية وضع الترجمة في المحرر بعد ذلك.
لدي المزيد أريد أن أفعله هنا بما في ذلك إعادة -التقليل من تدفق علامة الترجمة للمحتوى المترجمة وغيرهم . لكن هذا ,;' , هو ممتع هذا الموقع .; , ليس كل شيء صافي تماما ولكنه , ' , كل الشفرة الحقيقية يمكنك اللعب بها كما قمت ببناءها . مرة أخرى, يمكنكم رؤية كل شفرة المصدر لهذا على GitHub صفحة. لذا من فضلكم ذهبوا وبحثوا في ذلك إذا أردت أن تروا المزيد.