
Rails leveres med et innebygd internasjonaliseringsrammeverk. Du trenger ikke mye mer for å komme i gang – I18n API er en del av stacken, og den håndterer det grunnleggende godt: oversettelse av statiske strenger, formatering av datoer og tall, og håndtering av pluralisering på tvers av språk.
Men «innebygd» betyr ikke «fullstendig». I det øyeblikket kravene dine strekker seg til lokaliserte URL-er, JavaScript-oversettelser eller flerspråklig SEO, jobber du utenfor det Rails tilbyr innebygd. Disse hullene krever bevisste arkitekturbeslutninger.
La oss gå gjennom begge lagene: hva Rails I18n API håndterer, og hvor du må bygge – eller delegere.
I18n API gir deg to kjernemetoder: I18n.t for å oversette strenger og I18n.l for å lokalisere datoer og tall. Begge leser fra YAML- eller Ruby-filer i config/locales/, og Rails laster dem inn automatisk ved oppstart.
Standard, som dekker statiske UI-strenger, dato- og tallformatering og ActiveRecord-valideringsmeldinger. For de fleste ettspråklige prosjekter er det nok å sende.
Hullene viser seg raskt når du kommer lenger.
Rails I18n har ingen mening om URL-struktur: ingen lokaliserte ruter, underdomenedeteksjon eller stiprefikser. Den har ingen mekanisme for å oversette databaselagret innhold som produktnavn eller blogginnlegg. JavaScript-filer ligger helt utenfor pipelinen. Og flerspråklig SEO, ting som hreflang-tagger, oversatte slugs og språkspesifikke nettstedskart, krever separat arbeid som rammeverket ikke berører.
Du må vite hvor grensen går før du begynner å bygge.
Du kan sette i gang med rails-i18n-juvelen.
Rails sender bare engelskspråklige språkdata, så uten den feiler I18n.l(Date.today) i ikke-engelske språk, og strenger på rammeverksnivå, som valideringsmeldinger for Active Record, forblir på engelsk uavhengig av gjeldende språk.
# Gemfile
perle "rails-i18n"Konfigurer deretter standardinnstillingene for applikasjonen din:
# konfigurasjon/applikasjon.rb
config.i18n.default_locale = :no
config.i18n.available_locales = [:no, :fr]
config.i18n.enforce_available_locales = true
enforce_available_locales = true genererer en feilmelding hvis appen din prøver å angi en språkinnstilling utenfor den listen. Uten den vil en skrivefeil eller en feilformet URL-parameter stille angi en språkinnstilling som ikke støttes i produksjon.
Selv om moderne Rails skanner ett nivå dypt, går den ofte glipp av dypt nestede mapper (som config/locales/views/products/ ). Ved å legge til denne linjen sikrer du at alle underkataloger inkluderes etter hvert som appen din vokser:
config.i18n.load_path += Dir[Rails.root.join( "config" , "locales" , "**" , "*.{rb,yml}" )]Dette lar deg dele oversettelsesfiler etter domene:
config/locales/ en/ models.yml views.yml mailers.yml fr/ models.yml views.yml mailers.yml⚠️ YAML analyserer verdier som sann og usann som boolske verdier. Hvis du trenger dem som bokstavelig tekst, siter dem eksplisitt. Moderne Ruby/YAML-versjoner behandler nå ja og nei som strenger, men det er fortsatt en trygg vane å sitere dem for kompatibilitet:
en: svar: bekreftende: "ja"
negativ : «nei»Dette fanger opp en feilklasse som bare dukker opp når en oversettelse returnerer sann i stedet for «ja», og visningen din ikke gjengir noe, eller enda verre, strengen «sann».
I views er t og l de to hjelperne du vil bruke hele tiden.
l lokaliserer datoer, klokkeslett og tall i henhold til gjeldende språkinnstillinger. Med rails-i18n installert, er formatdefinisjoner for over 100 språkinnstillinger inkludert. Uten den vil kalling av l(Date.today) i en ikke-engelsk språkinnstillinger vanligvis returnere en 'translation missing'-streng eller en uformatert dato, ettersom Rails mangler de nødvendige lokaliserte formatmønstrene.
<%= l(Date.today) %>
<%= l(Date.today, format: :long) %>t oversetter strenger etter nøkkel. Den fullstendige formen er t("products.index.title"), men i views kan du bruke den late oppslagsforkortelsen:
<%= t(".title") %>Det innledende punktet forteller Rails at nøkkelen skal løses i forhold til gjeldende visningsbane. I app/views/products/index.html.erb utvides t(".title") automatisk til t("products.index.title"). Dette holder nøklene korte og håndhever en konsistent navnekonvensjon uten ekstra innsats.
For interpolation, use %{variable} in your YAML and pass the value as a keyword argument:
en:
welcome: "Hello, %{name}"<%= t(".welcome", name: current_user.name) %>Hvis en oversettelse inneholder HTML du stoler på, legg til _html i nøkkelnavnet. Rails markerer den automatisk som sikker, uten at det kreves et html_safe-kall.
en:
notice_html: "Please <strong>confirm</strong> your email."Lat oppslag fungerer bare der Rails kan utlede visningsbanen, noe som betyr at det ikke fungerer i bakgrunnsjobber og API-kontrollere. I slike sammenhenger, bruk hele nøkkelen hver gang.
Før du skriver kode for lokalitetsdeteksjon, bør du velge en strategi. Det finnes fem hovedtilnærminger, og den riktige avhenger av appens struktur og SEO-krav.
Stiprefiks, som /fr/products , er det vanligste valget for offentlig tilgjengelige Rails-apper. Det holder språkinnstillingen eksplisitt i hver URL, som søkemotorer kan indeksere uavhengig per språk.
Strategier for underdomener og toppnivådomener fungerer bra når du ønsker en sterkere regional identitet, men de legger til ekstra DNS-konfigurasjon og SSL-sertifikatkostnader.
URL-parametere, som /products?locale=fr , fungerer fint for interne verktøy der SEO ikke spiller noen rolle.
DB-lagrede preferanser fungerer for autentiserte apper der språkinnstillingene følger brukeren, ikke URL-en.
Uansett hvilken strategi du velger, er det én implementeringsfeil som forårsaker subtile produksjonsfeil: bruk av I18n.locale = direkte.
# Don't do this
before_action { I18n.locale = params[:locale] }I18n.locale = skriver til Thread.current.
Hvis du bruker en Puma- webserver, gjenbruker den tråder på tvers av forespørsler. Hvis en forespørsel ikke eksplisitt angir språkinnstillingene, arver den det den forrige forespørselen etterlot seg.
I lokal utvikling med én enkelt tråd dukker dette aldri opp. I produksjon forårsaker det periodiske feilspråklige svar som er vanskelige å reprodusere og vanskeligere å spore.
Bruk I18n.with_locale i en around_action i stedet:
rundt_handling :bytte_lokal
ef switch_locale(&action) locale = params[:locale] || I18n.default_locale I18n.with_locale(locale, &action) sluttwith_locale gjenoppretter den forrige lokale innstillingen etter at blokken avsluttes, uavhengig av hvordan forespørselen avsluttes.
Omfang rutene dine under /:locale og overstyr default_url_options slik at Rails automatisk legger til gjeldende språk foran alle URL-hjelpere:
# config/routes.rb
scope "/:locale" do
resources :products
root "home#index"
end
# app/controllers/application_controller.rb
around_action :switch_locale
def switch_locale(&action)
locale = params[:locale]
# Fallback to default if the param is missing or unsupported
valid_locale = I18n.available_locales.map(&:to_s).include?(locale) ? locale : I18n.default_locale
I18n.with_locale(valid_locale, &action)
end
def default_url_options
{ locale: I18n.locale }
end
Med default_url_options angitt, gjengis products_path automatisk som /fr/products når gjeldende språkinnstillinger er :fr.
Hvis du vil at standardspråket skal utelate prefikset, gjør segmentet valgfritt med omfanget "(:språk)". Dette betjener /products for engelsk og /fr/products for fransk, men skaper flertydighet. En sti som /about kan samsvare med enten et manglende språk eller en kontroller med navnet about, avhengig av ruterekkefølgen.
For å oppdage underdomener, les fra request.subdomains.first og valider mot available_locales før du angir noe:
def ekstrakt_lokal_fra_underdomene underdomene = forespørsel.underdomener.først returner null hvis subdomene.blank? || subdomene == "www"
underdomene hvis I18n.available_locales.map(&:to_s).include?(underdomene) sluttwww-sjekken hindrer at den blir behandlet som en locale. På localhost returnerer request.subdomains en tom array, så deteksjon av underdomener mislykkes i utvikling med mindre du bruker en Pow-server eller legger til oppføringer i /etc/hosts .
Én ting som må konfigureres separat: default_url_options definert i ApplicationController forplanter seg ikke til e-postutsendelser eller bakgrunnsjobber. Angi det eksplisitt for disse kontekstene:
Rails.application.routes.default_url_options = { host: "example.com", locale: :en }Lokaliserte ruter gir deg URL-strukturen, men hreflang-tagger , oversatte slugs og flerspråklige nettstedskart forblir helt manuelle etter dette oppsettet.
Weglot s reverse proxy-integrasjon håndterer dette laget automatisk, og genererer hreflang-tagger og språkspesifikke URL-er uten ytterligere konfigurasjon.
Engelsk flertall er enkel. Én form for entall, én for alt annet.
en:
messages:
one: "%{count} message"
other: "%{count} messages"
De fleste språk er ikke så enkle.
Bulgarsk har for eksempel en vanlig flertall og en spesiell «telleflertall» for noen ord i hankjønnsform. Så ден (dag) blir vanligvis til дни (mange dager), men til два дена (to dager) når det telles.
bg:
cities:
one: "%{count} ден"
few: "%{count} дена"
many: "%{count} дни"
Uten rails-i18n har Rails ingen kjennskap til disse reglene. Dine bulgarske oversettelser ville enten føre til feil eller falle tilbake til den andre formen for hver telling.
Gem-en sender pluraliseringslogikk for over 100 språkinnstillinger, slik at ting løses riktig på bulgarsk, russisk, arabisk, polsk og alle andre språk med ikke-engelske flertallsregler.
Reserver er et separat problem og er deaktivert som standard. Uten dem vil enhver manglende oversettelsesnøkkel gjengi en manglende oversettelsesstreng i produksjon. Det er den typen ting som sendes og vises i skjermbilder.
Aktiver reserveprogrammer og definer en standarddestinasjon i config/application.rb :
config.i18n.fallbacks = [I18n.default_locale]Med fallbacks = true, faller en manglende nøkkel i gjeldende språk tilbake til default_locale. For regionale varianter kan du definere eksplisitte kjeder:
config.i18n.fallbacks = { "fr-CA": :fr, "en-GB": :en }Konfigurer dette tidlig. Det er vanskeligere å ettermontere reservefunksjoner i en app som allerede har delvise oversettelser i produksjon enn å sette det opp i starten.
JavaScript har ikke tilgang til Rails I18n-pipelinen.
Det finnes ingen t-hjelper, YAML-laster eller innebygd bro mellom oversettelsesfilene dine og Stimulus-kontrollerne dine. Du må sende oversettelser eksplisitt fra serversiden.
Den enkleste tilnærmingen for Stimulus er Values API. Definer oversettelsen som en verdi på kontrolleren, angi den i HTML-koden og les den i JavaScript:
# app/views/products/index.html.erb
<div data-controller="notification"
data-notification-message-value="<%= t('.success_message') %>">
Verdien gjengis på serversiden ved hjelp av standard t-hjelperen, slik at den respekterer gjeldende språkinnstillinger automatisk. På JavaScript-siden, deklarer den som en statisk verdi og les den gjennom verdi-API-et:
// app/javascript/controllers/notification_controller.js
export default class extends Controller {
static values = { message: String }
show() {
alert(this.messageValue)
}
}
Hver oversettelse er eksplisitt og begrenset til kontrolleren som trenger den. Ingenting lekker inn i det globale omfanget.
Hvis en kontroller trenger flere oversettelser samtidig, er det enklere å samle dem som et JSON-dataattributt enn å legge til individuelle verdidefinisjoner for hver streng:
<div data-controller="cart"
data-cart-i18n-value="<%= { add: t('.add'), remove: t('.remove') }.to_json %>">
På JavaScript-siden, deklarer i18n som en objektverdi og få direkte tilgang til individuelle nøkler:
static values = { i18n: Object }
add() {
console.log(this.i18nValue.add)
}
For apper der JavaScript trenger bred oversettelsestilgang på tvers av mange kontrollere, eksporterer i18n-js-gemen YAML-filene dine til et JavaScript-objekt du kan spørre som I18n.t("key").
Det fungerer, men det legger til et byggetrinn og sender hele oversettelsessettet til klienten. Bruk det når de to første mønstrene blir repeterende, ikke som standard.
Når du bruker Turbo Frames, og src-URL-en til en Turbo Frame utelater prefikset for lokalitet, vil forespørselen vanligvis mislykkes med en rutingsfeil (404) fordi den ikke samsvarer med omfangsmønsteret "/:locale". Bruk alltid lokalitetsbevisste stihjelpere for rammekilder.
Dette er ikke en Turbo-feil. I stedet er det en rutingfeil, og around_action fra lokalitetsdeteksjonsdelen forhindrer det så lenge hver URL i rammen bruker en lokalitetsbevisst stihjelper.
Weglot s reverse proxy har en helt annen tilnærming. Den oversetter den gjengitte DOM-en etter at siden lastes inn, slik at stimulus-gjengitt innhold og dynamisk innsatte strenger dekkes uten noe av denne kablingen.
Tre kategorier ligger helt utenfor det Rails I18n håndterer:
Weglot adresserer alle disse på utdatalaget i stedet for kodelaget:
Når det er sagt, er det noen begrensninger som er verdt å vite før du evaluerer det:
Hver Rails i18n-implementering handler om de samme beslutningene som tas i omtrent samme rekkefølge.
Først må du velge URL-strategien din før du skriver kode for lokaliseringsdeteksjon. Banesegmenter fungerer for de fleste offentlige apper. Underdomener gir mening når regional identitet er viktig. DB-lagrede preferanser passer for autentiserte apper der lokaliseringen følger brukeren i stedet for URL-en.
For det andre, bestem hvordan JavaScript får oversettelser. Stimulus-verdi-API-et dekker de fleste tilfeller. JSON-attributter fungerer når en kontroller trenger flere strenger samtidig.
For det tredje, bestem deg for hva du bygger manuelt. Rails håndterer statiske strenger godt. SEO-laget, oversetterens arbeidsflyt og databaseinnholdet krever alle separate beslutninger. Du kan bygge hver enkelt, eller delegere utdatalaget til et verktøy som Weglot og fokusere ingeniørtiden på selve applikasjonen.
Hopp over utdatalaget helt. Prøv Weglot gratis i 14 dager og få flerspråklig SEO/GEO, oversatte URL-er og automatisk innholdsgjenkjenning uten å røre i18n-koden din.
Den beste måten å forstå kraften i Weglot er å se det selv. Test det gratis og uten forpliktelser.
En demo-nettside er tilgjengelig i dashbordet ditt hvis du ikke er klar til å koble til nettstedet ditt ennå.