Marketing międzynarodowy

Internacjonalizacja w Railsach: od adresów URL po JavaScript

Internacjonalizacja w Railsach: od adresów URL po JavaScript
Zaktualizowano dnia
18 maja 2026 r.

Rails zawiera wbudowany framework do internacjonalizacji. Aby zacząć, nie potrzeba wiele więcej – interfejs API I18n jest częścią tego środowiska i dobrze radzi sobie z podstawowymi zadaniami: tłumaczeniem ciągów statycznych, formatowaniem dat i liczb oraz obsługą liczby mnogiej w różnych ustawieniach regionalnych.

Jednak „wbudowane” nie oznacza „kompletne”. Gdy tylko Twoje wymagania obejmą zlokalizowane adresy URL, tłumaczenia w JavaScript lub wielojęzyczne SEO, wykraczasz poza to, co Rails oferuje w standardzie. Te luki wymagają przemyślanych decyzji architektonicznych.

Przyjrzyjmy się obu warstwom: temu, czym zajmuje się interfejs API Rails I18n, oraz temu, co trzeba zaimplementować samodzielnie – lub zlecić innym.

Zakres funkcji Rails I18n i jej ograniczenia

Interfejs API I18n udostępnia dwie podstawowe metody: I18n.t do tłumaczenia ciągów znaków oraz I18n.l do lokalizacji dat i liczb. Obie metody odczytują dane z plików YAML lub Ruby znajdujących się w katalogu config/locales/, a Rails ładuje je automatycznie podczas uruchamiania.

W standardowej konfiguracji obejmuje to statyczne ciągi tekstowe interfejsu użytkownika, formatowanie dat i liczb oraz komunikaty walidacyjne ActiveRecord. W przypadku większości projektów, w których występuje tylko jeden język źródłowy i jeden język docelowy, to wystarczy do wydania.

Gdy tylko się oddalisz, luki szybko stają się widoczne.

Rails I18n nie reguluje struktury adresów URL: nie obsługuje zlokalizowanych tras, wykrywania subdomen ani prefiksów ścieżek. Nie posiada mechanizmu tłumaczenia treści przechowywanych w bazie danych, takich jak nazwy produktów czy wpisy na blogu. Pliki JavaScript pozostają całkowicie poza tym procesem. Natomiast wielojęzyczne SEO – obejmujące takie elementy jak tagi hreflang, przetłumaczone slugi oraz mapy witryn dostosowane do poszczególnych języków – wymaga osobnych działań, których framework nie obsługuje.

Zanim zaczniesz budować, musisz wiedzieć, gdzie przebiega granica.

Tworzenie podstaw dla międzynarodowej lokalizacji

Możesz zacząć od gemu rails-i18n.

Rails dostarcza dane tylko dla ustawień regionalnych języka angielskiego, więc bez nich wyrażenie `I18n.l(Date.today)` nie działa poprawnie w ustawieniach regionalnych innych niż angielskie, a ciągi znaków na poziomie frameworka, takie jak komunikaty walidacyjne Active Record, pozostają w języku angielskim niezależnie od aktualnych ustawień regionalnych.

# Gemfile

gem „rails-i18n”

Następnie skonfiguruj domyślne ustawienia aplikacji:

# config/application.rb

config.i18n.default_locale = :en

config.i18n.dostępne_języki = [:en, :fr]

config.i18n.enforce_available_locales = true

Ustawienie `enforce_available_locales = true` powoduje wygenerowanie błędu, jeśli aplikacja próbuje ustawić lokalizację spoza tej listy. Bez tego ustawienia literówka lub nieprawidłowy parametr adresu URL może w środowisku produkcyjnym po cichu spowodować ustawienie nieobsługiwanej lokalizacji.

Chociaż nowoczesna wersja Railsów przeszukuje tylko jeden poziom głębokości, często pomija głęboko zagnieżdżone foldery (takie jak config/locales/views/products/). Dodanie tego wiersza gwarantuje, że wraz z rozwojem aplikacji uwzględnione zostaną wszystkie podkatalogi:

config.i18n.load_path += Dir[Rails.root.join("config", "locales", "**", „*.{rb,yml}”)]

Dzięki temu można podzielić pliki tłumaczeń według domen:

config/locales/  
	en/    
		models.yml    
		widoki.yml    
		mailers.yml  
	fr/    
		modele.yml    
		widoki.yml    
		mailers.yml

⚠️ YAML interpretuje wartości takie jak „true” i „false” jako wartości logiczne. Jeśli chcesz, aby były traktowane jako zwykły tekst, należy je wyraźnie ująć w cudzysłowy. Nowsze wersje Ruby/YAML traktują obecnie „yes” i „no” jako ciągi znaków, jednak umieszczanie ich w cudzysłowach pozostaje bezpiecznym nawykiem ze względu na kompatybilność:

pl:  
	odpowiedzi:    
		tak: „tak”    
		negatywne: „nie”

W ten sposób wykrywa się pewien rodzaj błędu, który ujawnia się tylko wtedy, gdy funkcja tłumaczenia zwraca wartość „true” zamiast „yes”, a widok nie wyświetla niczego lub – co gorsza – wyświetla ciąg znaków „true”.

Narzędzia do tłumaczenia i funkcja „Lazy Lookup” w widokach

W widokach t i l to dwa elementy pomocnicze, z których będziesz korzystać na co dzień.

l dostosowuje daty, godziny i liczby zgodnie z bieżącą lokalizacją. Po zainstalowaniu biblioteki rails-i18n dostępne są definicje formatów dla ponad 100 lokalizacji. Bez tej biblioteki wywołanie funkcji l(Date.today) w lokalizacji innej niż angielska zazwyczaj zwraca komunikat „brak tłumaczenia” lub nieformatowaną datę, ponieważ Rails nie dysponuje niezbędnymi wzorcami formatowania dostosowanymi do danej lokalizacji.

<%= l(Date.today) %>

<%= l(Date.today, format: :long) %>

t tłumaczy ciągi znaków według klucza. Pełna forma to t("products.index.title"), ale w widokach można użyć skrótu z odroczonym wyszukiwaniem:

<%= t(".title") %>

Kropka na początku klucza informuje Railsy, aby rozpoznały go względem bieżącej ścieżki widoku. W pliku app/views/products/index.html.erb wyrażenie t(".title") rozwijane jest automatycznie na t("products.index.title"). Dzięki temu klucze pozostają krótkie, a spójna konwencja nazewnictwa jest przestrzegana bez dodatkowego wysiłku.

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) %>

Jeśli tłumaczenie zawiera kod HTML, któremu ufasz, dodaj do nazwy klucza rozszerzenie _html. Rails automatycznie oznaczy go jako bezpieczny, bez konieczności wywoływania metody html_safe.

en:  
	notice_html: "Please <strong>confirm</strong> your email."

Leniwe wyszukiwanie działa tylko wtedy, gdy Rails może wywnioskować ścieżkę widoku, co oznacza, że nie działa w zadaniach wykonywanych w tle ani w kontrolerach API. W tych przypadkach należy za każdym razem używać pełnego klucza.

Wybór strategii wykrywania ustawień regionalnych

Zanim zaczniesz pisać kod do wykrywania ustawień regionalnych, wybierz strategię. Istnieje pięć głównych podejść, a wybór odpowiedniego zależy od struktury Twojej aplikacji oraz wymagań dotyczących SEO.

Strategia Przykład Najlepsze dla
Parametr adresu URL /products?locale=fr Narzędzia wewnętrzne, szybkie prototypowanie
Prefiks ścieżki /fr/produkty Strony publiczne wymagające wielojęzycznego pozycjonowania
Subdomena fr.example.com Charakterystyczne marki regionalne
TLD example.fr Domeny krajowe, które już posiadasz
Preferencje bazy danych Zapisane w rekordzie użytkownika Zweryfikowane aplikacje z ustawieniami użytkownika

Prefiks ścieżki, np. /fr/products, jest najczęściej wybieranym rozwiązaniem w aplikacjach Railsowych przeznaczonych dla użytkowników zewnętrznych. Dzięki temu w każdym adresie URL wyraźnie wskazane jest ustawienie regionalne, co pozwala wyszukiwarkom indeksować strony osobno dla poszczególnych języków.

Strategie oparte na subdomenach i domenach najwyższego poziomu sprawdzają się, gdy zależy nam na silniejszej tożsamości regionalnej, ale wiążą się one z dodatkowymi nakładami związanymi z konfiguracją DNS i certyfikatami SSL.

Parametry adresów URL, takie jak /products?locale=fr, sprawdzają się w przypadku narzędzi wewnętrznych, gdzie pozycjonowanie stron (SEO) nie ma znaczenia.

Ustawienia zapisane w bazie danych działają w przypadku zalogowanych aplikacji, w których ustawienia regionalne są dostosowywane do użytkownika, a nie do adresu URL.

Niezależnie od tego, którą strategię wybierzesz, istnieje jeden błąd wdrożeniowy, który powoduje subtelne błędy w środowisku produkcyjnym: bezpośrednie użycie `I18n.locale =`.

# Don't do this

before_action { I18n.locale = params[:locale] }

I18n.locale = zapisuje do Thread.current.

Jeśli korzystasz z serwera internetowego Puma, wykorzystuje on te same wątki dla różnych żądań. Jeśli w żądaniu nie podano wyraźnie ustawień regionalnych, przejmuje ono ustawienia z poprzedniego żądania.

W środowisku lokalnym, w którym działa tylko jeden wątek, problem ten nigdy nie występuje. W środowisku produkcyjnym powoduje to sporadyczne wyświetlanie odpowiedzi w niewłaściwym języku, które trudno odtworzyć, a jeszcze trudniej zlokalizować.

Zamiast tego użyj funkcji `I18n.with_locale` w akcji typu `around_action`:

around_action :switch_locale‍d


ef switch_locale(&action)  
	locale = params[:locale] || I18n.default_locale  
	I18n.with_locale(locale, &action)
koniec

Funkcja `with_locale` przywraca poprzednie ustawienia regionalne po zakończeniu bloku, niezależnie od tego, jak zakończy się żądanie.

Wdrażanie zlokalizowanych tras i wykrywanie subdomen

Zdefiniuj trasy w katalogu /:locale i nadpisz ustawienia default_url_options, aby Rails automatycznie dodawał bieżącą lokalizację na początku każdego adresu URL generowanego przez pomocniki:

# 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

Po ustawieniu parametru `default_url_options` ścieżka `products_path` jest automatycznie renderowana jako `/fr/products`, gdy bieżącym ustawieniem regionalnym jest :fr.

Jeśli chcesz, aby domyślna lokalizacja pomijała przedrostek, ustaw segment jako opcjonalny, używając zakresu „(:locale)”. Dzięki temu adres /products będzie wyświetlał stronę w języku angielskim, a /fr/products – w języku francuskim, ale spowoduje to niejasność. Ścieżka taka jak /about może odpowiadać albo brakującej lokalizacji, albo kontrolerowi o nazwie „about”, w zależności od kolejności tras.

Aby wykryć subdomenę, przed dokonaniem jakichkolwiek ustawień należy odczytać wartość z `request.subdomains.first` i porównać ją z listą `available_locales`:

def wyodrębnij_język_z_poddomeny
  subdomain = request.subdomains.first
  zwróć nil jeśli subdomain.blank? || subdomain == "www"
  subdomain jeśli I18n.available_locales.map(&:to_s).include?(subdomain)
end

Sprawdzenie „www” powoduje, że nie jest on traktowany jako ustawienie regionalne. Na serwerze localhost funkcja request.subdomains zwraca pustą tablicę, więc wykrywanie subdomen nie działa w środowisku deweloperskim, chyba że używasz serwera Pow lub dodasz wpisy do pliku /etc/hosts.

Jedną rzecz należy skonfigurować osobno: opcja `default_url_options` zdefiniowana w klasie `ApplicationController` nie jest przenoszona do modułów wysyłających wiadomości e-mail ani zadań wykonywanych w tle. Należy ją ustawić wyraźnie dla tych kontekstów:

Rails.application.routes.default_url_options = { host: "example.com", locale: :en }

Zlokalizowane trasy zapewniają strukturę adresów URL, jednak po tej konfiguracji tagi hreflang, przetłumaczone slugi oraz wielojęzyczne mapy witryny nadal wymagają ręcznej obsługi.

Integracja z serwerem proxy odwróconym Weglot automatycznie zajmuje się tą warstwą, generując tagi hreflang i adresy URL dostosowane do konkretnego języka bez konieczności dodatkowej konfiguracji.

Pluralizacja, rozwiązania awaryjne i gem rails-i18n

Tworzenie liczby mnogiej w języku angielskim jest proste. Jedna forma dla liczby pojedynczej, jedna dla wszystkich pozostałych przypadków.

en:
  messages:
    one: "%{count} message"
    other: "%{count} messages"

Większość języków nie jest aż tak prosta.

Na przykład w języku bułgarskim niektóre słowa w rodzaju męskim mają regularną liczbę mnogą oraz specjalną „liczbę mnogą liczbową”. Tak więc słowo ден (dzień) w normalnym użyciu oznacza дни (wiele dni), ale w przypadku liczenia brzmi два дена (dwa dni).

bg:
  cities:
    one: "%{count} ден"
    few: "%{count} дена"
    many: "%{count} дни"

Bez modułu rails-i18n framework Rails nie ma wiedzy na temat tych reguł. Twoje bułgarskie tłumaczenia będą generować błędy lub przy każdym liczeniu będą przechodzić na alternatywną formę.

Gem obsługuje logikę tworzenia liczby mnogiej dla ponad 100 ustawień regionalnych, dzięki czemu nazwy wyświetlają się poprawnie w języku bułgarskim, rosyjskim, arabskim, polskim i każdym innym języku, w którym zasady tworzenia liczby mnogiej odbiegają od angielskich.

Rozwiązania awaryjne to osobna kwestia i domyślnie są wyłączone. Bez nich każdy brakujący klucz tłumaczenia powoduje wyświetlenie komunikatu o braku tłumaczenia w środowisku produkcyjnym. To właśnie tego typu sytuacje trafiają do użytkowników i pojawiają się na zrzutach ekranu.

Włącz opcje awaryjne i zdefiniuj domyślne miejsce docelowe w pliku config/application.rb:

config.i18n.fallbacks = [I18n.default_locale]

Gdy ustawiono opcję `fallbacks = true`, brakujący klucz w bieżącej lokalizacji jest zastępowany wartością z `default_locale`. W przypadku wariantów regionalnych można zdefiniować łańcuchy w sposób jawny:

config.i18n.fallbacks = { "fr-CA": :fr, "en-GB": :en }

Zadbaj o to na samym początku. Wprowadzenie mechanizmu awaryjnego do aplikacji, która jest już częściowo przetłumaczona i działa w środowisku produkcyjnym, jest trudniejsze niż skonfigurowanie go od samego początku.

Przekazywanie tłumaczeń do kontrolerów Stimulus i JavaScript

JavaScript nie ma dostępu do potoku I18n w Railsach.

Nie ma tu żadnego modułu pomocniczego typu t, modułu ładującego YAML ani wbudowanego pomostu między plikami tłumaczeń a kontrolerami Stimulus. Tłumaczenia trzeba przekazywać jawnie po stronie serwera.

Najprostszym sposobem korzystania z biblioteki Stimulus jest użycie interfejsu API wartości. Zdefiniuj tłumaczenie jako wartość w kontrolerze, ustaw je w kodzie HTML i odczytaj w JavaScript:

# app/views/products/index.html.erb
<div data-controller="notification"
     data-notification-message-value="<%= t('.success_message') %>">

Wartość ta jest renderowana po stronie serwera przy użyciu standardowego pomocnika `t`, dzięki czemu automatycznie dostosowuje się do bieżących ustawień regionalnych. Po stronie JavaScript należy zadeklarować ją jako wartość statyczną i odczytywać za pomocą interfejsu API wartości:

// app/javascript/controllers/notification_controller.js
export default class extends Controller {
  static values = { message: String }

  show() {
    alert(this.messageValue)
  }
}

Każde tłumaczenie jest jednoznaczne i ograniczone do kontrolera, który go potrzebuje. Żadne dane nie przedostają się do zakresu globalnego.

Jeśli kontroler potrzebuje kilku tłumaczeń jednocześnie, zebranie ich w postaci atrybutu danych JSON jest bardziej przejrzystym rozwiązaniem niż dodawanie osobnych definicji wartości dla każdego ciągu znaków:

<div data-controller="cart"
     data-cart-i18n-value="<%= { add: t('.add'), remove: t('.remove') }.to_json %>">

W JavaScript należy zadeklarować i18n jako obiekt i uzyskiwać bezpośredni dostęp do poszczególnych kluczy:

static values = { i18n: Object }

add() {
  console.log(this.i18nValue.add)
}

W przypadku aplikacji, w których JavaScript wymaga szerokiego dostępu do tłumaczeń w wielu kontrolerach, gem i18n-js eksportuje pliki YAML do obiektu JavaScript, z którego można pobierać dane za pomocą wyrażenia I18n.t("key").

To działa, ale wymaga dodatkowego etapu kompilacji i powoduje wysłanie pełnego zestawu tłumaczeń do klienta. Należy z tego korzystać, gdy pierwsze dwa schematy stają się powtarzalne, a nie jako domyślne rozwiązanie.

W przypadku korzystania z Turbo Frames, jeśli adres URL źródła ramki Turbo Frame nie zawiera przedrostka lokalizacji, żądanie zazwyczaj zakończy się niepowodzeniem z błędem routingu (404), ponieważ nie będzie pasowało do wzorca zakresu „/:locale”. W źródłach ramek należy zawsze stosować pomocniki ścieżek uwzględniające lokalizację.

To nie jest błąd Turbo. Jest to raczej przeoczenie związane z routingiem, a akcja `around_action` z sekcji wykrywania ustawień regionalnych zapobiega temu, o ile każdy adres URL w ramce korzysta z pomocnika ścieżki uwzględniającego ustawienia regionalne.

Proxy odwrotne Weglot działa na zupełnie innej zasadzie. Przetwarza ono wyrenderowany model DOM po załadowaniu strony, dzięki czemu treści wyrenderowane przez Stimulus oraz dynamicznie wstawiane ciągi znaków są obsługiwane bez konieczności stosowania jakichkolwiek dodatkowych konfiguracji.

Czego brakuje w module Rails I18n

Istnieją trzy kategorie, które całkowicie wykraczają poza zakres obsługi funkcji Rails I18n:

  • Wielojęzyczne SEO w Railsach: i18n tłumaczy ciągi znaków, ale nie generuje tagów hreflang, map witryn dostosowanych do poszczególnych języków ani przetłumaczonych metadanych. Zadania te wymagają ręcznej pracy wykraczającej poza konfigurację i18n.
  • Treści przechowywane w bazie danych, takie jak nazwy produktów i wpisy na blogu, nie mogą być zapisywane w plikach YAML. W tym celu zaleca się użycie biblioteki Mobility, ponieważ obsługuje ona wiele różnych systemów baz danych. Traco stanowi lżejszą alternatywę dla mniejszych modeli z tłumaczeniami dla poszczególnych kolumn.
  • Proces tłumaczenia w Railsach opiera się na plikach YAML. Zarządzanie procesem tłumaczenia, cyklami weryfikacji, aktualizacjami oraz synchronizacją tłumaczeń z aplikacją musi odbywać się poza samym frameworkiem.

Weglot rozwiązuje wszystkie te kwestie na poziomie warstwy wyjściowej, a nie warstwy kodu:

  • Zamiast integrować się z potokiem I18n w Railsach, tłumaczy on renderowany kod HTML generowany przez aplikację. Oznacza to, że treści z bazy danych, ciągi znaków renderowane przez JavaScript oraz tłumaczenia statyczne są obsługiwane w ten sam sposób.
  • W pakiecie znajduje się wielojęzyczne SEO/GEO: tagi hreflang, przetłumaczone adresy URL oraz mapy witryn są generowane automatycznie.
  • Proces tłumaczenia odbywa się za pośrednictwem panelu administracyjnego Weglot, a nie poprzez cykle eksportu plików YAML.

Warto jednak pamiętać o kilku ograniczeniach, zanim przystąpisz do oceny:

  • Ten snippet JavaScript nie jest indeksowany przez wyszukiwarki; aby przetłumaczone strony pojawiały się w wynikach wyszukiwania, konieczne jest skonfigurowanie serwera proxy odwrotnego.
  • Funkcja niestandardowego przekierowywania do podkatalogów, przydatna w przypadku istniejącej konfiguracji CDN lub Nginx, jest dostępna wyłącznie w wersji Enterprise.
  • W przeciwieństwie do tłumaczeń wbudowanych w kod źródłowy, Weglot aktywnej subskrypcji, aby przetłumaczone treści były widoczne.

Wybór architektury i18n dla Railsów

Każda implementacja i18n w Railsach sprowadza się do tych samych decyzji podejmowanych mniej więcej w tej samej kolejności.

Najpierw należy wybrać strategię adresów URL przed rozpoczęciem pisania kodu do wykrywania ustawień regionalnych. Segmenty ścieżki sprawdzają się w większości aplikacji przeznaczonych dla użytkowników zewnętrznych. Poddomeny mają sens, gdy istotna jest tożsamość regionalna. Preferencje zapisane w bazie danych nadają się do aplikacji wymagających uwierzytelnienia, w których ustawienia regionalne są przypisane do użytkownika, a nie do adresu URL.

Po drugie, należy zdecydować, w jaki sposób JavaScript ma pobierać tłumaczenia. Interfejs API wartości Stimulus obejmuje większość przypadków. Atrybuty JSON sprawdzają się, gdy kontroler potrzebuje kilku ciągów znaków jednocześnie.

Po trzecie, zdecyduj, co chcesz tworzyć samodzielnie. Rails dobrze radzi sobie ze statycznymi ciągami znaków. Warstwa SEO, proces tłumaczenia oraz zawartość bazy danych wymagają osobnych decyzji. Możesz zająć się każdym z tych elementów samodzielnie lub powierzyć obsługę warstwy wyjściowej narzędziu takiemu jak Weglot poświęcić czas programistów na samą aplikację.

Zrezygnuj całkowicie z warstwy wyjściowej. Wypróbuj Weglot przez 14 dni i zyskaj dostęp do wielojęzycznego SEO/GEO, przetłumaczonych adresów URL oraz automatycznego wykrywania treści bez konieczności ingerowania w kod i18n.

ikona kierunku
Odkryj Weglot

Dołącz do ponad 110 000 marek, które już tłumaczą swoje strony z Weglot

Przetłumacz swoją stronę internetową błyskawicznie dzięki sztucznej inteligencji, dopracuj ją z pomocą ludzi i uruchom w kilka minut.

W tym artykule przyjrzymy się:
Ikona rakiety

Gotowi, żeby zacząć?

Najlepszym sposobem, aby zrozumieć potęgę Weglot wypróbowanie go samodzielnie. Wypróbuj go za darmo i bez żadnych zobowiązań.

Jeśli nie jesteś jeszcze gotowy, aby połączyć swoją stronę internetową, w panelu administracyjnym dostępna jest strona demonstracyjna.

Przeczytaj artykuły, które mogą Ci się spodobać

Ikona FAQ

Częste pytania

Brak wyników.

Niebieska strzałka

Niebieska strzałka

Niebieska strzałka