Så automatiserar du .po-översättning i din CI/CD-pipeline

Ditt team mergar till main fjorton gånger om dagen. Var och en av dessa mergar kan lägga till en ny användarriktad sträng – en knappetikett, ett felmeddelande, ett verktygstips. Och var och en av dessa strängar börjar sitt liv endast på engelska. Någonstans i din releaseprocess måste en människa uppmärksamma de nya strängarna, exportera dem, få dem översatta, klistra in dem igen och kompilera om. Den människan är en flaskhals, och i ett snabbt levererande team betyder den flaskhalsen att dina tyska användare ser engelska platshållare i två sprintar.
Lösningen är att automatisera översättning i din CI/CD-pipeline så att nya strängar översätts vid samma merge som introducerade dem – ingen människa i den kritiska sökvägen för rutinfallen. Detta är fullt möjligt idag, men bara om du undviker fällan som de flesta team hamnar i: att handskriva råa LLM-anrop som tyst korrumperar dina %s-platshållare och din .po-struktur. Denna guide går igenom ett realistiskt CI-pipeline-mönster, ett fungerande GitHub Actions-skelett och designbesluten – idempotens, översätt-endast-nya, granskningsgrindar – som skiljer en pipeline som hjälper från en som levererar trasiga byggen.
Varför manuell översättning är en release-flaskhals
Svaret på "varför inte bara översätta före varje release" är att tiderna aldrig stämmer överens. Strängar läggs kontinuerligt till av utvecklare, men översättning sker i omgångar av en annan person enligt ett annat schema. Klyftan mellan dessa två kadenser är där din lokaliseringsskuld ackumuleras.
Problemet med två kadenser
Föreställ dig det manuella flödet. En utvecklare lägger till __( 'Export to CSV', 'mytextdomain' ) och mergar. Ingen regenererar .pot-filen. Två veckor senare kör någon wp i18n make-pot, upptäcker fyrtio nya oöversatta strängar (vissa från utvecklare som sedan åkt på semester), gissar sig till avsikten med hälften av dem och skickar en .po-fil till en översättare. Översättningen kommer tillbaka, klistras in, och kanske överlevde platshållarna och kanske inte. Under tiden levererades tre releaser med dessa strängar på engelska.
Varje steg där är manuellt, batch-baserat och felbenäget. Målet med CI/CD-automatisering är att omvandla detta till något som körs automatiskt vid merge, översätter endast det som faktiskt ändrats, och misslyckas högljutt när något ser fel ut – vilket förvandlar översättning från en periodisk syssla till en osynlig del av pipelinen.
Pipeline-mönstret: Extrahera, Skilja, Översätta, Committa
På en hög nivå kör ett automatiserat översättningsjobb fyra steg vid merge till main: regenerera mallen, upptäcka vad som är nytt, översätta endast dessa strängar och committa resultaten tillbaka. Hela processen ska vara idempotent – att köra den vid en merge utan strängändringar måste producera noll skillnad och noll brus.
Extrahera och Skilja
Steg ett är extraktion. Regenerera .pot-filen från nuvarande källkod så att den återspeglar varje sträng i kodbasen:
wp i18n make-pot . languages/myplugin.pot
Steg två är diffen. Detta är det viktigaste designbeslutet i hela pipelinen. Du vill inte översätta om varje sträng vid varje merge – det slösar API-anrop, riskerar att översätta om strängar som en människa redan korrigerat, och producerar enorma gransknings-diffar. Istället mergar du den färska .pot-filen in i varje befintlig .po-fil och översätter endast de poster som nu är tomma (de genuint nya strängarna):
# 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 endast helt nya strängar en tom msgstr. Allt som tidigare översatts är orört. Denna egenskap är det som gör pipelinen idempotent och som håller dina gransknings-diffar små och granskningsbara.
Översätta och Committa
Steg tre skickar endast de tomma posterna till ett översättningssteg. Steg fyra committar den uppdaterade .po-filen, kompilerar .mo-filen och regenererar eventuella JSON-filer. Vi kommer att koppla in allt detta i GitHub Actions härnäst.
Ett GitHub Actions-skelett
Här är svaret på "hur ser detta egentligen ut i en arbetsflödesfil": ett jobb som triggas vid push till main och som kör extrahera-skilja-översätta-committa-cykeln och öppnar en pull request med resultaten istället för att committa direkt till main.
Arbetsflödesfilen
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"
Där det verkliga arbetet döljer sig
Steget translate-new-strings.sh är där den faktiska översättningen sker. Den naiva versionen av det skriptet läser varje tom post, skickar den till en LLM API-tjänst en sträng i taget och klistrar in svaret tillbaka. Den naiva versionen är precis fällan, och det är värt att vara tydlig med varför.
Varför handgjorda LLM-anrop förstör dina filer
Det korta svaret: råa LLM-anrop behandlar dina .po-strängar som prosa, och dina .po-strängar är inte prosa – de är strukturerad data med platshållare, pluralformer och kontext som ett chatt-kompletteringsanrop glatt förstör.
Korruption av platshållare som du inte kommer att se i tester
Skicka Deleted %d of %s files till en allmän chatt-endpoint och du kan få tillbaka Supprimé %d des %s fichiers (bra) eller Supprimé %d de % s fichiers (ett mellanslag smög sig in i platshållaren, och nu kastar sprintf() ett fel vid körning). Skicka en pluralpost och modellen kan slå ihop två former till en, vilket bryter språk med fler än två pluralkategorier. Skicka en sträng med <a href="%s"> och modellen kan översätta URL:en eller släppa taggen. Inget av dessa fel visar sig i dina tester om du inte specifikt testar renderad utdata i varje lokalisering – de visar sig som runtime-fel i produktion för användare som du inte kan läsa buggrapporter från.
Underhållsfällan
Du kan försöka försvara dig mot detta med prompt engineering och regex-efterbearbetning, och många team gör det. Problemet är att du nu underhåller en bräcklig översättningsmotor som ett sidoprojekt, och återupptäcker varje platshållar-edge-case som Gettext-ekosystemet redan har löst. Vi katalogiserar de specifika sätten variabler blir förvrängda – och hur man förhindrar dem – i översätta PO-filer utan att bryta kodvariabler. Lärdomen där är direkt tillämplig: modellkvaliteten är sällan problemet; det är den strukturella hanteringen runt den.
Anpassa en API-driven molnöversättare till CI
Det är här en API-driven översättningstjänst ersätter dina gissningar med translate-new-strings.sh. Istället för att handskriva LLM-anrop och efterbearbeta med regex, laddar ditt CI-steg upp den ändrade .po-filen till en tjänst som redan förstår Gettext-strukturen och returnerar ren utdata. Pipeline-formen förblir identisk – extrahera, skilja, översätta, committa – men det bräckliga mellanstadiet blir ett enda API-anrop.
API-anropet som ersätter ditt skript
SimplePoTranslate är byggt för just detta. Det exponerar en moln-API anpassat för automatisering och CI, så ditt arbetsflödes översättningssteg blir en förfrågan som lämnar över .po-filen och får tillbaka en översatt, utan per-sträng-loopning. Dess Syntax Locking håller %s, %1$s, {count}, HTML och kod-tokens på plats automatiskt – hela klassen av buggar från föregående avsnitt hanteras av motorn snarare än av regex du underhåller. Med fullt stöd för Gettext plural och msgctxt överlever pluralformer och kontext tur och retur, vilket ett chatt-kompletteringsanrop inte kan garantera.
Idempotens och granskningsgrinden förblir dina
Två designbeslut tillhör fortfarande dig oavsett vilken motor du använder. För det första, idempotens: fortsätt att endast översätta de tomma posterna efter msgmerge, så att no-op merges inte producerar någon skillnad. För det andra, en granskningsgrind: låt jobbet öppna en pull request, som skelettet ovan gör, istället för att committa direkt till main. Maskinöversättning är utmärkt för att snabbt få strängar live, men en mänsklig granskning före merge fångar sällsynta kontextfel – och en PR ger dig den granskningen utan att blockera det vanliga fallet. Team som hanterar många språkversioner eller många klientwebbplatser kommer att känna igen denna form från det ideala lokaliseringsarbetsflödet för byråer, där samma automatisera-sedan-granska-mönster skalas över dussintals projekt.
Slutsats
För att automatisera översättning i CI/CD utan att leverera trasiga byggen, är mönstret konsekvent: regenerera .pot-filen vid merge, msgmerge den in i varje lokalisering för att endast visa nya strängar, översätt just dessa strängar, och committa resultatet bakom en pull-request granskningsgrind. Idempotens håller dina diffar rena; granskningsgrinden håller den sällsynta dåliga översättningen borta från produktion.
Handgjorda LLM-anrop kommer att korrumpera platshållare och pluralformer på sätt som dina tester inte kommer att upptäcka, och du kommer att lägga mer tid på att underhålla översättningslimmet än den funktionskod det tjänar. En API-driven motor med Syntax Locking tar bort hela det feltillståndet, så din pipeline översätter nya strängar vid samma merge som introducerade dem – och dina icke-engelska användare slutar se engelska platshållare.
Redo att automatisera översättning i din pipeline utan att förstöra platshållare? Prova SimplePoTranslate gratis – inget kreditkort krävs. Börja på gratisnivån, validera API:et mot dina egna
.po-filer och koppla in det i CI när du är redo.