Topics

Een meertalige blog maken met EmDash - hoe je vertaal-API-kosten bespaart met SSR

  • column

Onze blog ゆるろぐ heeft onlangs stiekem een Engelse versie gekregen. Deze keer delen we wat achter de schermen gebeurde, vooral hoe we het probleem hebben opgelost dat SSR-sites met meertalige ondersteuning enorme API-kosten kunnen genereren.

Context: we gebruiken een CMS genaamd EmDash

Deze blog draait op EmDash, een op Astro gebaseerd CMS. We schrijven artikelen via de beheerpaneel en renderen de frontend met Astro server-side rendering (SSR).

De intuïtieve bedieningsstijl lijkt op WordPress, en het wordt soms de 'spirituele opvolger van WordPress' genoemd.

EmDash heeft ingebouwde ondersteuning voor 'meertalige inhoud', zodat artikelen, pagina's, menu's en taxonomieën (categorieën en tags) als afzonderlijke records per locale kunnen worden opgeslagen. Met het translationOf veld kunt u eenvoudig aangeven dat 'dit Engelse artikel een vertaling is van dit Japanse artikel', waarna het als een vertaalde versie van hetzelfde artikel wordt behandeld.

Migratie van microCMS naar EmDash + Cloudflare

Meertaligheid is eenvoudig met SSG, maar SSR is anders

Over het algemeen is het eenvoudig wanneer je 'meertalige vertaling via API automatiseren' met SSG (Static Site Generation) doet.

  • Tijdens het bouwen roep je de vertalings-API slechts één keer aan
  • Resultaten als statische bestanden in cache opslaan
  • Dan hoef je alleen nog maar die HTML te leveren

Dus ongeacht hoeveel artikelen je hebt, API-kosten ontstaan alleen "bij elke build". De frequentie is makkelijk onder controle en caching werkt rechtlijnig.

Maar Yurulog is SSR. Dus ga je dan API bij elk request aanroepen?

Het probleem zit hier. De CMS-pagina's van EmDash worden volgens de specificatie allemaal SSR (output: "server") gerenderd. In plaats van pagina's vooraf statisch in te bouwen, haalt het systeem bij elk binnenkomend request de nieuwste content uit de CMS API en database en rendert die.

Dus wat gebeurt er als je een simplistische aanpak gebruikt: "wanneer je de Engelse pagina toont, laat Gemini ter plekke vertalen en serveren"?

  • Bij elk request ontstaan kosten voor de vertaal-API
  • Latency neemt toe (je moet op het antwoord van het LLM wachten)
  • Dezelfde artikel kan elke keer een iets ander vertaalresultaat opleveren (geen reproduceerbaarheid)

Dit is volledig onacceptabel. Als je vanwege SSR voor "API bij elk request" kiest, dan vallen zowel kosten als UX uit elkaar.

Oplossing: vertaling verplaatsen van "bij aanvraag" naar "bij het maken van inhoud"

Het antwoord is eenvoudig: voer vertaling niet uit op het moment van aanvraag.

Concreet hebben we deze pipeline ingericht.

  1. Schrijf en publiceer het Japanse artikel in het EmDash-beheerderspaneel
  2. Voer het vertaalscript (scripts/translate.mjs) slechts eenmaal uit
    • Haal het Japanse artikel in Portable Text-formaat (gestructureerde rijke tekst) op via de EmDash-API
    • Extraheer alleen de tekstgedeelten die moeten worden vertaald en stuur deze naar de Gemini API (gemini-3.6-flash)
    • Blokken zoals afbeeldingen en Amazon-productkaarten worden niet vertaald, maar rechtstreeks doorgegeven
  3. Sla het vertaalresultaat op in de EmDash-database als een nieuw Engelstalig artikel
    • Geef de ID van het originele Japanse artikel op in translationOf om het als een Engelse versie van dezelfde artikelgroep te koppelen
    • Het wordt opgeslagen als concept, zodat u het kunt controleren voordat u het publiceert.

Dit betekent dat vertaalresultaten niet "ter plekke gegenereerd" zijn, maar als "inhoud in een ander taal die al in de CMS is opgeslagen" worden behandeld.

Op deze manier verschilt de SSR-aanvraagverwerking zelf niet van de normale artikelweergave. Met de standaard locale-query van microCMS ((getEmDashEntry(..., { locale: "en" }))) haalt u eenvoudig de al in de database opgeslagen Engelse inhoud op. De communicatie met de Gemini API vindt slechts één keer plaats wanneer het artikel wordt vertaald, en gebeurt helemaal niet tijdens het browsen.

Met andere woorden, dit is het diagram

Het moment waarop vertaling plaatsvindt

API-kosten per aanvraag

SSG + vertaling per keer

Tijdens het bouwen

Nul (statische bestandslevering)

SSR + vertaling per keer (geen goed voorbeeld)

Per aanvraag

Gegenereerd per artikel × aantal weergaven 😱

SSR + voorvertaalde inhoud opgeslagen in database (onze aanpak)

Bij inhoudscreatatie (eenmalig)

Nul

Het inzicht van vandaag was: het idee van SSG om alles in het build-proces in cache op te slaan, kunnen we naar de SSR-wereld brengen door voorvertaalde inhoud als permanent opgeslagen gegevens in de database vast te leggen. Ook met SSR wordt de inhoud zelf niet voortdurend dynamisch gegenereerd – door alleen de 'zware operatie' van vertalen van tevoren uit te voeren, blijft de daadwerkelijke levering net zo licht als bij een normale CMS-pagina.

Verschillende database in lokale en productieomgeving – het verhaal van talloze 'wacht, waar is het gebleven?'

Deze implementatie heeft me het meest uitgeput op dit punt. EmDash maakt gebruik van afzonderlijke databases voor de lokale ontwikkelingsomgeving en de productieomgeving. Logisch gezien, maar precies hierdoor heb ik meermaals meegemaakt dat 'een functie die net nog werkte, plots verdwenen is!'.

De symptomen zien er meestal als volgt uit

  • De navigatie in de header wordt plotseling leeg
  • Categories-lijst in footer verdwijnt
  • Sidebar-widget op artikelpagina verdwijnt
  • Zelf gemaakte lokale menu-items en widgets worden teruggezet

Bovendien gebeurt dit allemaal op het moment dat "het werkte net nog," dus eerst ga je jezelf afvragen: "Heb ik iets gebroken?" voordat je naar de oorzaak zoekt.

Oorzaak 1: Het importeren van de productiedatabase naar lokaal (pull.sh)

Tijdens de ontwikkeling heb ik het script dat de productiedatabase naar lokaal importeert meerdere keren uitgevoerd, omdat ik "de nieuwste productiegegevens ook lokaal wilde zien." Wanneer je dit doet, wordt de lokale database volledig overschreven met de productiestatus (waarin Engelse artikelen bijvoorbeeld nog in concept zijn of de menu-structuur nog niet klaar voor publicatie is).

Met andere woorden: zelfs als je lokaal een functie wilt controleren die in productie nog niet live is, wordt je lokale database direct na het importeren teruggezet naar de productiestatus, dus het werk dat je lokaal al had gedaan verdwijnt tijdelijk.

Oorzaak 2: De "seed"-verwerking die elke keer wordt uitgevoerd als je opnieuw inlogt op het lokale beheerpaneel

De tweede oorzaak was dat het mechanisme voor het opnieuw inloggen op het lokale beheerpaneel (een bypass-login voor ontwikkeling) elke keer ook het bestand seed.json (het initiële datadefinitiebestand) opnieuw inlaadde.

Dit was lastig, want:

  • Menu-items en widgets → worden volledig overschreven om exact met de seed-inhoud overeen te stemmen (= handmatig toegevoegde items lokaal verdwijnen)
  • Taxonomieën zoals categorieën en tags → 'bestaande items worden overgeslagen' dus verdwijnen niet

Dit waren dus specificaties met verschillend gedrag per item. Daarom trad het schijnbaar paradoxale fenomeen op dat 'categorieën behouden blijven, maar menu en widgets steeds opnieuw verdwijnen'.

Genomen maatregelen

Het was moeilijk om deze dubbele structuur (overschrijven via pull / overschrijven via seed) fundamenteel uit te roeien, dus als praktische compromis:

  • Alle dummy-inhoud voor demo (menu-items, widgets, categorieën enz.) uit seed.json verwijderen
  • Telkens wanneer je de lokale omgeving opnieuw binnengaat, de benodigde menu's en widgets via API opnieuw aanmaken

Dit is hoe we het hebben opgelost. Dit is geen radicale genezing maar een verzachtingsmaatregel, maar dit voorkomt in ieder geval dat 'onbedoelde demo-gegevens overschrijven' voorkomen.

Samenvatting

  • EmDash is oorspronkelijk een multilinguale CMS die per lokalisatie inhoud kan hebben
  • Met SSG kan 'vertaling bij buildtijd → cache' volstaan, maar met SSR wordt 'telkens vertalen' een financieel nachtmerrie
  • De oplossing is om de vertaling slechts eenmaal uit te voeren bij het maken van content, in plaats van bij elk verzoek, en het resultaat als normale CMS-content in de database op te slaan
  • De gebruikte vertaalengine is Gemini API ((gemini-3.6-flash))
  • Bij het meertalig maken van een CMS moet je echter niet vergeten dat het feit dat de database in lokale en productieomgeving gescheiden zijn, zelf nieuwe valkuilen creëert
  • Met name ontwikkeling waarbij je in de lokale omgeving al vooruitloopt op toestanden die nog niet in productie zijn, wordt gemakkelijk in beslag genomen door de bijeffecten van synchronisatiemechanismen voor omgevingen (pull, seeding, caching, enzovoort)

De les van dit keer is dat als je van tevoren begrijpt wanneer je vertaalt en wanneer en wat er in de omgevingssynchronisatie wordt overschreven, je met SSR ook zonder last van operationele kosten en incidenten meertalig kunt werken

Hoe zit het met het meertalig maken van uw site!

Auteur van dit artikel

Ik concentreer me op markup en ontwikkel frontends met JavaScript, React en Next.js. Het geeft me veel voldoening als een website waaraan ik heb gewerkt succesvol wordt gepubliceerd! Mijn hobby's zijn gitaar spelen. Ik ben dol op katten en zoete aardappelen🐱🍠

Hiracchi

Frontend-engineer / aangenomen in 2022

Artikelen van deze medewerker bekijken

Ons sterke punt is ons betrouwbare teamstructuur en snelle responsiviteit

Bij Liberogic worden ervaren teamleden actief ingezet voor projectvoering, wat door klanten zeer wordt gewaardeerd.
We wijzen vakbekwaam projectmanagers en directors aan en streven ernaar projecten soepel te laten verlopen. We voorkomen onnodig kostenverhogingen door volledig inzet te vermijden en wijzen middelen toe waar ze het meest geschikt zijn. Onze snelheid bij taakanalyse en bij het opmaken en indienen van offertes is goed bekend.

* Wij voeren niet actief SES-achtige permanente werkzaamheden uit, dus graag van tevoren dank voor uw begrip.

U kunt vrijwel alle grote projectmanagementtools en chattoolsgebruiken, zoals Slack, Teams, Redmine, Backlog, Asana, Jira, Notion, Google Workspace, Zoom en Webex.

Neem contact met ons op voor advies over uw webvragen.

Casestudies