taustat käännökset Pt. 3
Esitys
Edellä mainituissa artikloissa, olemme keskustelleet käännöksen merkityksestä web-sovellusten yhteydessä . Olemme myös tutkineet EasyNMT-kirjaston käyttöä käännösten suorittamiseen ASP:ssa.NET Core -sovellusessaM SK3 Tässä artikkelissa aion käsitellä sitä, miten olen lisännyt taustapalvelun hakemukseen, jonka avulla voitte esittää käännöshakemuksen.
Jälleen kerran, voitte nähdä tämän koko lähdekoodin GitHub sivu.
Edellinen artikkeli
Siihen lisätään pieni väline, joka toimittaa kädentakaiset työt palvelulle, jota olemme yksityisttäneet osittain.2. Tämä väline on yksinkertainen lomake, jonka avulla voitte jättää käännöshakemuksen palveluun.
Tämä lisää toiminnallisuutta, kun valittaessa 'newM SK1 asiakirjaa voidaan kääntää se.

käännöslaki
Tulkkauslähettiläs
Markdown-editorisivuillemme lisäsin muutaman koodin, joka sisältää pienen laskun (in _LanguageDropDown.cshtml), jonka avulla voidaan valita kieli, jota haluatte kääntää
@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 -kieli
Meidän _LanguageDropDown osittainen näkemys on yksinkertainen pudotus, jonka avulla voidaan valita kieli, jota haluatte kääntää .-kieleen. Languages mallin omaisuus.
Voitte nähdä, että se käyttää Alpine-järjestelmää.js:tä laskun käsittelemiseen ja valitsemansa kielen ja lipun asettamiseen päävalinnan kohdassa.
Alpingin käyttö merkitsee sitä, että säilytämme vähimmäismäärän, paikallisesti viittaavan JavaScript-koodin näkemyksissämme . Tämä on erinomainen tapa pitää näkemyksenne puhtaina ja helppolukuisina
@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>
Esityskäännöksi
Huomaatte, että tässä on Apline-koodi, joka kutsuu meidän window.mostlylucid.translations.submitTranslation function. Funktio määritellään translations.js asiakirja, joka sisältyy _Layout.cshtml asiakirja.
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);
});
Jatkodiagrammi
Tämä koodi on kuvattu seuraavassa järjestyssuunnitelmassa:
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
Vaikka tämä näyttääkin paljon kodilta, se on itse asiassa varsin yksinkertainen.
Lähetämme serverille POST-pyynnön, johon sisältyy kieli ja merkitsevä sisältö.
start-translationjoka on määriteltyTranslationAPI. Tämä aloittaa käännöstehtävän ja lisää tehtävän tämän käyttäjän Cache-tietokantaanServer vastaa tehtävän numerolla (, joka tallennetaan mutta jota ei käytetä.
Sen jälkeen lähetämme GET-pyynnön palvelimelle saadaksemme käännökset. Tämä tehdään käyttämällä HTMX-libraaria, joka antaa mahdollisuuden päivittää sivun osia ilman koko sivun päivittämistäM SK1 Tämä on erittäin tehokas väline ja sitä käytetään monissa kohdissa tässä sovelluksessa.
Aloituskäännöksen loppupiste
Kyseessä on WebAPI-ohjaaja, joka ottaa pyynnöt, jotka sisältävät merkitsemisen ja kielenkoodin. (Onnistin ValidateAntiForgeryToken -属性ta toistaiseksi, koska en käytä sitä.
[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);
}
Tulkkausten saattaminen loppupisteessä
Tämä on pyydetty käyttämällä HTMX-järjestelmää ja palauttaa käännökset nykyiselle käyttäjälle. Tämä on yksinkertainen loppupiste, joka saa käännöt cachesta ja palaa ne clientille
[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);
}
Osittainen katsaus käännöksiin
Tämä on yksinkertainen näkemys, jossa käytetään HTMX-järjestelmää verkon arvioimiseksi joka 5 sekunti saadakseen nykyisen käyttäjän käännökset.
Siinä on myös käsiteltävä, kun kaikki käännökset ovat valmiita pysäyttämään äänestys ( asettamalla lähtölaukaus none). Ja vaihtoehtoisen viestin näyttäminen, jos käännöksiä ei ole
Käytän myös Humanizer-kirjastoa kuvatakseni, kuinka kauan on kulunut käännöksen loppuun saattamiseen inhimillisessä luettavissa olevassa muodossa.
Tämä johtaa tähän näkemykseen:

@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>
}
}
Näkemyksen käännöstoiminta
Kuten näette edellä esitetyssä kuvauksessa, kutsumme pieneen Alping-osoitteeseen ja katsomme käännöksen.
<a href="#" x-on:click.prevent="window.mostlylucid.translations.viewTranslation('@item.TaskId')">View</a>
Se kutsuu tähän. Kaikki, mitä se tekee, on saada vastaanotettu transaltio serveristä ja esittää sen sivussa
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
}
Tulkkauksen saattaminen loppupisteessä
Tämä on samanlainen kuin aikaisempi menetelmä saada käännösten luetteloa lukuun ottamatta sitä, että se saa yhden käännöksen OriginalMarkdown ja TranslatedMarkdown populated:
[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);
}
Päätelmissä
Kaiken tämän seurauksena voitte nyt toimittaa käännöshakemuksen ja nähdä käännöksen tilanteen kääntäjässä sen jälkeen.
Minulla on enemmän, mitä haluan tehdä täällä muun muassa uudelleen-tarkastella Render Markdown-virtaa käännettyjen sisältöjen jne. osaltaM SK1 mutta se;' on tämän sivuston hauskaaMSC3 kaikki ei ole täysin poltettu vaan se ' kaikki reaalikoodi, jonka kanssa voitte leikkiä, kun laadin sen ulkopuolelle Jälleen kerran, voitte nähdä tämän koko lähdekoodin GitHub sivu. Olkaa hyvä ja katsokaa sitä, jos haluatte nähdä enemmänM SK1