Översättning¶
Översikt¶
För att göra ett Django-projekt översättningsbart måste du lägga till ett minimalt antal krokar i din Python-kod och mallar. Dessa krokar kallas översättningssträngar. De berättar för Django: ”Den här texten ska översättas till slutanvändarens språk, om en översättning för den här texten finns tillgänglig på det språket.” Det är ditt ansvar att markera översättningsbara strängar; systemet kan bara översätta strängar som det känner till.
Django tillhandahåller sedan verktyg för att extrahera översättningssträngarna till en message file. Denna fil är ett bekvämt sätt för översättare att tillhandahålla motsvarigheten till översättningssträngarna på målspråket. När översättarna har fyllt i meddelandefilen måste den kompileras. Denna process är beroende av GNU gettext-verktygsuppsättningen.
När detta är gjort tar Django hand om att översätta webbappar i farten på varje tillgängligt språk, enligt användarnas språkpreferenser.
Djangos internationaliseringskrokar är på som standard, och det betyder att det finns lite i18n-relaterad overhead på vissa ställen i ramverket. Om du inte använder internationalisering bör du ta två sekunder på dig att ställa in USE_I18N = False
i din inställningsfil. Då kommer Django att göra vissa optimeringar så att internationaliseringsmaskineriet inte behöver laddas.
Observera
Se till att du har aktiverat översättning för ditt projekt (det snabbaste sättet är att kontrollera om MIDDLEWARE
inkluderar django.middleware.locale.LocaleMiddleware
). Om du inte har gjort det ännu, se hur-django-discovers-language-preference.
Internationalisering: i Python-kod¶
Standardöversättning¶
Ange en översättningssträng genom att använda funktionen gettext()
. Det är konvention att importera detta som ett kortare alias, _
, för att spara skrivning.
Observera
Pythons standardbiblioteksmodul gettext
installerar _()
i det globala namnrymden, som ett alias för gettext()
. I Django har vi valt att inte följa denna praxis, av ett par skäl:
Ibland bör du använda
gettext_lazy()
som standardöversättningsmetod för en viss fil. Utan_()
i det globala namnområdet måste utvecklaren tänka på vilken som är den mest lämpliga översättningsfunktionen.Understreckstecknet (
_
) används för att representera ”det föregående resultatet” i Pythons interaktiva skal och doctest-tester. Att installera en global_()
-funktion orsakar störningar. Explicit import avgettext()
som_()
undviker detta problem.
Vilka funktioner kan ha aliaset _
?
På grund av hur xgettext
(används av makemessages
) fungerar, kan endast funktioner som tar ett enda strängargument importeras som _
:
I det här exemplet markeras texten "Välkommen till min webbplats."
som en översättningssträng:
from django.http import HttpResponse
from django.utils.translation import gettext as _
def my_view(request):
output = _("Welcome to my site.")
return HttpResponse(output)
Du kan koda detta utan att använda aliaset. Detta exempel är identiskt med det föregående:
from django.http import HttpResponse
from django.utils.translation import gettext
def my_view(request):
output = gettext("Welcome to my site.")
return HttpResponse(output)
Översättning fungerar på beräknade värden. Detta exempel är identiskt med de två föregående:
def my_view(request):
words = ["Welcome", "to", "my", "site."]
output = _(" ".join(words))
return HttpResponse(output)
Översättning fungerar på variabler. Återigen, här är ett identiskt exempel:
def my_view(request):
sentence = "Welcome to my site."
output = _(sentence)
return HttpResponse(output)
(Varningen med att använda variabler eller beräknade värden, som i de två föregående exemplen, är att Djangos verktyg för att upptäcka översättningssträngar, django-admin makemessages
, inte kommer att kunna hitta dessa strängar. Mer om makemessages
senare)
Strängarna som du skickar till _()
eller gettext()
kan innehålla platshållare som anges med Pythons standardiserade syntax för interpolering av namngivna strängar. Exempel:
def my_view(request, m, d):
output = _("Today is %(month)s %(day)s.") % {"month": m, "day": d}
return HttpResponse(output)
Med den här tekniken kan språkspecifika översättningar ändra ordningen på platshållartexten. Till exempel kan en engelsk översättning vara ”Today is November 26.”, medan en spansk översättning kan vara ”Hoy es 26 de noviembre.” - med platshållarna för månad och dag ombytta.
Av denna anledning bör du använda namngiven stränginterpolering (t.ex. %(day)s
) i stället för positionsinterpolering (t.ex. %s
eller %d
) när du har mer än en enda parameter. Om du använde positionell interpolering skulle översättningarna inte kunna ändra ordningen på platshållartexten.
Eftersom strängutdragning görs av kommandot xgettext
, stöds endast syntaxer som stöds av gettext
av Django. Python f-strings kan inte användas direkt med gettext
-funktioner eftersom f-stringsuttryck utvärderas innan de når gettext
. Detta innebär att _(f"Welcome {name}")
inte kommer att fungera som förväntat, eftersom variabeln ersätts innan översättning sker. Använd istället named-string interpolation:
# Good
_("Welcome %(name)s") % {"name": name}
# Good
_("Welcome {name}").format(name=name)
# Bad
_(f"Welcome {name}") # f-string evaluated before translation.
JavaScript-mallsträngar kräver gettext
0.21+.
Kommentarer för översättare¶
Om du vill ge översättarna tips om en översättningsbar sträng kan du lägga till en kommentar med nyckelordet Translators
som prefix på raden före strängen, t.ex.:
def my_view(request):
# Translators: This message appears on the home page only
output = gettext("Welcome to my site.")
Kommentaren kommer sedan att visas i den resulterande filen .po
som är associerad med den översättningsbara konstruktionen som finns under den och bör också visas av de flesta översättningsverktyg.
Observera
Bara för fullständighetens skull är detta motsvarande fragment av den resulterande .po
-filen:
#. Translators: This message appears on the home page only
# path/to/python/file.py:123
msgid "Welcome to my site."
msgstr ""
Detta fungerar även i mallar. Se Kommentarer för översättare i mallar för mer information.
Markera strängar som no-op¶
Använd funktionen django.utils.translation.gettext_noop()
för att markera en sträng som en översättningssträng utan att översätta den. Strängen översätts senare från en variabel.
Använd detta om du har konstanta strängar som bör lagras på källspråket eftersom de utbyts mellan system eller användare - t.ex. strängar i en databas - men som bör översättas vid sista möjliga tidpunkt, t.ex. när strängen presenteras för användaren.
Pluralisering¶
Använd funktionen django.utils.translation.ngettext()
för att ange pluraliserade meddelanden.
ngettext()
tar tre argument: översättningssträngen i singular, översättningssträngen i plural och antalet objekt.
Den här funktionen är användbar när du vill att din Django-applikation ska kunna lokaliseras till språk där antalet och komplexiteten i pluralformerna <https://www.gnu.org/software/gettext/manual/gettext.html#Plural-forms>`_ är större än de två former som används på engelska (”object” för singular och ”objects” för alla fall där ”count” skiljer sig från ett, oavsett dess värde)
Till exempel:
from django.http import HttpResponse
from django.utils.translation import ngettext
def hello_world(request, count):
page = ngettext(
"there is %(count)d object",
"there are %(count)d objects",
count,
) % {
"count": count,
}
return HttpResponse(page)
I det här exemplet skickas antalet objekt till översättningsspråken som variabeln ”count”.
Observera att pluralisering är komplicerat och fungerar olika på olika språk. Att jämföra count
med 1 är inte alltid den korrekta regeln. Den här koden ser sofistikerad ut, men kommer att ge felaktiga resultat för vissa språk:
from django.utils.translation import ngettext
from myapp.models import Report
count = Report.objects.count()
if count == 1:
name = Report._meta.verbose_name
else:
name = Report._meta.verbose_name_plural
text = ngettext(
"There is %(count)d %(name)s available.",
"There are %(count)d %(name)s available.",
count,
) % {"count": count, "name": name}
Försök inte att implementera din egen singular-eller-plural-logik; den kommer inte att vara korrekt. I ett fall som detta kan man tänka sig något i stil med följande:
text = ngettext(
"There is %(count)d %(name)s object available.",
"There are %(count)d %(name)s objects available.",
count,
) % {
"count": count,
"name": Report._meta.verbose_name,
}
Observera
När du använder ngettext()
, se till att du använder ett enda namn för varje extrapolerad variabel som ingår i den bokstavliga texten. I exemplen ovan, notera hur vi använde Python-variabeln name
i båda översättningssträngarna. Detta exempel skulle, förutom att det är felaktigt på vissa språk enligt ovan, misslyckas:
text = ngettext(
"There is %(count)d %(name)s available.",
"There are %(count)d %(plural_name)s available.",
count,
) % {
"count": Report.objects.count(),
"name": Report._meta.verbose_name,
"plural_name": Report._meta.verbose_name_plural,
}
Du skulle få ett fel när du kör django-admin compilemessages
:
a format specification for argument 'name', as in 'msgstr[0]', doesn't exist in 'msgid'
Kontextuella markörer¶
Ibland har ord flera betydelser, som till exempel "May"
på engelska, som hänvisar till ett månadsnamn och till ett verb. För att översättare ska kunna översätta dessa ord korrekt i olika sammanhang kan du använda funktionen django.utils.translation.pgettext()
, eller funktionen django.utils.translation.npgettext()
om strängen behöver pluraliseras. Båda tar en kontextsträng som den första variabeln.
I den resulterande .po
-filen kommer strängen sedan att visas så ofta som det finns olika kontextuella markörer för samma sträng (kontexten visas på msgctxt
-raden), vilket gör det möjligt för översättaren att ge en annan översättning för var och en av dem.
Till exempel:
from django.utils.translation import pgettext
month = pgettext("month name", "May")
eller:
from django.db import models
from django.utils.translation import pgettext_lazy
class MyThing(models.Model):
name = models.CharField(
help_text=pgettext_lazy("help text for MyThing model", "This is the help text")
)
kommer att visas i filen .po
som:
msgctxt "month name"
msgid "May"
msgstr ""
Kontextuella markörer stöds också av malltaggarna translate
och blocktranslate
.
Lat översättning¶
Använd de lata versionerna av översättningsfunktioner i django.utils.translation
(lätt igenkännliga genom suffixet lazy
i deras namn) för att översätta strängar lata – när värdet nås snarare än när de anropas.
Dessa funktioner lagrar en latent referens till strängen - inte den faktiska översättningen. Själva översättningen görs när strängen används i en strängkontext, t.ex. i mallrendering.
Detta är viktigt när anrop till dessa funktioner finns i kodvägar som exekveras vid modulens laddningstid.
Detta är något som lätt kan hända när man definierar modeller, formulär och modellformulär, eftersom Django implementerar dessa så att deras fält faktiskt är attribut på klassnivå. Se därför till att använda lazy translations i följande fall:
Modellfält och relationer verbose_name
och help_text
alternativvärden¶
Om du t.ex. vill översätta hjälptexten för fältet name i följande modell gör du så här:
from django.db import models
from django.utils.translation import gettext_lazy as _
class MyThing(models.Model):
name = models.CharField(help_text=_("This is the help text"))
Du kan markera namn på ForeignKey
, ManyToManyField
eller OneToOneField
relationer som översättningsbara genom att använda deras verbose_name
alternativ:
class MyThing(models.Model):
kind = models.ForeignKey(
ThingKind,
on_delete=models.CASCADE,
related_name="kinds",
verbose_name=_("kind"),
)
Precis som du skulle göra i verbose_name
bör du ange en text med små bokstäver för verbose-namnet för relationen eftersom Django automatiskt kommer att skriva det med små bokstäver när det krävs.
Modell utförliga namn värden¶
Det rekommenderas att alltid tillhandahålla explicita verbose_name
och verbose_name_plural
alternativ snarare än att förlita sig på den engelska-centrerade och något naiva bestämningen av verbose-namn som Django utför genom att titta på modellens klassnamn:
from django.db import models
from django.utils.translation import gettext_lazy as _
class MyThing(models.Model):
name = models.CharField(_("name"), help_text=_("This is the help text"))
class Meta:
verbose_name = _("my thing")
verbose_name_plural = _("my things")
Modellmetoder description
argument till @display
dekoratorn¶
För modellmetoder kan du tillhandahålla översättningar till Django och admin-webbplatsen med argumentet description
till display()
decorator:
from django.contrib import admin
from django.db import models
from django.utils.translation import gettext_lazy as _
class MyThing(models.Model):
kind = models.ForeignKey(
ThingKind,
on_delete=models.CASCADE,
related_name="kinds",
verbose_name=_("kind"),
)
@admin.display(description=_("Is it a mouse?"))
def is_mouse(self):
return self.kind.type == MOUSE_TYPE
Arbeta med objekt för latent översättning¶
Resultatet av ett gettext_lazy()
-anrop kan användas där du skulle använda en sträng (ett str
-objekt) i annan Django-kod, men det kanske inte fungerar med godtycklig Python-kod. Till exempel kommer följande inte att fungera eftersom biblioteket requests inte hanterar gettext_lazy
-objekt:
body = gettext_lazy("I \u2764 Django") # (Unicode :heart:)
requests.post("https://example.com/send", data={"body": body})
Du kan undvika sådana problem genom att kasta gettext_lazy()
-objekt till textsträngar innan du skickar dem till icke-Django-kod:
requests.post("https://example.com/send", data={"body": str(body)})
Om du inte gillar det långa namnet gettext_lazy
kan du använda aliaset _
(understreck), så här:
from django.db import models
from django.utils.translation import gettext_lazy as _
class MyThing(models.Model):
name = models.CharField(help_text=_("This is the help text"))
Att använda gettext_lazy()
och ngettext_lazy()
för att markera strängar i modeller och verktygsfunktioner är en vanlig operation. När du arbetar med dessa objekt på andra ställen i din kod bör du se till att du inte av misstag konverterar dem till strängar, eftersom de bör konverteras så sent som möjligt (så att rätt lokal används). Detta gör det nödvändigt att använda den hjälpfunktion som beskrivs härnäst.
Lata översättningar och plural¶
När du använder latent översättning för en pluralsträng (n[p]gettext_lazy
) känner du i allmänhet inte till argumentet number
vid tidpunkten för strängdefinitionen. Därför är du behörig att skicka ett nyckelnamn istället för ett heltal som argumentet number
. Då kommer number
att slås upp i ordlistan under den nyckeln vid stränginterpolering. Här är ett exempel:
from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import ngettext_lazy
class MyForm(forms.Form):
error_message = ngettext_lazy(
"You only provided %(num)d argument",
"You only provided %(num)d arguments",
"num",
)
def clean(self):
# ...
if error:
raise ValidationError(self.error_message % {"num": number})
Om strängen innehåller exakt en icke namngiven platshållare kan du interpolerar direkt med argumentet number
:
class MyForm(forms.Form):
error_message = ngettext_lazy(
"You provided %d argument",
"You provided %d arguments",
)
def clean(self):
# ...
if error:
raise ValidationError(self.error_message % number)
Formatering av strängar: format_lazy()
¶
Pythons str.format()
-metod fungerar inte när antingen format_string
eller något av argumenten till str.format()
innehåller lata översättningsobjekt. Istället kan du använda django.utils.text.format_lazy()
, som skapar ett latent objekt som kör metoden str.format()
endast när resultatet ingår i en sträng. Till exempel:
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy
...
name = gettext_lazy("John Lennon")
instrument = gettext_lazy("guitar")
result = format_lazy("{name}: {instrument}", name=name, instrument=instrument)
I det här fallet kommer de lata översättningarna i result
endast att konverteras till strängar när result
själv används i en sträng (vanligtvis vid mallens renderingstid).
Andra användningar av lazy i försenade översättningar¶
För alla andra fall där du vill fördröja översättningen, men måste skicka den översättningsbara strängen som argument till en annan funktion, kan du själv packa in den här funktionen i ett lazy call. Till exempel:
from django.utils.functional import lazy
from django.utils.translation import gettext_lazy as _
def to_lower(string):
return string.lower()
to_lower_lazy = lazy(to_lower, str)
Och sedan senare:
lazy_string = to_lower_lazy(_("My STRING!"))
Lokaliserade namn på språk¶
Funktionen get_language_info()
ger detaljerad information om språk:
>>> from django.utils.translation import activate, get_language_info
>>> activate("fr")
>>> li = get_language_info("de")
>>> print(li["name"], li["name_local"], li["name_translated"], li["bidi"])
German Deutsch Allemand False
Attributen name
, name_local
och name_translated
i ordlistan innehåller namnet på språket på engelska, på språket självt respektive på det aktuella aktiva språket. Attributet bidi
är True endast för dubbelriktade språk.
Källan till språkinformationen är modulen django.conf.locale
. Liknande tillgång till denna information är tillgänglig för mallkod. Se nedan.
Internationalisering: i mallkod¶
Översättningar i Django templates använder två malltaggar och en något annorlunda syntax än i Python-kod. För att ge din mall tillgång till dessa taggar, lägg {% load i18n %}
längst upp i din mall. Som med alla malltaggar måste denna tagg laddas i alla mallar som använder översättningar, även de mallar som bygger på andra mallar som redan har laddat i18n
-taggen.
Varning
Översatta strängar kommer inte att escapas när de återges i en mall. Detta gör att du kan inkludera HTML i översättningar, t.ex. för att betona, men potentiellt farliga tecken (t.ex. "
) kommer också att återges oförändrade.
translate
mall tagg¶
Malltaggen {% translate %}
översätter antingen en konstant sträng (omgiven av enkla eller dubbla citattecken) eller variabelt innehåll:
<title>{% translate "This is the title." %}</title>
<title>{% translate myvar %}</title>
Om alternativet noop
finns kvar sker fortfarande variabeluppslagning, men översättningen hoppas över. Detta är användbart när man ”stubbar ut” innehåll som kommer att behöva översättas i framtiden:
<title>{% translate "myvar" noop %}</title>
Internt använder inline-översättningar ett gettext()
-anrop.
Om en mallvariabel (myvar
ovan) skickas till taggen kommer taggen först att omvandla variabeln till en sträng under körningen och sedan leta upp strängen i meddelandekatalogen.
Det är inte möjligt att blanda en mallvariabel i en sträng inom {% translate %}
. Om dina översättningar kräver strängar med variabler (platshållare), använd {% blocktranslate %}
istället.
Om du vill hämta en översatt sträng utan att visa den kan du använda följande syntax:
{% translate "This is the title" as the_title %}
<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">
I praktiken använder du detta för att få en sträng som du kan använda på flera ställen i en mall eller så att du kan använda utdata som ett argument för andra malltaggar eller filter:
{% translate "starting point" as start %}
{% translate "end point" as end %}
{% translate "La Grande Boucle" as race %}
<h1>
<a href="/" title="{% blocktranslate %}Back to '{{ race }}' homepage{% endblocktranslate %}">{{ race }}</a>
</h1>
<p>
{% for stage in tour_stages %}
{% cycle start end %}: {{ stage }}{% if forloop.counter|divisibleby:2 %}<br>{% else %}, {% endif %}
{% endfor %}
</p>
{% translate %}
stöder även :ref:kontextuella markörer<contextual-markers>
med hjälp av nyckelordet context
:
{% translate "May" context "month name" %}
blocktranslate
mall tagg¶
I motsats till taggen translate
kan du med taggen blocktranslate
markera komplexa meningar som består av bokstavstecken och variabelt innehåll för översättning genom att använda platshållare:
{% blocktranslate %}This string will have {{ value }} inside.{% endblocktranslate %}
För att översätta ett malluttryck - t.ex. åtkomst till objektattribut eller användning av mallfilter - måste du binda uttrycket till en lokal variabel som kan användas i översättningsblocket. Exempel på detta:
{% blocktranslate with amount=article.price %}
That will cost $ {{ amount }}.
{% endblocktranslate %}
{% blocktranslate with myvar=value|filter %}
This will have {{ myvar }} inside.
{% endblocktranslate %}
Du kan använda flera uttryck i en enda blocktranslate
-tagg:
{% blocktranslate with book_t=book|title author_t=author|title %}
This is {{ book_t }} by {{ author_t }}
{% endblocktranslate %}
Observera
Det tidigare mer utförliga formatet stöds fortfarande: {% blocktranslate med book|title som book_t och author|title som author_t %}`
Andra blocktaggar (till exempel {% for %}
eller {% if %}
) är inte tillåtna inuti en blocktranslate
-tagg.
Om lösningen av ett av blockargumenten misslyckas kommer blocktranslate
att falla tillbaka till standardspråket genom att avaktivera det aktuella aktiva språket tillfälligt med funktionen deactivate_all()
.
Den här taggen möjliggör även pluralisering. För att använda den:
Utse och bind ett räknevärde med namnet
count
. Detta värde kommer att vara det som används för att välja rätt pluralform.Ange både singular- och pluralformerna och separera dem med taggen
{% plural %}
i taggarna{% blocktranslate %}
och{% endblocktranslate %}
.
Ett exempel:
{% blocktranslate count counter=list|length %}
There is only one {{ name }} object.
{% plural %}
There are {{ counter }} {{ name }} objects.
{% endblocktranslate %}
Ett mer komplext exempel:
{% blocktranslate with amount=article.price count years=i.length %}
That will cost $ {{ amount }} per year.
{% plural %}
That will cost $ {{ amount }} per {{ years }} years.
{% endblocktranslate %}
När du använder både pluraliseringsfunktionen och binder värden till lokala variabler utöver räknevärdet, kom ihåg att blocktranslate
-konstruktionen internt konverteras till ett ngettext
-anrop. Detta innebär att samma noteringar om ngettext-variabler gäller.
Omvända URL-uppslagningar kan inte utföras inom blocktranslate
och bör hämtas (och lagras) i förväg:
{% url 'path.to.view' arg arg2 as the_url %}
{% blocktranslate %}
This is a URL: {{ the_url }}
{% endblocktranslate %}
Om du vill hämta en översatt sträng utan att visa den kan du använda följande syntax:
{% blocktranslate asvar the_title %}The title is {{ title }}.{% endblocktranslate %}
<title>{{ the_title }}</title>
<meta name="description" content="{{ the_title }}">
I praktiken använder du detta för att få en sträng som du kan använda på flera ställen i en mall eller så att du kan använda utdata som ett argument för andra malltaggar eller filter.
{% blocktranslate %}
stöder även :ref:kontextuella markörer<contextual-markers>
med hjälp av nyckelordet context
:
{% blocktranslate with name=user.username context "greeting" %}Hi {{ name }}{% endblocktranslate %}
En annan funktion som stöds av {% blocktranslate %}
är alternativet trimmed
. Detta alternativ tar bort ny rad-tecken från början och slutet av innehållet i taggen {% blocktranslate %}
, ersätter alla blanksteg i början och slutet av en rad och sammanfogar alla rader till en med ett mellanslagstecken för att separera dem. Detta är mycket användbart för att indentera innehållet i en {% blocktranslate %}
-tagg utan att indentationstecknen hamnar i motsvarande post i filen .po
, vilket gör översättningsprocessen enklare.
Till exempel följande tagg {% blocktranslate %}
:
{% blocktranslate trimmed %}
First sentence.
Second paragraph.
{% endblocktranslate %}
kommer att resultera i posten "Första meningen. Second paragraph."
i filen .po
, jämfört med "\n First sentence.\n Second paragraph.\n"
, om alternativet trimmed
inte hade angetts.
Kommentarer för översättare i mallar¶
Precis som med Python-kod kan dessa anteckningar för översättare anges med hjälp av kommentarer, antingen med taggen comment
:
{% comment %}Translators: View verb{% endcomment %}
{% translate "View" %}
{% comment %}Translators: Short intro blurb{% endcomment %}
<p>{% blocktranslate %}A multiline translatable
literal.{% endblocktranslate %}</p>
eller med {#
… #}
:ref:``enradskommentarer konstrueras <template-comments>`:
{# Translators: Label of a button that triggers search #}
<button type="submit">{% translate "Go" %}</button>
{# Translators: This is a text of the base template #}
{% blocktranslate %}Ambiguous translatable block of text{% endblocktranslate %}
Observera
Bara för fullständighetens skull är detta motsvarande fragment av den resulterande .po
-filen:
#. Translators: View verb
# path/to/template/file.html:10
msgid "View"
msgstr ""
#. Translators: Short intro blurb
# path/to/template/file.html:13
msgid ""
"A multiline translatable"
"literal."
msgstr ""
# ...
#. Translators: Label of a button that triggers search
# path/to/template/file.html:100
msgid "Go"
msgstr ""
#. Translators: This is a text of the base template
# path/to/template/file.html:103
msgid "Ambiguous translatable block of text"
msgstr ""
Byte av språk i mallar¶
Om du vill välja ett språk i en mall kan du använda malltaggen language
:
{% load i18n %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<p>{% translate "Welcome to our page" %}</p>
{% language 'en' %}
{% get_current_language as LANGUAGE_CODE %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<p>{% translate "Welcome to our page" %}</p>
{% endlanguage %}
Medan den första förekomsten av ”Välkommen till vår sida” använder det aktuella språket, kommer den andra alltid att vara på engelska.
Internationalisering: i JavaScript-kod¶
Att lägga till översättningar i JavaScript innebär vissa problem:
JavaScript-kod har inte tillgång till en
gettext
-implementering.JavaScript-kod har inte tillgång till filerna
.po
eller.mo
, utan de måste levereras av servern.Översättningskatalogerna för JavaScript bör hållas så små som möjligt.
Django erbjuder en integrerad lösning för dessa problem: Översättningarna skickas till JavaScript, så att du kan anropa gettext
etc. från JavaScript.
Den huvudsakliga lösningen på dessa problem är följande vy JavaScriptCatalog
, som genererar ett JavaScript-kodbibliotek med funktioner som efterliknar gränssnittet gettext
, plus en matris med översättningssträngar.
Vyn JavaScriptCatalog
(JavaScript-katalog)¶
- class JavaScriptCatalog[source]¶
En vy som producerar ett JavaScript-kodbibliotek med funktioner som efterliknar gränssnittet
gettext
, plus en matris med översättningssträngar.Attribut
- domain¶
Översättningsdomän som innehåller strängar som ska läggas till i vyns utdata. Standardvärdet är
'djangojs
.
- packages¶
En lista med
applikationsnamn
bland installerade applikationer. Dessa appar bör innehålla enlocale
-katalog. Alla dessa kataloger plus alla kataloger som finns iLOCALE_PATHS
(som alltid ingår) slås samman till en katalog. Standardvärdet ärNone
, vilket innebär att alla tillgängliga översättningar från allaINSTALLED_APPS
tillhandahålls i JavaScript-utdata.
Exempel med standardvärden:
from django.views.i18n import JavaScriptCatalog urlpatterns = [ path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"), ]
Exempel med anpassade paket:
urlpatterns = [ path( "jsi18n/myapp/", JavaScriptCatalog.as_view(packages=["your.app.label"]), name="javascript-catalog", ), ]
Om din root URLconf använder
i18n_patterns()
, måsteJavaScriptCatalog
också omslutas avi18n_patterns()
för att katalogen ska genereras korrekt.Exempel med
i18n_patterns()
:from django.conf.urls.i18n import i18n_patterns urlpatterns = i18n_patterns( path("jsi18n/", JavaScriptCatalog.as_view(), name="javascript-catalog"), )
Översättningarnas prioritet är sådan att de paket som förekommer senare i argumentet packages
har högre prioritet än de som förekommer i början. Detta är viktigt när det gäller krockande översättningar för samma bokstav.
Om du använder mer än en JavaScriptCatalog
-vy på en webbplats och några av dem definierar samma strängar, har strängarna i den katalog som laddades senast företräde.
Använda JavaScript-översättningskatalogen¶
För att använda katalogen drar du in det dynamiskt genererade skriptet så här:
<script src="{% url 'javascript-catalog' %}"></script>
Detta använder omvänd URL-uppslagning för att hitta URL:en för JavaScript-katalogvyn. När katalogen är laddad kan din JavaScript-kod använda följande metoder:
gettext
ngettext
interpolera
get_format
gettext_noop
pgettext
npgettext
pluralidx
gettext
¶
Funktionen gettext
beter sig på samma sätt som standardgränssnittet gettext
i din Python-kod:
document.write(gettext("this is to be translated"))
ngettext
¶
Funktionen ngettext
tillhandahåller ett gränssnitt för att pluralisera ord och fraser:
const objectCount = 1 // or 0, or 2, or 3, ...
const string = ngettext(
'literal for the singular case',
'literal for the plural case',
objectCount
);
interpolera
¶
Funktionen interpolate
har stöd för att dynamiskt fylla i en formatsträng. Interpolationssyntaxen är lånad från Python, så funktionen interpolate
stöder både positions- och namninterpolation:
Positionell interpolering:
obj
innehåller ett JavaScript Array-objekt vars elementvärden sedan interpoleras sekventiellt i motsvarandefmt
-platshållare i samma ordning som de visas. Till exempel:const formats = ngettext( 'There is %s object. Remaining: %s', 'There are %s objects. Remaining: %s', 11 ); const string = interpolate(formats, [11, 20]); // string is 'There are 11 objects. Remaining: 20'
Namngiven interpolation: Detta läge väljs genom att skicka den valfria booleska parametern
named
somtrue
.obj
innehåller ett JavaScript-objekt eller en associativ matris. Exempelvis:const data = { count: 10, total: 50 }; const formats = ngettext( 'Total: %(total)s, there is %(count)s object', 'there are %(count)s of a total of %(total)s objects', data.count ); const string = interpolate(formats, data, true);
Du bör dock inte överdriva med stränginterpolering: det här är fortfarande JavaScript, så koden måste göra upprepade substitutioner med reguljära uttryck. Detta är inte lika snabbt som stränginterpolering i Python, så håll det till de fall där du verkligen behöver det (till exempel i samband med ngettext
för att producera korrekta pluraliseringar).
get_format
¶
Funktionen get_format
har tillgång till de konfigurerade i18n-formateringsinställningarna och kan hämta formatsträngen för ett givet inställningsnamn:
document.write(get_format('DATE_FORMAT'));
// 'N j, Y'
Den har tillgång till följande inställningar:
Detta är användbart för att upprätthålla formateringskonsistens med de Python-renderade värdena.
gettext_noop
¶
Detta efterliknar funktionen gettext
men gör ingenting, utan returnerar det som skickas till den:
document.write(gettext_noop("this will not be translated"))
Det här är användbart för att stubba ut delar av koden som kommer att behöva översättas i framtiden.
pgettext
¶
Funktionen pgettext
beter sig som Python-varianten (pgettext()
) och ger ett kontextuellt översatt ord:
document.write(pgettext("month name", "May"))
npgettext
¶
Funktionen npgettext
beter sig också som Python-varianten (npgettext()
), vilket ger ett pluraliserat kontextuellt översatt ord:
document.write(npgettext('group', 'party', 1));
// party
document.write(npgettext('group', 'party', 2));
// parties
pluralidx
¶
Funktionen pluralidx
fungerar på samma sätt som mallfiltret pluralize
och avgör om ett givet count
ska använda pluralformen av ett ord eller inte:
document.write(pluralidx(0));
// true
document.write(pluralidx(1));
// false
document.write(pluralidx(2));
// true
I det enklaste fallet, om ingen anpassad pluralisering behövs, returnerar detta false
för heltalet 1
och true
för alla andra tal.
Pluralisering är dock inte lika enkelt på alla språk. Om språket inte stöder pluralisering anges ett tomt värde.
Om det dessutom finns komplexa regler kring pluralisering, kommer katalogvyn att återge ett villkorligt uttryck. Detta utvärderas till antingen ett true
(bör pluraliseras) eller false
(bör inte pluraliseras) värde.
Vyn ”JSONCatalog¶
- class JSONCatalog[source]¶
För att använda ett annat bibliotek på klientsidan för att hantera översättningar kanske du vill dra nytta av vyn
JSONCatalog
. Den liknarJavaScriptCatalog
men returnerar ett JSON-svar.Se dokumentationen för
JavaScriptCatalog
för att lära dig mer om möjliga värden och användning av attributendomain
ochpackages
.Svarsformatet är som följer:
{ "catalog": { # Translations catalog }, "formats": { # Language formats for date, time, etc. }, "plural": "..." # Expression for plural forms, or null. }
Not om prestanda¶
De olika JavaScript/JSON i18n-vyerna genererar katalogen från .mo
-filer vid varje förfrågan. Eftersom dess utdata är konstant, åtminstone för en viss version av en webbplats, är det en bra kandidat för cachelagring.
Cachelagring på serversidan minskar CPU-belastningen. Det är enkelt att implementera med cache_page()
decorator. För att utlösa cache-invalidering när dina översättningar ändras, ange ett versionsberoende nyckelprefix, som visas i exemplet nedan, eller mappa vyn på en versionsberoende URL:
from django.views.decorators.cache import cache_page
from django.views.i18n import JavaScriptCatalog
# The value returned by get_version() must change when translations change.
urlpatterns = [
path(
"jsi18n/",
cache_page(86400, key_prefix="jsi18n-%s" % get_version())(
JavaScriptCatalog.as_view()
),
name="javascript-catalog",
),
]
Cachelagring på klientsidan sparar bandbredd och gör att din webbplats laddas snabbare. Om du använder ETags (ConditionalGetMiddleware
) är du redan täckt. Annars kan du tillämpa :ref:``villkorliga dekoratorer <conditional-decorators>`. I följande exempel ogiltigförklaras cachen när du startar om din applikationsserver:
from django.utils import timezone
from django.views.decorators.http import last_modified
from django.views.i18n import JavaScriptCatalog
last_modified_date = timezone.now()
urlpatterns = [
path(
"jsi18n/",
last_modified(lambda req, **kw: last_modified_date)(
JavaScriptCatalog.as_view()
),
name="javascript-catalog",
),
]
Du kan till och med förgenerera JavaScript-katalogen som en del av din distributionsprocedur och servera den som en statisk fil. Denna radikala teknik implementeras i django-statici18n.
Internationalisering: i URL-mönster¶
Django tillhandahåller två mekanismer för att internationalisera URL-mönster:
Lägger till språkprefixet i roten av URL-mönstren för att göra det möjligt för
LocaleMiddleware
att upptäcka vilket språk som ska aktiveras från den begärda URL:en.Gör URL-mönster själva översättningsbara via funktionen
django.utils.translation.gettext_lazy()
.
Varning
För att använda någon av dessa funktioner krävs att ett aktivt språk ställs in för varje begäran; med andra ord måste du ha django.middleware.locale.LocaleMiddleware
i din MIDDLEWARE
-inställning.
Språkprefix i URL-mönster¶
Denna funktion kan användas i en rot-URLconf och Django kommer automatiskt att preppa den aktuella aktiva språkkoden till alla URL-mönster som definieras inom i18n_patterns()
.
Om du anger prefix_default_language
till False
tas prefixet bort från standardspråket (LANGUAGE_CODE
). Detta kan vara användbart när du lägger till översättningar på en befintlig webbplats så att de aktuella webbadresserna inte ändras.
Exempel på URL-mönster:
from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
from about import views as about_views
from news import views as news_views
from sitemap.views import sitemap
urlpatterns = [
path("sitemap.xml", sitemap, name="sitemap-xml"),
]
news_patterns = (
[
path("", news_views.index, name="index"),
path("category/<slug:slug>/", news_views.category, name="category"),
path("<slug:slug>/", news_views.details, name="detail"),
],
"news",
)
urlpatterns += i18n_patterns(
path("about/", about_views.main, name="about"),
path("news/", include(news_patterns, namespace="news")),
)
Efter att ha definierat dessa URL-mönster kommer Django automatiskt att lägga till språkprefixet till de URL-mönster som lades till av funktionen i18n_patterns
. Exempel på detta:
>>> from django.urls import reverse
>>> from django.utils.translation import activate
>>> activate("en")
>>> reverse("sitemap-xml")
'/sitemap.xml'
>>> reverse("news:index")
'/en/news/'
>>> activate("nl")
>>> reverse("news:detail", kwargs={"slug": "news-slug"})
'/nl/news/news-slug/'
Med prefix_default_language=False
och LANGUAGE_CODE='en'
blir webbadresserna följande:
>>> activate("en")
>>> reverse("news:index")
'/news/'
>>> activate("nl")
>>> reverse("news:index")
'/nl/news/'
Varning
i18n_patterns()
är endast tillåtet i en root URLconf. Om du använder den i en inkluderad URLconf kommer du att få ett ImproperlyConfigured
undantag.
Varning
Se till att du inte har icke-prefixerade URL-mönster som kan kollidera med ett automatiskt tillagt språkprefix.
Översättning av URL-mönster¶
URL-mönster kan också markeras som översättningsbara med hjälp av funktionen gettext_lazy()
. Exempel:
from django.conf.urls.i18n import i18n_patterns
from django.urls import include, path
from django.utils.translation import gettext_lazy as _
from about import views as about_views
from news import views as news_views
from sitemaps.views import sitemap
urlpatterns = [
path("sitemap.xml", sitemap, name="sitemap-xml"),
]
news_patterns = (
[
path("", news_views.index, name="index"),
path(_("category/<slug:slug>/"), news_views.category, name="category"),
path("<slug:slug>/", news_views.details, name="detail"),
],
"news",
)
urlpatterns += i18n_patterns(
path(_("about/"), about_views.main, name="about"),
path(_("news/"), include(news_patterns, namespace="news")),
)
När du har skapat översättningarna kommer funktionen reverse()
att returnera webbadressen på det aktiva språket. Exempel på detta:
>>> from django.urls import reverse
>>> from django.utils.translation import activate
>>> activate("en")
>>> reverse("news:category", kwargs={"slug": "recent"})
'/en/news/category/recent/'
>>> activate("nl")
>>> reverse("news:category", kwargs={"slug": "recent"})
'/nl/nieuws/categorie/recent/'
Varning
I de flesta fall är det bäst att använda översatta webbadresser endast inom ett språkkodsprefixerat block av mönster (med i18n_patterns()
), för att undvika risken att en slarvigt översatt webbadress orsakar en kollision med ett icke-översatt webbadressmönster.
Omvändning i mallar¶
Om lokaliserade webbadresser blir omvända i mallar använder de alltid det aktuella språket. Om du vill länka till en URL på ett annat språk använder du malltaggen language
. Den aktiverar det angivna språket i det bifogade mallavsnittet:
{% load i18n %}
{% get_available_languages as languages %}
{% translate "View this category in:" %}
{% for lang_code, lang_name in languages %}
{% language lang_code %}
<a href="{% url 'category' slug=category.slug %}">{{ lang_name }}</a>
{% endlanguage %}
{% endfor %}
Taggen language
förväntar sig språkkoden som det enda argumentet.
Lokalisering: hur man skapar språkfiler¶
När stränglitteralerna i en applikation har taggats för senare översättning, måste själva översättningen skrivas (eller erhållas). Så här fungerar det.
Meddelandefiler¶
Det första steget är att skapa en message file för ett nytt språk. En meddelandefil är en fil i klartext som representerar ett enda språk och som innehåller alla tillgängliga översättningssträngar och hur de ska representeras på det aktuella språket. Meddelandefiler har filändelsen .po
.
Django levereras med ett verktyg, django-admin makemessages
, som automatiserar skapandet och underhållet av dessa filer.
Gettext-verktyg
Kommandot makemessages
(och compilemessages
som diskuteras senare) använder kommandon från GNU gettext toolset: xgettext
, msgfmt
, msgmerge
och msguniq
.
Den minsta versionen av gettext
-verktygen som stöds är 0.19.
Kör det här kommandot för att skapa eller uppdatera en meddelandefil:
django-admin makemessages -l de
…där de
är lokalnamn för den meddelandefil du vill skapa. Till exempel pt_BR
för brasiliansk portugisiska, de_AT
för österrikisk tyska eller id
för indonesiska.
Skriptet bör köras från en av två platser:
Rotkatalogen för ditt Django-projekt (den som innehåller
manage.py
).Rotkatalogen för en av dina Django-appar.
Skriptet körs över projektets källträd eller applikationens källträd och drar ut alla strängar som är markerade för översättning (se Hur Django upptäcker översättningar och se till att LOCALE_PATHS
är korrekt konfigurerad). Det skapar (eller uppdaterar) en meddelandefil i katalogen locale/LANG/LC_MESSAGES
. I exemplet de
kommer filen att vara locale/de/LC_MESSAGES/django.po
.
När du kör makemessages
från rotkatalogen i ditt projekt, kommer de extraherade strängarna automatiskt att distribueras till rätt meddelandefiler. Det vill säga, en sträng som extraheras från en fil i en app som innehåller en locale
-katalog kommer att hamna i en meddelandefil under den katalogen. En sträng som extraheras från en fil i en app utan någon locale
-katalog kommer antingen att läggas i en meddelandefil under den katalog som listas först i LOCALE_PATHS
eller generera ett fel om LOCALE_PATHS
är tom.
Som standard undersöker django-admin makemessages
alla filer som har filtillägget .html
, .txt
eller .py
. Om du vill åsidosätta denna standard använder du alternativet --extension
eller -e
för att ange vilka filtillägg som ska undersökas:
django-admin makemessages -l de -e txt
Separera flera tillägg med kommatecken och/eller använd -e
eller --extension
flera gånger:
django-admin makemessages -l de -e html,txt -e xml
Varning
När du skapar meddelandefiler från JavaScript-källkod måste du använda den speciella domänen djangojs
, inte -e js
.
Använda Jinja2-mallar?
makemessages
förstår inte syntaxen i Jinja2-mallar. För att extrahera strängar från ett projekt som innehåller Jinja2-mallar, använd Message Extracting från Babel istället.
Här är ett exempel på konfigurationsfilen babel.cfg
:
# Extraction from Python source files
[python: **.py]
# Extraction from Jinja2 templates
[jinja2: **.jinja]
extensions = jinja2.ext.with_
Se till att du listar alla tillägg du använder! Annars kommer Babel inte att känna igen de taggar som definieras av dessa tillägg och kommer att ignorera Jinja2-mallar som innehåller dem helt och hållet.
Babel tillhandahåller liknande funktioner som makemessages
, kan ersätta det i allmänhet och är inte beroende av gettext
. För mer information, läs dess dokumentation om att arbeta med meddelandekataloger.
Ingen gettext?
Om du inte har verktygen gettext
installerade kommer makemessages
att skapa tomma filer. Om så är fallet, installera antingen gettext
-verktygen eller kopiera den engelska meddelandefilen (locale/en/LC_MESSAGES/django.po
) om den finns tillgänglig och använd den som utgångspunkt, vilket är en tom översättningsfil.
Arbetar du på Windows?
Om du använder Windows och behöver installera GNU:s gettext-verktyg för att makemessages
ska fungera, se gettext på Windows för mer information.
Varje .po
-fil innehåller en liten del metadata, till exempel kontaktuppgifter till den som ansvarar för översättningen, men huvuddelen av filen är en lista med messages - mappningar mellan översättningssträngar och den faktiska översatta texten för det aktuella språket.
Till exempel, om din Django-app innehåller en översättningssträng för texten "Välkommen till min webbplats."
, så här:
_("Welcome to my site.")
…då kommer django-admin makemessages
att ha skapat en .po
-fil som innehåller följande utdrag – ett meddelande:
#: path/to/python/module.py:23
msgid "Welcome to my site."
msgstr ""
En snabb förklaring:
msgid
är översättningssträngen, som visas i källan. Ändra inte den.msgstr
är där du lägger in den språkspecifika översättningen. Den är tom från början, så det är ditt ansvar att ändra den. Se till att du behåller citattecknen runt din översättning.För enkelhetens skull innehåller varje meddelande, i form av en kommentarrad med
#
som prefix och ovanförmsgid
-raden, filnamnet och radnumret från vilket översättningssträngen hämtades.
Långa meddelanden är ett specialfall. Där är den första strängen direkt efter msgstr
(eller msgid
) en tom sträng. Sedan skrivs själva innehållet över de närmaste raderna som en sträng per rad. Dessa strängar konkateneras direkt. Glöm inte efterföljande mellanslag i strängarna; annars kommer de att häftas ihop utan blanksteg!
Tänk på din charset
På grund av hur gettext
-verktygen fungerar internt och eftersom vi vill tillåta icke-ASCII-källsträngar i Djangos kärna och dina applikationer, måste du must använda UTF-8 som kodning för dina .po
-filer (standard när .po
-filer skapas). Detta innebär att alla kommer att använda samma kodning, vilket är viktigt när Django bearbetar .po
-filerna.
Flummiga poster
makemessages
genererar ibland översättningsposter som är markerade som luddiga, t.ex. när översättningar härleds från tidigare översatta strängar. Som standard bearbetas inte fuzzy-poster av compilemessages
.
Om du vill granska all källkod och alla mallar för nya översättningssträngar och uppdatera alla meddelandefiler för alla språk kör du detta:
django-admin makemessages -a
Sammanställning av meddelandefiler¶
När du har skapat din meddelandefil - och varje gång du gör ändringar i den - måste du sammanställa den till en mer effektiv form som kan användas av gettext
. Detta gör du med django-admin compilemessages
.
Detta verktyg går igenom alla tillgängliga .po
-filer och skapar .mo
-filer, som är binära filer optimerade för att användas av gettext
. I samma katalog som du körde django-admin makemessages
, kör django-admin compilemessages
så här:
django-admin compilemessages
Nu är det klart. Dina översättningar är klara att användas.
Arbetar du på Windows?
Om du använder Windows och behöver installera GNU:s gettext-verktyg för att django-admin compilemessages
ska fungera, se gettext på Windows för mer information.
.po
-filer: Kodning och BOM-användning.
Django stöder endast .po
-filer kodade i UTF-8 och utan någon BOM (Byte Order Mark) så om din textredigerare lägger till sådana märken i början av filer som standard måste du konfigurera om den.
Felsökning: gettext()
upptäcker felaktigt python-format
i strängar med procenttecken¶
I vissa fall, till exempel strängar med ett procenttecken följt av ett mellanslag och en :ref:strängkonverteringstyp <old-string-formatting>
(t.ex. _("10% interest")
), gettext()
flaggar felaktigt strängar med python-format
.
Om du försöker kompilera meddelandefiler med felaktigt markerade strängar får du ett felmeddelande som antalet formatspecifikationer i 'msgid' och 'msgstr' matchar inte
eller 'msgstr' är inte en giltig Python-formatsträng, till skillnad från 'msgid'
.
För att kringgå detta kan du undkomma procenttecken genom att lägga till ett andra procenttecken:
from django.utils.translation import gettext as _
output = _("10%% interest")
Eller så kan du använda no-python-format
så att alla procenttecken behandlas som bokstavstecken:
# xgettext:no-python-format
output = _("10% interest")
Skapa meddelandefiler från JavaScript-källkod¶
Du skapar och uppdaterar meddelandefilerna på samma sätt som de andra Django-meddelandefilerna - med verktyget django-admin makemessages
. Den enda skillnaden är att du uttryckligen måste ange vad som på gettextspråk kallas en domän, i det här fallet djangojs
-domänen, genom att tillhandahålla en -d djangojs
-parameter, så här:
django-admin makemessages -d djangojs -l de
Detta skulle skapa eller uppdatera meddelandefilen för JavaScript för tyska. Efter uppdatering av meddelandefiler kör du django-admin compilemessages
på samma sätt som du gör med vanliga Django-meddelandefiler.
gettext
på Windows¶
Detta behövs endast för personer som antingen vill extrahera meddelande-ID:n eller sammanställa meddelandefiler (.po
). Själva översättningsarbetet innebär att redigera befintliga filer av den här typen, men om du vill skapa dina egna meddelandefiler, eller vill testa eller kompilera en ändrad meddelandefil, ladda ner en förkompilerad binär installerare.
Du kan också använda gettext
binära filer som du har fått på annat håll, så länge kommandot xgettext --version
fungerar korrekt. Försök inte att använda Djangos översättningsverktyg med ett gettext
-paket om kommandot xgettext --version
som anges i en Windows-kommandotolk orsakar ett popup-fönster som säger ”xgettext.exe
har genererat fel och kommer att stängas av Windows”.
Anpassa kommandot ”Makemessages¶
Om du vill skicka ytterligare parametrar till xgettext
måste du skapa ett anpassat makemessages
-kommando och åsidosätta dess xgettext_options
-attribut:
from django.core.management.commands import makemessages
class Command(makemessages.Command):
xgettext_options = makemessages.Command.xgettext_options + ["--keyword=mytrans"]
Om du behöver mer flexibilitet kan du också lägga till ett nytt argument till ditt anpassade makemessages
-kommando:
from django.core.management.commands import makemessages
class Command(makemessages.Command):
def add_arguments(self, parser):
super().add_arguments(parser)
parser.add_argument(
"--extra-keyword",
dest="xgettext_keywords",
action="append",
)
def handle(self, *args, **options):
xgettext_keywords = options.pop("xgettext_keywords")
if xgettext_keywords:
self.xgettext_options = makemessages.Command.xgettext_options[:] + [
"--keyword=%s" % kwd for kwd in xgettext_keywords
]
super().handle(*args, **options)
Diverse¶
Omdirigeringsvyn ”set_language¶
Som en bekvämlighet kommer Django med en vy, django.views.i18n.set_language()
, som ställer in en användares språkpreferens och omdirigerar till en given URL eller, som standard, tillbaka till föregående sida.
Aktivera denna vy genom att lägga till följande rad i din URLconf:
path("i18n/", include("django.conf.urls.i18n")),
(Observera att detta exempel gör vyn tillgänglig på /i18n/setlang/
.)
Varning
Se till att du inte inkluderar ovanstående URL inom i18n_patterns()
- den måste vara språkoberoende själv för att fungera korrekt.
Vyn förväntar sig att anropas via metoden POST
, med en language
parameter angiven i begäran. Om sessionsstöd är aktiverat sparar vyn språkvalet i användarens session. Det sparar också språkvalet i en cookie som heter django_language
som standard. (Namnet kan ändras genom inställningen LANGUAGE_COOKIE_NAME
)
Efter att ha ställt in språkvalet letar Django efter en next
parameter i POST
eller GET
data. Om den hittas och Django anser att det är en säker URL (dvs. den pekar inte till en annan värd och använder ett säkert schema), kommer en omdirigering till den URL:en att utföras. Annars kan Django falla tillbaka till att omdirigera användaren till webbadressen från Referer
-huvudet eller, om det inte är inställt, till /
, beroende på typen av begäran:
Om begäran accepterar HTML-innehåll (baserat på HTTP-rubriken
Accept
) utförs alltid fallbacken.Om begäran inte accepterar HTML, kommer fallbacken endast att utföras om parametern
next
har angetts. I annat fall returneras en statuskod 204 (Inget innehåll).
Här är ett exempel på HTML-mallkod:
{% load i18n %}
<form action="{% url 'set_language' %}" method="post">{% csrf_token %}
<input name="next" type="hidden" value="{{ redirect_to }}">
<select name="language">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% for language in languages %}
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected{% endif %}>
{{ language.name_local }} ({{ language.code }})
</option>
{% endfor %}
</select>
<input type="submit" value="Go">
</form>
I det här exemplet letar Django upp webbadressen till den sida som användaren kommer att omdirigeras till i kontextvariabeln redirect_to
.
Explicit inställning av det aktiva språket¶
Du kanske vill ange det aktiva språket för den aktuella sessionen uttryckligen. Kanske hämtas en användares språkpreferens från ett annat system, till exempel. Du har redan blivit introducerad till django.utils.translation.activate()
. Det gäller endast för den aktuella tråden. För att behålla språket för hela sessionen i en cookie, ställ in LANGUAGE_COOKIE_NAME
-cookien på svaret:
from django.conf import settings
from django.http import HttpResponse
from django.utils import translation
user_language = "fr"
translation.activate(user_language)
response = HttpResponse(...)
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, user_language)
Du vill vanligtvis använda båda: django.utils.translation.activate()
ändrar språket för den här tråden, och om du anger cookien kommer denna preferens att kvarstå i framtida förfrågningar.
Använda översättningar utanför vyer och mallar¶
Även om Django tillhandahåller en rik uppsättning i18n-verktyg för användning i vyer och mallar, begränsar det inte användningen till Django-specifik kod. Djangos översättningsmekanismer kan användas för att översätta godtyckliga texter till alla språk som stöds av Django (så länge som en lämplig översättningskatalog finns, naturligtvis). Du kan ladda en översättningskatalog, aktivera den och översätta text till valfritt språk, men kom ihåg att växla tillbaka till originalspråket, eftersom aktivering av en översättningskatalog görs per tråd och en sådan ändring kommer att påverka kod som körs i samma tråd.
Till exempel:
from django.utils import translation
def welcome_translated(language):
cur_language = translation.get_language()
try:
translation.activate(language)
text = translation.gettext("welcome")
finally:
translation.activate(cur_language)
return text
Anrop av denna funktion med värdet 'de'
ger dig "Willkommen"
, oavsett LANGUAGE_CODE
och språk som anges av mellanprogramvaran.
Funktioner av särskilt intresse är django.utils.translation.get_language()
som returnerar språket som används i den aktuella tråden, django.utils.translation.activate()
som aktiverar en översättningskatalog för den aktuella tråden, och django.utils.translation.check_for_language()
som kontrollerar om det angivna språket stöds av Django.
För att hjälpa till att skriva mer kortfattad kod finns det också en kontexthanterare django.utils.translation.override()
som lagrar det aktuella språket vid inmatning och återställer det vid utmatning. Med den blir exemplet ovan:
from django.utils import translation
def welcome_translated(language):
with translation.override(language):
return translation.gettext("welcome")
Anteckningar om implementering¶
Specialiteter inom Django-översättning¶
Djangos översättningsmaskineri använder standardmodulen gettext
som medföljer Python. Om du känner till gettext
kanske du noterar dessa specialiteter i hur Django gör översättning:
Strängdomänen är
django
ellerdjangojs
. Denna strängdomän används för att skilja mellan olika program som lagrar sina data i ett gemensamt meddelandefilsbibliotek (vanligtvis/usr/share/locale/
). Domänendjango
används för översättningssträngar för Python och mallar och laddas in i de globala översättningskatalogerna. Domänendjangojs
används endast för JavaScript-översättningskataloger för att se till att de är så små som möjligt.Django använder inte enbart
xgettext
. Det använder Pythonomslag runtxgettext
ochmsgfmt
. Detta är mest för bekvämlighetens skull.
Hur Django upptäcker språkpreferenser¶
När du har förberett dina översättningar - eller om du vill använda de översättningar som följer med Django - måste du aktivera översättning för din app.
Bakom kulisserna har Django en mycket flexibel modell för att bestämma vilket språk som ska användas - installationsövergripande, för en viss användare eller båda.
För att ställa in en installationsomfattande språkpreferens, ställ in LANGUAGE_CODE
. Django använder detta språk som standardöversättning - det sista försöket om ingen bättre matchande översättning hittas genom någon av de metoder som används av locale middleware (se nedan).
Om allt du vill är att köra Django med ditt modersmål behöver du bara ställa in LANGUAGE_CODE
och se till att motsvarande message files och deras kompilerade versioner (.mo
) finns.
Om du vill låta varje enskild användare ange vilket språk de föredrar måste du också använda LocaleMiddleware
. LocaleMiddleware
möjliggör språkval baserat på data från begäran. Det anpassar innehållet för varje användare.
För att använda LocaleMiddleware
, lägg till 'django.middleware.locale.LocaleMiddleware'
till din MIDDLEWARE
inställning. Eftersom ordningen på middleware spelar roll, följ dessa riktlinjer:
Se till att det är en av de första middleware som installeras.
Det bör komma efter
SessionMiddleware
, eftersomLocaleMiddleware
använder sessionsdata. Och det bör komma föreCommonMiddleware
eftersomCommonMiddleware
behöver ett aktiverat språk för att kunna lösa den begärda URL:en.Om du använder
CacheMiddleware
, lägg tillLocaleMiddleware
efter det.
Till exempel kan din MIDDLEWARE
se ut så här:
MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.locale.LocaleMiddleware",
"django.middleware.common.CommonMiddleware",
]
(Mer information om middleware finns i middleware documentation.)
LocaleMiddleware
försöker bestämma användarens språkpreferens genom att följa denna algoritm:
Först letar den efter språkprefixet i den begärda webbadressen. Detta utförs endast när du använder funktionen
i18n_patterns
i din rot-URLconf. Se url-internationization för mer information om språkprefixet och hur man internationaliserar URL-mönster.Om det inte lyckas letar den efter en kaka.
Namnet på den cookie som används bestäms av inställningen
LANGUAGE_COOKIE_NAME
. (Standardnamnet ärdjango_language
.)Om så inte är fallet tittar den på HTTP-huvudet ”Accept-Language”. Denna header skickas av din webbläsare och talar om för servern vilket eller vilka språk du föredrar, i prioritetsordning. Django provar varje språk i rubriken tills den hittar ett med tillgängliga översättningar.
Om så inte är fallet används den globala inställningen
LANGUAGE_CODE
.
Anteckningar:
På var och en av dessa platser förväntas språkpreferensen vara i standard language format, som en sträng. Till exempel är brasiliansk portugisiska
pt-br
.Om ett basspråk är tillgängligt men det angivna underspråket inte är det, använder Django basspråket. Till exempel, om en användare anger
de-at
(österrikisk tyska) men Django bara harde
tillgängligt, använder Djangode
.Endast de språk som anges i inställningen
LANGUAGES
kan väljas. Om du vill begränsa språkvalet till en delmängd av de språk som tillhandahålls (eftersom ditt program inte tillhandahåller alla dessa språk) anger duLANGUAGES
till en lista med språk. Till exempel:LANGUAGES = [ ("de", _("German")), ("en", _("English")), ]
I det här exemplet begränsas de språk som är tillgängliga för automatiskt val till tyska och engelska (och alla underspråk, som
de-ch
elleren-us
).Om du definierar en anpassad
LANGUAGES
-inställning, som förklaras i föregående punkt, kan du markera språknamnen som översättningssträngar - men användgettext_lazy()
istället förgettext()
för att undvika en cirkulär import.Här är ett exempel på en inställningsfil:
from django.utils.translation import gettext_lazy as _ LANGUAGES = [ ("de", _("German")), ("en", _("English")), ]
När LocaleMiddleware
har bestämt användarens preferens gör den denna preferens tillgänglig som request.LANGUAGE_CODE
för varje HttpRequest
. Läs gärna detta värde i din view-kod. Här är ett exempel:
from django.http import HttpResponse
def hello_world(request, count):
if request.LANGUAGE_CODE == "de-at":
return HttpResponse("You prefer to read Austrian German.")
else:
return HttpResponse("You prefer to read another language.")
Observera att med statisk översättning (utan middleware) finns språket i settings.LANGUAGE_CODE
, medan det med dynamisk översättning (middleware) finns i request.LANGUAGE_CODE
.
Hur Django upptäcker översättningar¶
Vid körning bygger Django en enhetlig katalog av bokstavs-översättningar i minnet. För att uppnå detta letar den efter översättningar genom att följa denna algoritm angående ordningen i vilken den undersöker de olika filsökvägarna för att ladda de kompilerade meddelandefilerna (.mo
) och företräde för flera översättningar för samma bokstav:
De kataloger som anges i
LOCALE_PATHS
har högst prioritet, där de som visas först har högre prioritet än de som visas senare.Sedan letar den efter och använder om det finns en
locale
-katalog i var och en av de installerade appar som listas iINSTALLED_APPS
. De som visas först har högre prioritet än de som visas senare.Slutligen används den Django-tillhandahållna basöversättningen i django/conf/locale som en fallback.
Se även
Översättningarna för literaler som ingår i JavaScript-tillgångar söks upp enligt en liknande men inte identisk algoritm. Se JavaScriptCatalog
för mer information.
Du kan också placera :ref:custom format files <custom-format-files>
i LOCALE_PATHS
-katalogerna om du också anger FORMAT_MODULE_PATH
.
I samtliga fall förväntas namnet på den katalog som innehåller översättningen vara namngivet med locale name-notation. T.ex. de
, pt_BR
, es_AR
, etc. Oöversatta strängar för territoriella språkvarianter använder översättningarna av det generiska språket. Till exempel, oöversatta pt_BR
strängar använder pt
översättningar.
På så sätt kan du skriva program som innehåller sina egna översättningar och du kan åsidosätta basöversättningar i ditt projekt. Eller så kan du bygga ett stort projekt av flera appar och lägga alla översättningar i en stor gemensam meddelandefil som är specifik för det projekt du skapar. Valet är ditt.
Alla arkiv för meddelandefiler är uppbyggda på samma sätt. De är följande:
Alla sökvägar som anges i
LOCALE_PATHS
i din inställningsfil söks efter<language>/LC_MESSAGES/django.(po|mo)
$APPPATH/locale/<language>/LC_MESSAGES/django.(po|mo)`
$PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.(po|mo)
För att skapa meddelandefiler använder du verktyget django-admin makemessages
. Och du använder django-admin compilemessages
för att producera de binära .mo
-filerna som används av gettext
.
Du kan också köra django-admin compilemessages --settings=path.to.settings
för att få kompilatorn att bearbeta alla kataloger i din LOCALE_PATHS
-inställning.
Användning av ett icke-engelskt basspråk¶
Django gör det allmänna antagandet att originalsträngarna i ett översättningsbart projekt är skrivna på engelska. Du kan välja ett annat språk, men du måste vara medveten om vissa begränsningar:
gettext
ger bara två pluralformer för de ursprungliga meddelandena, så du måste också tillhandahålla en översättning för basspråket för att inkludera alla pluralformer om pluralreglerna för basspråket skiljer sig från engelska.När en engelsk variant är aktiverad och engelska strängar saknas, kommer reservspråket inte att vara projektets
LANGUAGE_CODE
, utan originalsträngarna. Till exempel kommer en engelsk användare som besöker en webbplats medLANGUAGE_CODE
inställt på spanska och originalsträngar skrivna på ryska att se rysk text i stället för spansk.