Django
Localhero.ai översätter Djangos gettext-filer i .po-format vid varje pull request. Nya och ändrade strängar översätts och committas till samma PR. Då får du översättningar som följer ditt varumärke, redo att driftsättas tillsammans med resten av ändringarna.
Förutsättningar
- Ett Django-projekt med meddelandekataloger för gettext i
locale/eller en motsvarande mapp - Node 18 eller senare för att köra CLI:t via
npx - En API-nyckel för Localhero.ai
Hämta en API-nyckel från ditt konto.
Konfiguration
1. Sätt upp projektet
Kör CLI:t i projektmappen. Du behöver inte installera något. CLI:t känner igen Django utifrån din manage.py:
npx @localheroai/cli init
När CLI:t känner igen ett Django-projekt kommer det föreslå mönstret **/*.{po,pot}. Det kommer också leta efter mappen med meddelandekatalogerna bland locale/, locales/ och translations/ och välja den första som finns. Har projektet ingen av dem ännu föreslås translations/. En struktur där varje app har en egen <app>/locale/-mapp upptäcks inte automatiskt. Ange därför sökvägarna via --path när du kör init, eller redigera translationFiles.paths i efterhand.
Mönstret innehåller .pot eftersom den filen är din källkatalog, som beskrivs under "Källkatalogen" längre ner. Räkna med att init erbjuder sig att skapa den om du aldrig har sparat någon.
2. Kontrollera den genererade konfigurationen
init kommer skriva en localhero.json i projektmappen. För ett Django-projekt med meddelandekataloger i locale/ kommer konfigurationen se ut så här:
localhero.json
{
"schemaVersion": "1.0",
"projectId": "your-project-id",
"sourceLocale": "en",
"outputLocales": ["sv", "de", "es"],
"translationFiles": {
"paths": ["locale/"],
"pattern": "**/*.{po,pot}",
"workflow": "django"
}
}
Committa den här filen. Fälten är dokumenterade i Projektkonfiguration.
3. Lägg till ett GitHub Actions-workflow
init kommer fråga om det ska skapa GitHub action workflowet åt dig. Workflowet kör localheroai/localhero-action. Lägg till API-nyckeln på GitHub som en repository secret med namnet LOCALHERO_API_KEY.
Det genererade workflowet innehåller ett makemessages-steg före Localhero.ai-steget. Utan det körs jobbet utan fel men översätter ingenting, eftersom bara strängar som redan finns i dina meddelandekataloger är synliga. Det ser ut så här:
Justera filtret paths: om dina .po-filer ligger någon annanstans än i konfigurationen ovan:
.github/workflows/localhero-translate.yml
name: Localhero.ai - Automatic I18n translation
on:
pull_request:
paths:
- "locale/**"
- "**/*.py"
- "**/*.html"
- "**/*.txt"
- "localhero.json"
repository_dispatch:
types: [localhero-sync]
workflow_dispatch:
concurrency:
group: translate-${{ github.event.client_payload.branch || github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
translate:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v7
with:
ref: ${{ github.event.client_payload.branch || github.head_ref || github.ref_name }}
fetch-depth: 0
- uses: actions/setup-python@v5
if: github.event_name == 'pull_request'
with:
python-version: "3.12"
- name: Extract messages
if: github.event_name == 'pull_request'
run: |
sudo apt-get install -y -qq gettext
pip install -r requirements.txt
python manage.py makemessages --keep-pot -l sv -l de -l es
# Valfritt: makemessages skriver om headern med skapandedatum vid varje körning,
# så utan den här raden visar varje CI-körning en rads diff i varje katalog.
git ls-files --modified --others --exclude-standard -z -- '*.po' '*.pot' | xargs -0 -r sed -i '/^"POT-Creation-Date: /d' --
- name: Translate
uses: localheroai/localhero-action@v1
with:
api-key: ${{ secrets.LOCALHERO_API_KEY }}
fetch-depth: 0 behövs eftersom actionen diffar mot bas-branchen. Vid en shallow checkout finns ingen historik att jämföra med. Alla inputs beskrivs i guiden för GitHub Actions.
Extraheringssteget gör att nya strängar översätts även när en PR bara ändrar templates eller Python-kod. Dina målspråk anges explicit i stället för --all, som bara plockar upp språk som redan har en katalog och alltså skulle skriva tomt i ett nystartat projekt.
--keep-pot är det som håller din källkatalog aktuell: .pot-filen byggs om från koden vid varje PR, så dina källsträngar kan aldrig hamna i otakt. Mer om den filen nedan om du inte har sparat någon tidigare.
sed-raden är valfri. makemessages skriver om headern POT-Creation-Date vid varje körning, så utan den visar varje CI-körning en rads diff i varje katalog trots att ingen sträng har ändrats. Ta bort raden om du hellre behåller headern.
Två saker att kontrollera: steget installerar beroenden med pip install -r requirements.txt och Python 3.12, så justera båda om du använder något annat. init läser din lockfil, så ett projekt med uv.lock, poetry.lock eller Pipfile.lock får det verktyget uppsatt och använt i stället. Extraheringen hoppas över vid synkkörningar, när Localhero.ai skriver tillbaka granskade översättningar. Vill du hellre extrahera lokalt före varje PR? Ta då bort steget och de poster under paths: som gäller källfiler.
Så fungerar det
- Du öppnar en pull request som innehåller ändringar i dina templates, din Python-kod eller dina meddelandekataloger.
- Workflowet kör
makemessagesoch extraherar nya strängar till meddelandekatalogerna. Därefter jämför GitHub-actionen branchen med bas-branchen och översätter bara meddelanden som har ändrats eller fortfarande saknar översättning. Uppdaterade och borttagna meddelanden synkas till Localhero backend. - Actionen committar de uppdaterade
.po-filerna till samma PR. Diffen du granskar innehåller då både kod och översättningar. - Du granskar och redigerar översättningarna i dashboarden, och ändringarna synkas tillbaka till branchen.
- De översatta
.po-filerna måste fortfarande kompileras medpython manage.py compilemessagestill.mo-filer som Django läser in. De flesta projekt kör redan kommandot när appen byggs eller driftsätts. Om ditt projekt inte gör det, lägg till kommandot i driftsättningssteget eller efter översättningssteget i det här workflowet.
Appen behöver inte känna till Localhero.ai. Integrationen sker helt via .po-filerna, så dina gettext-anrop och template-taggar kan vara oförändrade.
Vill du inte översätta automatiskt i pull requests? Kör makemessages --keep-pot och sedan npx @localheroai/cli translate lokalt för att fylla i alla meddelanden som saknar översättning. Committa sedan resultatet själv. Se CLI-referensen.
En längre genomgång med en riktig kodbas: att lokalisera en Django-app. För en fristående genomgång av CI-delen, se att översätta .po-filer i CI.
Specifikt för Django och .po
Källkatalogen
Localhero.ai behöver veta vad dina källsträngar är just nu. Allt som redan fyller den funktionen fungerar:
- En committad
locale/en/LC_MESSAGES/django.po, som många projekt har för att någon kördemakemessages -l eneller för att projektet extraherar alla språk isettings.LANGUAGES. Inget att göra. - En committad
.pot, om du redan sparar en. Inget att göra där heller.
Har du ingendera, läs vidare. Annars hoppa till nästa avsnitt.
Ett vanligt Django-projekt har ingen källkatalog, eftersom engelskan finns i dina gettext()-anrop och {% translate %}-taggar i stället för i en fil. makemessages samlar dem i en .pot, sammanfogar den med varje målkatalog och tar sedan bort den igen.
Två saker gör att den blir kvar, och init sätter upp båda:
- Behåll den. Skicka med
--keep-pottillmakemessages. Det genererade workflowet gör det redan. - Committa den. Behandla
locale/django.potsom vilken katalogfil som helst och lägg den inte i.gitignore. Actionen jämför med bas-branchen, och en fil som saknas där ser ut som att varje sträng är ny.
Kör du makemessages utan flaggan lokalt efter det tar Django bort mallen igen, och när du committar den borttagningen översätter nästa CI-körning allt från början. Ett mål i din Makefile brukar lösa det.
När init inte hittar någon källkatalog erbjuder den sig att köra extraheringen åt dig. Standardsvaret är nej och den kör ingenting annat än det kommandot, och svarar du nej skrivs kommandot ut så att du kan köra det själv.
Båda filerna kan finnas samtidigt. .pot-filen har företräde, och skillnaden är att den byggs om från koden vid varje körning medan en handhållen engelsk katalog bara uppdateras när du kommer ihåg att be om det.
Mappstruktur för språkfiler
Djangos vanliga mappstruktur för gettext, <locale>/LC_MESSAGES/<domain>.po, fungerar utan extra konfiguration. .po-filens namn anger domänen, inte språket. Mappen ovanför LC_MESSAGES tolkas därför som språkkod.
CLI:t godtar även språkkoder som inte följer standardformatet: sv_FI_custom/LC_MESSAGES/django.po tolkas som sv_FI_custom, utan att du behöver ange localeRegex.
Utesluta meddelanden
Uteslutningar görs per fil, inte per meddelande. Använd translationFiles.ignore med ett glob-mönster för att lämna en hel .po-fil orörd.
Om du vill utesluta en viss uppsättning meddelanden lägger du dem i en egen domän eller mapp och utesluter den sökvägen.
Byta namn på och ta bort meddelanden
I flödet för pull requests sköts det automatiskt: meddelanden som tas bort i diffen rapporteras till Localhero backend tillsammans med övriga ändringar. Det manuella kommandot push är försiktigare och tar aldrig bort något. Om du har ändrat ordalydelsen i eller tagit bort meddelanden utanför en pull request kör du push --prune --force, så att Localhero backend stämmer överens med dina filer.
Det här är vanligare i Django än i format med separata nycklar, eftersom msgid är själva källsträngen. När du redigerar den engelska texten skapas därför ett nytt meddelande i stället för att det befintliga uppdateras.
Pluralformer
Varje målspråks egen Plural-Forms-header avgör hur många msgstr[N]-platser det behöver, inte en kopia av källspråkets antal. Polska får fyra platser (msgstr[0]-msgstr[3]) även om ditt engelska källspråk bara skiljer på singular och plural.
Vanliga frågor
Kan jag översätta vissa språk själv?
Ja. Stäng av automatisk översättning för de språk du vill hantera själv. Då skriver Localhero.ai inga översättningar för dem. CLI:t visar vilka språk som har hoppats över, och du hanterar dem i ditt eget flöde medan övriga språk översätts automatiskt.
Översätts hela meddelandekatalogen vid varje pull request?
Nej, bara meddelanden som har ändrats eller saknar översättning. För pull requests jämförs diffen med bas-branchen. Lokalt jämför --changed-only med main, om du inte har angett en annan branch i translationFiles.baseBranch.
Hur hindrar jag actionen från att köras för en viss PR?
Lägg till labeln skip-translation. Actionen hoppar också över draft-PR:er och commits som den själv har skapat. Läs mer i guiden för GitHub Actions.
Last updated
Redo att testa?
Kom igång på några minuter. Inget kreditkort behövs.