背景翻译: Pt.} @ 3
一. 导言导言 导言 导言 导言
在前几篇文章“,”中,我们讨论过翻译在网络应用程序背景下的重要性 “.” 我们还探索了使用简易NMT图书馆进行ASP=.NET Core application@.}翻译的事宜。
再说一次MSMK0 你可以看到我所有的源代码 吉特胡布 页面@. @
前一篇文章
在此我们添加一个小工具, 向部分描述的“ 2.” 服务提供后轮工作。 此工具是一个简单格式, 可以让您向服务提交翻译请求@ MS K1} It'} 然后缓存并加入到队列中, 为您提供有关翻译状态的信息\ .}
这将添加功能, 当选择 {'} new ' 文档时您可以将它翻译为 < MS K2 > 。

翻译编码
翻译提交器
在我们的 Markdown 编辑器页面上, 我添加了一些代码, 其中含有一点下调@ ( @ 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 区域视图是一个简单的自下下载选项,它允许您选择要翻译到.}这在语言列表中被包含 Languages 模型“.”的属性
您可以看到它使用 Alpine.js 来处理下调并设置选定语言和旗帜以在主选择部分显示 .}它还设定提交翻译请求时所用语言的短代码@.
使用 Alping 意味着我们保留本地引用的 JavaScript 最小值@. @ this is a great way to keep your option clean and easy to readQ.}, 这是让您的观点保持清晰和易读的绝佳方法 。
@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 请求到带有语言和标记内容的服务器@. @ this to the endpoint called
start-translation定义在TranslationAPI. 这将启动翻译任务, 并将此任务添加到此用户的缓存@.服务器响应任务 Id {(} 。 虽然我们登录了这个任务, 但使用)
然后我们向服务器发送一个 Get 请求以获取翻译@.。 这是使用 HTMX完成的, 这个图书馆允许您更新页面的部分内容而不进行整页刷新@ .。 这是一个非常强大的工具, 此应用程序的许多地方都使用了这个工具 @.}
开始翻译结束点
这是WebAPI 控制器, 它接收包含标记的请求和语言代码@ .。 然后将请求发送到我们的背景翻译服务器 @ MS K1} 缓存此项任务, 并将任务 Id 返回客户端=.} (I 目前禁用了验证的 AtiforgeryToken 属性, 因为 I'm 不使用它@)}
[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);
}
Get 翻译结束点
使用 HTMX 请求此选项, 并返回当前用户的翻译@ . @ this is a simple endpoint 从缓存中获取译文并将其反馈到客户端\ .}
[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% 秒对服务器进行民意测验, 以获得当前用户的翻译@ MS K1 @ 它会显示一个带有链接的翻译表格, 以便查看翻译@ .} *
当所有的翻译完成以停止投票时, none). 并在没有译文时显示其它消息@.
我还使用人文化器库显示完成可读翻译所需的时间@. @%
由此可见,

@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>
}
}
视图翻译函数
正如您在上面的视图中看到的那样,我们请点击一个小 Alping 键以查看翻译@.}这是一个简单的函数,从服务器上获取译文并在模式对话框中显示=.}
<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);
}
在结论中的结论
所有这一切的结果是,您现在可以提交翻译请求并查看在它完成后编辑的翻译状态@' @s complete .} I' 会更详细地了解下一篇文章中的翻译服务是如何运作的#.}
我还有很多要做的事, 包括重新“-” 挂起 Render Markdown 流来进行翻译内容等的标记@.}(但这个网站的乐趣) @;'不是每个字都被完全擦亮了 但当我用 “.” 构建它时 你可以玩弄所有真实代码 再说一次MSMK0 你可以看到我所有的源代码 吉特胡布 .}所以如果你想看更多的话,请去看一下