In mijn vorige artikelen behandelde ik de verschillende aspecten van Elixir - een moderne functionele programmeertaal. Vandaag wil ik echter een stapje verder gaan met de taal zelf en een zeer snel en betrouwbaar MVC-raamwerk bespreken, Phoenix genaamd, dat is geschreven in Elixir.
Dit raamwerk is bijna vijf jaar geleden ontstaan en heeft sindsdien enige tractie gekregen. Het is natuurlijk nog niet zo populair als Rails of Django, maar het heeft een groot potentieel en ik vind het echt leuk.
In dit artikel gaan we kijken hoe I18n in Phoenix-toepassingen kan worden geïntroduceerd. Wat is i18n, je vraagt? Welnu, het is een numeroniem dat "internationalisering" betekent, want er zijn precies 18 tekens tussen de eerste letter "i" en de laatste "n". Waarschijnlijk heb je ook een ontmoet l10n numeroniem wat "lokalisatie" betekent. Ontwikkelaars zijn tegenwoordig zo lui dat ze niet eens een paar extra karakters kunnen schrijven, hè?
Internationalisering is een zeer belangrijk proces, vooral als u verwacht dat de applicatie door mensen van over de hele wereld wordt gebruikt. Immers, niet iedereen kent Engels goed, en het laten vertalen van de app in de moedertaal van een gebruiker geeft een goede indruk.
Het lijkt erop dat het proces van het vertalen van Phoenix-applicaties enigszins verschilt van, bijvoorbeeld, het vertalen van Rails-apps (maar vrij gelijkaardig aan hetzelfde proces in Django). Om Phoenix-toepassingen te vertalen, gebruiken we een vrij populaire oplossing genaamd Gettext, die al meer dan 25 jaar bestaat. Gettext werkt met speciale soorten bestanden, namelijk PO en POT, en ondersteunt functies zoals scoping, pluralisatie en andere goodies.
Dus in deze post ga ik je uitleggen wat Gettext is, hoe PO verschilt van POT, hoe je berichten in Phoenix kunt lokaliseren en waar je vertalingen kunt opslaan. We gaan ook kijken hoe we de locale van de applicatie kunnen veranderen en hoe we met pluralisatieregels en -domeinen kunnen werken.
Zullen we beginnen?
Gettext is een door de strijd getest open-source internationalisatietool dat oorspronkelijk door Sun Microsystems in 1990 werd geïntroduceerd. In 1995 bracht GNU zijn eigen versie van Gettext uit, die nu als de meest populaire wordt beschouwd (de nieuwste versie was 0.19.8 op de tijd van schrijven van dit artikel). Gettext kan worden gebruikt om meertalige systemen van elk formaat en type te maken, van webapps tot operationele systemen. Deze oplossing is vrij complex en we zullen natuurlijk niet alle functies bespreken. De volledige Gettext-documentatie is te vinden op gnu.org.
Gettext biedt u alle benodigde hulpmiddelen om lokalisatie uit te voeren en stelt enkele eisen aan de vraag hoe vertaalbestanden moeten worden benoemd en georganiseerd. Er worden twee bestandstypen gebruikt om vertalingen te hosten: PO en MO.
PO (Draagbaar object) bestanden opslaan vertalingen voor gegeven strings evenals pluralisatieregels en metadata. Deze bestanden hebben een vrij eenvoudige structuur en kunnen eenvoudig door een persoon worden bewerkt, dus in dit artikel zullen we ons aan hen houden. Elk PO-bestand bevat vertalingen (of een deel van de vertalingen) voor een enkele taal en moet worden opgeslagen in een map met de naam van deze taal: nl, fr, de, enz.
MO (Machine Object) bestanden bevatten binaire gegevens die niet bedoeld zijn om rechtstreeks door een mens te worden bewerkt. Ze zijn moeilijker om mee te werken en bespreken ervan valt buiten het bestek van dit artikel.
Om dingen complexer te maken, zijn er ook POT (sjabloon voor draagbare objecten) bestanden. Ze hosten alleen een reeks gegevens om te vertalen, maar niet de vertalingen zelf. In principe worden POT-bestanden alleen als blauwdrukken gebruikt om PO-bestanden voor verschillende landinstellingen te maken.
Oké, dus laten we nu doorgaan met oefenen! Als je wilt meegaan, zorg dan dat je het volgende hebt geïnstalleerd:
Maak een nieuwe voorbeeldtoepassing zonder een database door te draaien:
mix phx.new i18ndemo --no-ecto
--no-ecto
zegt dat de database niet door de app moet worden gebruikt (Ecto is een hulpmiddel om met de DB zelf te communiceren). Merk op dat de generator mogelijk een paar minuten nodig heeft om alles voor te bereiden.
Gebruik nu CD
om naar de nieuw gecreëerde te gaan i18ndemo
map en voer de volgende opdracht uit om de server op te starten:
mix phx.server
Open vervolgens de browser en ga naar http: // localhost: 4000
, waar je een "Welcome to Phoenix!" bericht.
Wat interessant is aan onze Phoenix-app en, in het bijzonder, de welkomstboodschap is dat Gettext al standaard wordt gebruikt. Ga je gang en open de demo / lib / demo_web / templates / page / index.html.eex
bestand dat fungeert als standaard startpagina. Verwijder alles behalve deze code:
<%= gettext "Welcome to %name!", name: "Phoenix" %>
Deze welkomstboodschap maakt gebruik van a gettext
functie die een tekenreeks accepteert om te vertalen als het eerste argument. Deze reeks kan worden beschouwd als een vertaalsleutel, hoewel het enigszins verschilt van de toetsen die worden gebruikt in Rails I18n en enkele andere frameworks. In Rails zouden we een sleutel zoals hebben gebruikt page.welcome
, terwijl hier de vertaalde reeks een sleutel is zelf. Dus als de vertaling niet kan worden gevonden, kunnen we deze reeks direct weergeven. Zelfs een gebruiker die slecht Engels kent, kan op zijn minst een idee krijgen van wat er gaande is.
Deze aanpak is eigenlijk best handig - stop even en denk erover na. Je hebt een applicatie waarbij alle berichten in het Engels zijn. Als u het wilt internationaliseren, hoeft u in het eenvoudigste geval uw berichten in te pakken met de gettext
functioneer en voorzie vertalingen ervan (later zullen we zien dat het proces van het uitpakken van de sleutels eenvoudig kan worden geautomatiseerd, wat de zaken nog meer versnelt).
Oké, laten we terugkeren naar ons kleine codefragment en een kijkje nemen naar het tweede argument dat is doorgegeven gettext
: naam: "Phoenix"
. Dit is een zogenaamde verbindend-een parameter omwikkeld met %
die we willen interpoleren in de gegeven vertaling. In dit voorbeeld is er slechts één parameter genaamd naam
.
We kunnen ook nog een bericht aan deze pagina toevoegen voor demonstratiedoeleinden:
<%= gettext "Welcome to %name!", name: "Phoenix" %>
<%= gettext "We are using version %version", version: "1.3" %>
Nu we twee berichten op de hoofdpagina hebben staan, waar moeten we dan vertalingen voor toevoegen? Het lijkt erop dat alle vertalingen zijn opgeslagen onder de priv / gettext
map, die een vooraf gedefinieerde structuur heeft. Laten we een moment nemen om te bespreken hoe Gettext-bestanden moeten worden georganiseerd (dit geldt niet alleen voor Phoenix maar voor elke app met Gettext).
Allereerst moeten we een map maken met de naam van de locale waarvoor het vertalingen gaat opslaan. Binnenin zou er een map moeten zijn LC_MESSAGES
een of meerdere bevatten .po
bestanden met de eigenlijke vertalingen. In het eenvoudigste geval zou je er een hebben default.po
bestand per landinstelling. standaard
hier is de naam van het domein (of de scope). Domeinen worden gebruikt om vertalingen in verschillende groepen op te splitsen: u kunt bijvoorbeeld domeinen met de naam krijgen beheerder
, WYSIWIG
, kar
, en andere. Dit is handig als u een grote applicatie met honderden berichten heeft. Voor kleinere apps echter met een sole standaard
domein is voldoende.
Onze bestandsstructuur ziet er dus als volgt uit:
Om te beginnen met het maken van PO-bestanden, hebben we eerst de bijbehorende sjabloon (POT) nodig. We kunnen het handmatig maken, maar ik ben te lui om het op deze manier te doen. Laten we in plaats daarvan het volgende commando uitvoeren:
mix gettext.extract
Het is een erg handige tool die de projectbestanden scant en controleert of Gettext overal wordt gebruikt. Nadat het script zijn taak heeft voltooid, een nieuw priv / gettext / default.pot
bestand met strings om te vertalen zal worden gemaakt.
Zoals we al hebben geleerd, zijn POT-bestanden sjablonen, dus ze slaan alleen de sleutels zelf op, niet de vertalingen, dus wijzig dergelijke bestanden niet handmatig. Open een nieuw bestand en bekijk de inhoud ervan:
## Dit bestand is een PO-sjabloonbestand. ## ## 'msgid hier worden vaak geëxtraheerd uit de broncode. ## Voeg alleen handmatig nieuwe vertalingen toe als het dynamische ##-vertalingen zijn die niet statisch kunnen worden geëxtraheerd. ## ## Voer 'mix gettext.extract' uit om dit bestand naar de ## datum te brengen. Laat 'msgstr's leeg als ze hier veranderen als geen ## effect: bewerk ze in PO (' .po ') bestanden in plaats daarvan. msgstr "" msgstr "We gebruiken versie% versie" msgstr "" #: lib / demo_web / templates / page / index.html .eex: 2 msgstr "Welkom bij% name!" msgstr ""
Handig, toch? Al onze berichten werden automatisch ingevoegd en we kunnen eenvoudig zien waar ze zich precies bevinden. msgid
, zoals je waarschijnlijk wel hebt geraden, is de sleutel, terwijl msgstr
zal een vertaling bevatten.
De volgende stap is natuurlijk het genereren van een PO-bestand. Rennen:
mix gettext.merge priv / gettext
Dit script gaat gebruik maken van de default.pot
sjabloon en maak een default.po
bestand in de priv / gettext / nl / LC_MESSAGES
map. Voorlopig hebben we alleen een Engelse locale, maar ondersteuning voor een andere taal zal ook in de volgende sectie worden toegevoegd.
Overigens is het mogelijk om de POT-sjabloon en alle PO-bestanden in één keer te maken of bij te werken met behulp van de volgende opdracht:
mix gettext.extract --merge
Laten we nu de. Openen priv / gettext / nl / LC_MESSAGES / default.po
bestand, dat de volgende inhoud heeft:
## 'msgid's in dit bestand komen uit POT (.pot) bestanden. ## ## Voeg hier geen msgid's toe, wijzig of verwijder ze hier niet als ## ze zijn gekoppeld aan die in het corresponderende POT-bestand ## (met hetzelfde domein). ## ## Gebruik 'mix gettext.extract --merge' of 'mix gettext.merge' ## om POT-bestanden in PO-bestanden samen te voegen. msgstr "" msgstr "" "Taal: en \ n" #: lib / demo_web / templates / pagina / index.html.eex: 3 msgstr "We gebruiken versie% versie" msgstr "" #: lib / demo_web / templates / page / index.html.eex: 2 msgid "Welkom bij% name!" msgstr ""
Dit is het bestand waar we de eigenlijke vertaling moeten uitvoeren. Het heeft natuurlijk weinig zin om dit te doen omdat de berichten al in het Engels zijn, dus laten we doorgaan naar de volgende sectie en ondersteuning voor een tweede taal toevoegen.
Natuurlijk is de standaardlocale voor Phoenix-toepassingen Engels, maar deze instelling kan eenvoudig worden gewijzigd door de config / config.exs
het dossier. Laten we bijvoorbeeld de standaard locale instellen op Russisch (voel je vrij om te houden met een andere taal naar keuze):
config: demo, I18ndemoWeb.Gettext, default_locale: "ru"
Het is ook een goed idee om de volledige lijst met alle ondersteunde landinstellingen te specificeren:
config: demo, I18ndemoWeb.Gettext, default_locale: "ru", locales: ~ w (en ru)
Wat we nu moeten doen is een nieuw PO-bestand genereren met vertalingen voor de Russische locale. Het kan worden gedaan door het gettext.merge
script opnieuw, maar met een --locale
schakelaar:
mix gettext.merge priv / gettext --locale ru
Uiteraard, een priv / gettext / ru / LC_MESSAGES
map met de .po
bestanden binnen worden gegenereerd. Merk overigens op dat los van de default.po
bestand, we hebben ook errors.po
. Dit is een standaardlocatie voor het vertalen van foutmeldingen, maar in dit artikel gaan we het negeren.
Nu tweak priv / gettext / ru / LC_MESSAGES / default.po
door enkele vertalingen toe te voegen:
msgstr "We gebruiken versie% versie" msgstr "Используется версия% version" #: lib / demo_web / templates / page / index.html .eex: 2 msgstr "Welkom bij% name!" msgstr "Добро пожаловать в приложение% name!"
Nu, afhankelijk van de gekozen locale, geeft Phoenix Engelse of Russische vertalingen. Maar wacht even! Hoe kunnen we eigenlijk schakelen tussen locales in onze applicatie? Laten we doorgaan naar het volgende gedeelte en ontdekken!
Nu sommige vertalingen aanwezig zijn, moeten we onze gebruikers in staat stellen om tussen de verschillende talen te schakelen. Het lijkt erop dat er een externe plug is die set_locale heet. Het werkt door de gekozen locale uit de URL of uit te pakken Accept-Language
HTTP-header. Dus, om een locale in de URL op te geven, zou je typen http: // localhost: 4000 / nl / some_path
. Als de landinstelling niet is opgegeven (of als een niet-ondersteunde taal is aangevraagd), gebeurt er een van de twee dingen:
Accept-Language
HTTP-header en deze locale wordt ondersteund, de gebruiker wordt omgeleid naar een pagina met de bijbehorende locale.Open de mix.exs
bestand en drop-in set_locale
naar de deps
functie:
defp-deps doen [# ... : set_locale, "~> 0.2.1"] eindigen
We moeten het ook toevoegen aan de toepassing
functie:
def application do [mod: Demo.Application, [], extra_applications: [: logger,: runtime_tools,: set_locale]] end
Installeer vervolgens alles:
mix deps.get
Onze router bevindt zich op lib / demo_web / router.ex
vereist ook enkele veranderingen. Concreet moeten we een nieuwe plug toevoegen aan de : browser
pijpleiding:
pipeline: browser do # ... plug SetLocale, gettext: DemoWeb.Gettext, default_locale: "ru" end
Maak ook een nieuwe scope:
scope "/: locale", DemoWeb do pipe_through: browser krijgt "/", PageController,: index end
En dat is het! U kunt de server opstarten en naar navigeren http: // localhost: 4000 / ru
en http: // localhost: 4000 / nl
. Merk op dat de berichten correct worden vertaald, en dat is precies wat we nodig hebben!
U kunt ook een soortgelijke functie zelf coderen door een module-plug te gebruiken. Een klein voorbeeld is te vinden in de officiële gids van Phoenix.
Een laatste ding om te vermelden is dat je in sommige gevallen een specifieke locale moet afdwingen. Om dit te doen, gebruikt u gewoon a with_locale
functie:
Gettext.with_locale I18ndemoWeb.Gettext, "en", fn -> MyApp.I18ndemoWeb.gettext ("test") einde
We hebben de grondbeginselen geleerd van het gebruik van Gettext met Phoenix, dus het is tijd om iets complexere dingen te bespreken. pluralization is een van hen. Kortom, werken met meervoudige en enkelvoudige vormen is een veel voorkomende maar mogelijk complexe taak. Dingen zijn min of meer duidelijk in het Engels als je "1 appel", "2 appels", "9000 appels" enz. Hebt (hoewel "1 os", "2 ossen"!).
Helaas zijn de regels in sommige andere talen, zoals Russisch of Pools, ingewikkelder. Bijvoorbeeld, in het geval van appels, zou je zeggen "1 яблоко", "2 яблока", "9000 яблок". Maar gelukkig voor ons heeft Phoenix een Gettext.Plural
gedrag (u kunt het gedrag in actie zien in een van mijn vorige artikelen) dat veel verschillende talen ondersteunt. Daarom is alles wat we moeten doen, gebruik maken van de ngettext
functie.
Deze functie accepteert drie vereiste argumenten: een tekenreeks in enkelvoud, een tekenreeks in meervoud en aantal. Het vierde argument is optioneel en kan bindingen bevatten die in de vertaling moeten worden geïnterpoleerd.
Laten we eens kijken ngettext
in actie door te zeggen hoeveel geld de gebruiker heeft door het te wijzigen demo / lib / demo_web / templates / page / index.html.eex
het dossier:
<%= ngettext "You have one buck. Ow :(", "You have %count bucks", 540 %>
% Count
is een interpolatie die zal worden vervangen door een nummer (540
in dit geval). Vergeet niet om de sjabloon en alle PO-bestanden bij te werken na het toevoegen van de bovenstaande reeks:
mix gettext.extract --merge
Je zult zien dat een nieuw blok aan beide is toegevoegd default.po
bestanden:
msgstr "U hebt één buck. Ow :(" msgid_plural "U hebt% count bucks" msgstr [0] "" msgstr [1] ""
We hebben hier niet één maar twee sleutels tegelijk: in enkelvoud en in meervoudige vormen. msgstr [0]
zal wat tekst bevatten om weer te geven als er maar één bericht is. msgstr [1]
, bevat natuurlijk de tekst die moet worden weergegeven als er meerdere berichten zijn. Dit is oké voor Engels, maar niet genoeg voor Russisch, waar we een derde geval moeten introduceren:
msgstr "U hebt één buck. Ow :(" msgid_plural "U hebt% count bucks" msgstr [0] "У 1 доллар. Маловато будет!" msgstr [1] "У вас% count доллара" msgstr [2 ] "У вас% count долларов"
Geval 0
wordt gebruikt voor 1 dollar en koffer 1
voor nul of weinig geld. Geval 2
wordt anders gebruikt.
Een ander onderwerp dat ik wilde bespreken in dit artikel is gewijd aan domeinen. Zoals we al weten, worden domeinen gebruikt om vertalingen te maken, voornamelijk in grote applicaties. In principe gedragen ze zich als namespaces.
U kunt immers in een situatie belanden dat dezelfde sleutel op meerdere plaatsen wordt gebruikt, maar een beetje anders moet worden vertaald. Of wanneer u veel te veel vertalingen in één exemplaar hebt default.po
bestand en zou ze op de een of andere manier willen splitsen. Dat is wanneer domeinen erg handig kunnen zijn.
Gettext ondersteunt meerdere domeinen uit de verpakking. Het enige dat u hoeft te doen is gebruik maken van de dgettext
functie, die bijna hetzelfde werkt als gettext
. Het enige verschil is dat het de domeinnaam als het eerste argument accepteert. Laten we bijvoorbeeld een meldingsdomein introduceren bij, nou, display-meldingen. Voeg nog drie regels code toe aan de demo / lib / demo_web / templates / page / index.html.eex
het dossier:
<%= dgettext "notifications", "Heads up: %msg", msg: "something has happened!" %>
Nu moeten we nieuwe POT- en PO-bestanden maken:
mix gettext.extract --merge
Nadat het script klaar is met zijn werk, notifications.pot
evenals twee notifications.po
bestanden worden aangemaakt. Merk nogmaals op dat ze naar het domein zijn genoemd. Het enige wat je nu hoeft te doen is een vertaling toevoegen voor de Russische taal door het wijzigen van de priv / ru / LC_MESSAGES / notifications.po
het dossier:
msgid "Heads up:% msg" msgstr "Внимание:% msg"
Wat als u een bericht dat is opgeslagen onder een bepaald domein wilt pluraliseren? Dit is zo simpel als het gebruik van een dngettext
functie. Het werkt net als ngettext
maar accepteert ook de naam van een domein als eerste argument:
dgettext "domain", "Singular string% msg", "Meervoudig tekenreeks% msg", 10, msg: "demo"
In dit artikel hebben we gezien hoe we internationalisering in een Phoenix-toepassing konden introduceren met behulp van Gettext. Je hebt geleerd wat Gettext is en met wat voor soort bestanden het werkt. We hebben deze oplossing in actie, hebben met PO- en POT-bestanden gewerkt en verschillende Gettext-functies gebruikt.
We hebben ook een manier gezien om ondersteuning voor meerdere locaties toe te voegen en een manier toegevoegd om eenvoudig tussen deze locaties te schakelen. Ten slotte hebben we gezien hoe pluralisatieregels kunnen worden toegepast en hoe vertalingen kunnen worden gemaakt met behulp van domeinen.
Hopelijk was dit artikel nuttig voor u! Als u meer wilt weten over Gettext in het Phoenix-framework, kunt u de officiële gids raadplegen, die handige voorbeelden en API-referentie biedt voor alle beschikbare functies..
Ik dank u voor uw aanwezigheid bij mij en tot binnenkort!