Back to "Markdown-asiakirjojen automaattinen kääntäminen EasyNMT:in avulla"

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

EasyNMT Markdown

Markdown-asiakirjojen automaattinen kääntäminen EasyNMT:in avulla

Saturday, 03 August 2024

Esitys

EasyNMT on paikallisesti asennettavissa oleva palvelu, joka tarjoaa yksinkertaisen yhteyden moniin kääntämispalveluihin.. Tässä oppauksessa, käytämme EasyN MT:ia merkintäaineiston automaattiseen käännökseen englannista moneen kieleen.

Voitaisiin löytää kaikki tämän oppaan asiakirjat GitHub-tietokanta hankkeelle.

Tämän tuloksena syntyi uusien merkintäohjelmien BUNCH kohdekielissä. Tämä on erittäin yksinkertainen tapa saada blogin artikkeli käännetty monille kielille.

Tulkitut postit

Edellytykset

EasyNMT:n asentaminen on välttämätöntä tämän oppaan noudattamiseksi.. Käyn sitä yleensä Docker-palveluna. täällä , jossa käsitellään sitä, miten sitä voidaan käyttää docker-palveluna.

docker run -d -p 24080:80 --env MAX_WORKERS_BACKEND=6 --env MAX_WORKERS_FRONTEND=6 easynmt/api:2.0-cpu

tai jos käytettävissä on NVIDIA GPU:

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- ja MAX _WOSKERS_FRONTEND-ympäristövaihtolaiset määräävät EasyNMT:n käyttämien työntekijöiden määrän.

PANEE MERKILLE: EasyNMT ei ole~'SMOOTHEST-palvelu, jota voida käyttää ,, mutta se on paras, mitä tähän tarkoitukseen on löydetty M SK3~Idän input stringin osalta.

Oh ja se myös käännöi 'päätelmäM SK1 joillekin hölynpölylle ehdotuksen jättämisestä EU:n käsiteltäväksi

Naiivi lähestymistapa load balancing -järjestelmään

Easy NMT on raivokas hirviö, kun on kyse resursseista, joten MarkdownTranslatorService-palvelussani minulla on super yksinkertainen sattumanvarainen IP-selektori, joka vain pyörittää luettelon koneista käytettäviä IP-osoitteita.

Alun perin tämä menee model_name menetelmä EasyNMT-palvelussa, tämä on nopea, yksinkertainen tapa tarkistaa, onko palvelu alkanutM SK2 jos se on , se lisää IP:tä työssä olevien IP:ien luetteloon

    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;
        }
    }

Sen jälkeen Post menetelmä MarkdownTranslatorService käännetään työ IP:ien läpi löytääksemme seuraavan

          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;

Tämä on erittäin yksinkertainen tapa tasapainottaa pyyntöjä monilla laitoksilla.

Smick currentIPIndex = (currentIPIndex + 1) % IPs.Length; vain kiertää luetteloa IP-osoitteista alkaen 0 ja menemään luettelon pitkälle .

Markdown-asiakirjan kääntäminen

Tämä on se koodi, joka minulla on MarkdownTranslatorService:ssa.

    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();
    }

Kuten näette, sillä on useita vaiheita:

  1. var document = Markdig.Markdown.Parse(markdown); - Tämä parsi merkitsevää stringiä asiakirjaksi
  2. var textStrings = ExtractTextStrings(document); - Tämä vie tekstistringit asiakirjasta Siinä käytetään menetelmää
  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);
    } 

Tämä varmistaa, että 'sana' on todellakin työ.

  1. var batchSize = 10; - Tämä asettaa käännöspalvelun jakelukokonaisuudenM SK1 EasyNMT:llä on raja sanamäärälle, jonka se voi kääntää yhdessä käännössä.
  2. csharp await Post(batch, targetLang, cancellationToken) Tämä kutsuu menetelmään, joka siirtää paketin EasyNMT-palveluun.
    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;
        }
    }
  1. ReinsertTranslatedStrings(document, translatedStrings.ToArray()); - Tämä siirtää uudelleen käännöstetyt stringit takaisin asiakirjaan.

    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++;
            }
        }
    }

Järjestetty palvelu

Kaikkien näiden toimenpiteiden toteuttamiseksi käytän IHostedLifetimeService-palvelua, joka käynnistetään ohjelmissa.cs-tietokannassa. Tämä palvelu lukee merkitsemistietokantaanM SK2 se kääntää sen moniin kieliin ja kirjoittaa käännettyjä tietokantoja diskille .

  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);
                }
            });
        }
       

Kuten näette, se myös tarkistaa tiedoston hashia nähdäkseen, onko se muuttunut ennen sen kääntämistä. Tämä on välttääkseen sellaisten tietojen kääntäminen, jotka eivät ole muuttuneet

Tämä tehdään laskemalla alkuperäisen merkintöaineiston nopea hash ja testaamalla, onko se muuttunut ennen sen kääntämistä.

    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;
    }

Ohjelmien käyttöönotto.cs on varsin yksinkertainen:


    builder.Services.AddHostedService<BackgroundTranslateService>();
services.AddHttpClient<MarkdownTranslatorService>(options =>
{
    options.Timeout = TimeSpan.FromMinutes(15);
});

Järjestin HostedService (BackgroundTranslateService) ja HttpClient MarkdownTranslatorService . IHostedLifetimeService on pitkä, taustalla toimittava palvelu.. Se on hyvä paikka sijoittaa palveluja, jotka tarvitsevat jatkuvia toimia taustalla tai jotka vievät vain jonkin aikaa saattaakseen päätökseen.IHosted LifetimeService -yhteys on hieman joustavampi kuin vanha, ja se antaa meille mahdollisuuden hoitaa tehtävät täysin taustalla helpommin kuin vanha.

Siten voitte nähdä, että I'm asettaa HttpClient'n tauon 15 minuuteille . Tämä johtuu siitä, että EasyNMT voi reagoida hieman hitaammin.

Päätelmissä

Tämä on melko yksinkertainen tapa kääntää merkintöjä sisältävä asiakirja useisiin kieliin. Se' ei ole täydellinenM SK2, mutta se ' on hyvä aloitusMSC4 Käytän tätä yleensä jokaista uutta blogin artikkelia varten ja sitä käytetään MarkdownBlogService käännettyjen nimien vetäminen kullekin blogin postille.

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.