FunktionerPluginPriserRessourcer
Skift sprog
RessourcerHvordan man automatiserer .po-oversættelse i din CI/CD-pipeline

Hvordan man automatiserer .po-oversættelse i din CI/CD-pipeline

SimplePoTranslate Team9. juni 2026
Hvordan man automatiserer .po-oversættelse i din CI/CD-pipeline

Dit team merger til main fjorten gange om dagen. Hver af disse merges kan tilføje en ny brugerrettet streng – en knaptekst, en fejlmeddelelse, et værktøjstip. Og hver af disse strenge starter kun på engelsk. Et sted i din releaseproces skal et menneske bemærke de nye strenge, eksportere dem, få dem oversat, indsætte dem igen og genkompilere. Dette menneske er en flaskehals, og i et hurtigtleverende team betyder den flaskehals, at dine tyske brugere ser engelske pladsholdere i to sprints.

Løsningen er at automatisere oversættelse i din CI/CD-pipeline, så nye strenge bliver oversat på den samme merge, der introducerede dem – ingen mennesker i den kritiske sti for rutinetilfældet. Dette er fuldt ud opnåeligt i dag, men kun hvis du undgår den fælde, de fleste teams falder i: manuelt at lave rå LLM-kald, der ubemærket ødelægger dine %s-pladsholdere og din .po-struktur. Denne guide gennemgår et realistisk CI-pipelinemønster, et fungerende GitHub Actions-skelet og de designbeslutninger – idempotens, oversæt-kun-nye, review-gates – der adskiller en pipeline, der hjælper, fra en, der sender ødelagte builds ud.

Hvorfor manuel oversættelse er en flaskehals i releases

Svaret på "hvorfor ikke bare oversætte før hver release" er, at timingen aldrig stemmer overens. Strenge tilføjes løbende af udviklere, men oversættelse sker i batches af en anden person med en anden tidsplan. Kløften mellem disse to rytmer er der, hvor din lokaliseringsgæld akkumuleres.

Problemet med to rytmer

Forestil dig det manuelle flow. En udvikler tilføjer __( 'Export to CSV', 'mytextdomain' ) og merger. Ingen genopretter .pot-filen. To uger senere kører nogen wp i18n make-pot, bemærker fyrre nye uoversatte strenge (nogle fra udviklere, der siden er taget på ferie), gætter på intentionen bag halvdelen af dem og sender en .po-fil til en oversætter. Oversættelsen kommer tilbage, indsættes, og måske overlevede pladsholderne, og måske gjorde de ikke. I mellemtiden blev tre releases udgivet med disse strenge på engelsk.

Hvert skridt er manuelt, batch-baseret og fejlbehæftet. Målet med CI/CD-automatisering er at samle dette til noget, der kører automatisk ved merge, kun oversætter det, der faktisk er ændret, og fejler højlydt, når noget ser forkert ud – hvilket forvandler oversættelse fra en periodisk pligt til en usynlig del af pipelinen.

Pipeline-mønsteret: Udtræk, Diff, Oversæt, Commit

På et højt niveau udfører et automatiseret oversættelsesjob fire trin ved merge til main: genopret skabelonen, opdag hvad der er nyt, oversæt kun disse strenge, og commit resultaterne tilbage. Hele processen bør være idempotent – at køre den på en merge uden strengændringer skal producere nul diff og nul støj.

Udtræk og Diff

Trin ét er udtrækning. Genopret .pot-filen fra den aktuelle kilde, så den afspejler hver streng i kodebasen:

wp i18n make-pot . languages/myplugin.pot

Trin to er diff. Dette er den vigtigste designbeslutning i hele pipelinen. Du ønsker ikke at genoversætte hver streng ved hver merge – det spilder API-kald, risikerer at genoversætte strenge, et menneske allerede har korrigeret, og producerer enorme review-diffs. I stedet merger du den friske .pot-fil ind i hver eksisterende .po-fil og oversætter kun de poster, der nu er tomme (de ægte nye strenge):

# Merge new template into each locale, preserving existing translations.
# Newly-added strings appear with empty msgstr; --backup=none keeps the tree clean.
for po in languages/*.po; do
    msgmerge --update --backup=none "$po" languages/myplugin.pot
done

Efter msgmerge har kun helt nye strenge en tom msgstr. Alt tidligere oversat er uberørt. Den egenskab er det, der gør pipelinen idempotent, og det, der holder dine review-diffs små og gennemgåelige.

Oversæt og Commit

Trin tre sender kun de tomme poster til et oversættelsestrin. Trin fire committer den opdaterede .po-fil, kompilerer .mo-filen og genopretter eventuelle JSON-filer. Vi vil herefter forbinde alt dette til GitHub Actions.

Et GitHub Actions-skelet

Her er svaret på "hvordan ser dette egentlig ud i en workflow-fil": et job udløst ved push til main, der kører extract-diff-translate-commit-cyklussen og åbner en pull request med resultaterne i stedet for at committe direkte til main.

Workflow-filen

name: Translate on merge

on:
  push:
    branches: [ main ]

jobs:
  translate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install WP-CLI and gettext
        run: |
          sudo apt-get update && sudo apt-get install -y gettext
          curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
          sudo mv wp-cli.phar /usr/local/bin/wp && sudo chmod +x /usr/local/bin/wp

      - name: Regenerate template and merge into locales
        run: |
          wp i18n make-pot . languages/myplugin.pot
          for po in languages/*.po; do
            msgmerge --update --backup=none "$po" languages/myplugin.pot
          done

      - name: Translate only new (empty) strings
        env:
          TRANSLATE_API_KEY: ${{ secrets.TRANSLATE_API_KEY }}
        run: ./scripts/translate-new-strings.sh languages/

      - name: Compile .mo and JSON
        run: |
          wp i18n make-mo languages/
          wp i18n make-json languages/ --no-purge

      - name: Open pull request with translations
        uses: peter-evans/create-pull-request@v6
        with:
          branch: chore/auto-translations
          title: "chore: auto-translate new strings"
          commit-message: "chore: auto-translate new strings"

Hvor det rigtige arbejde gemmer sig

translate-new-strings.sh-trinnet er der, hvor den faktiske oversættelse finder sted. Den naive version af det script læser hver tom post, sender den til en LLM API én streng ad gangen og indsætter svaret tilbage. Den naive version er præcis fælden, og det er værd at være eksplicit omkring hvorfor.

Hvorfor manuelt udformede LLM-kald ødelægger dine filer

Det korte svar: rå LLM-kald behandler dine .po-strenge som prosa, og dine .po-strenge er ikke prosa – de er struktureret data med pladsholdere, flertalsformer og kontekst, som et chat-completion-kald med glæde ødelægger.

Pladsholderkorruptionen du ikke vil se i tests

Send Deleted %d of %s files til et generelt chat-endpoint, og du får måske Supprimé %d des %s fichiers (fint) eller Supprimé %d de % s fichiers (et mellemrum sneg sig ind i pladsholderen, og nu kaster sprintf() en fejl ved runtime). Send en flertals-post, og modellen kan slå to former sammen til én, hvilket ødelægger sprog med mere end to flertalskategorier. Send en streng med <a href="%s">, og modellen kan oversætte URL'en eller fjerne tagget. Ingen af disse fejl dukker op i dine tests, medmindre du specifikt tester gengivet output i hvert sprog – de dukker op som runtime-fejl i produktionen for brugere, du ikke kan læse fejlrapporter fra.

Vedligeholdelsesfælden

Du kan forsøge at forsvare dig mod dette med prompt engineering og regex-efterbehandling, og mange teams gør det. Problemet er, at du nu vedligeholder en skrøbelig oversættelsesmotor som et sideprojekt, hvor du genopdager hver eneste pladsholder-edge-case, som Gettext-økosystemet allerede har løst. Vi katalogiserer de specifikke måder, variabler bliver fordrejet på – og hvordan man forhindrer dem – i oversættelse af PO-filer uden at ødelægge kodevariabler. Læren derfra gælder direkte: modelkvaliteten er sjældent problemet; den strukturelle håndtering omkring den er.

Sådan passer en API-drevet cloud-oversætter ind i CI

Det er her, en API-drevet oversættelsestjeneste erstatter dit translate-new-strings.sh-gættearbejde. I stedet for manuelt at lave LLM-kald og efterbehandle regex, uploader dit CI-trin den ændrede .po-fil til en tjeneste, der allerede forstår Gettext-strukturen og returnerer et rent output. Pipeline-formen forbliver identisk – udtræk, diff, oversæt, commit – men det skrøbelige mellemtrin bliver et enkelt API-kald.

API-kaldet der erstatter dit script

SimplePoTranslate er bygget til netop dette. Den udstiller en cloud API egnet til automatisering og CI, så dit workflows oversættelsestrin bliver en anmodning, der overfører .po-filen og får en oversat tilbage, uden looping pr. streng. Dens Syntax Locking holder %s, %1$s, {count}, HTML og kode-tokens automatisk på plads – hele den klasse af fejl fra det forrige afsnit håndteres af motoren snarere end af regex, du vedligeholder. Med fuld Gettext plural- og msgctxt-support overlever flertalsformer og kontekst turen frem og tilbage, hvilket et chat-completion-kald ikke kan garantere.

Idempotens og review-gate forbliver din

To designbeslutninger tilhører stadig dig, uanset hvilken motor du bruger. For det første, idempotens: fortsæt med kun at oversætte de tomme poster efter msgmerge, så no-op merges ikke producerer nogen diff. For det andet, en review-gate: lad jobbet åbne en pull request, som skelettet ovenfor gør, snarere end at committe direkte til main. Maskinoversættelse er fremragende til hurtigt at få strenge live, men et menneskeligt blik før merge fanger den sjældne kontekstfejl – og en PR giver dig det blik uden at blokere for det almindelige tilfælde. Teams, der jonglerer med mange sprog eller mange kundesider, vil genkende denne form fra den ideelle lokaliserings-workflow for bureauer, hvor det samme automatiser-derefter-gennemse-mønster skalerer over snesevis af projekter.

Konklusion

For at automatisere oversættelse i CI/CD uden at udgive ødelagte builds, er mønsteret konsekvent: genopret .pot-filen ved merge, msgmerge den ind i hvert sprog for kun at vise nye strenge, oversæt kun disse strenge, og commit resultatet bag en pull-request review-gate. Idempotens holder dine diffs rene; review-gaten holder den sjældne dårlige oversættelse ude af produktionen.

Den del, der skal gøres rigtigt, er selve oversættelsestrinnet. Manuelt udformede LLM-kald vil ødelægge pladsholdere og flertalsformer på måder, dine tests ikke vil fange, og du vil bruge mere tid på at vedligeholde oversættelses-limen end den feature-kode, den betjener. En API-drevet motor med Syntax Locking fjerner hele den fejltilstand, så din pipeline oversætter nye strenge på den samme merge, der introducerede dem – og dine ikke-engelske brugere holder op med at se engelske pladsholdere.

Klar til at automatisere oversættelse i din pipeline uden at ødelægge pladsholdere? Prøv SimplePoTranslate gratis — intet kreditkort påkrævet. Start på det gratis niveau, valider API'et mod dine egne .po-filer, og integrer det i CI, når du er klar.