Skip to content
← Wszystkie wpisy
5 min czytania Dawid Skłodowski

Internacjonalizacja sklepu Rails dla wielu rynków

Sprzedaż w wielu krajach oznacza tłumaczenie nie tylko interfejsu, ale danych, URL-i, formatów i SEO. Deep-dive o i18n w Rails przy skali — pliki lokalizacji, tłumaczona treść, routing lokalny i pułapki.

Internacjonalizacja aplikacji to jedno z tych zadań, które wszyscy niedoceniają. „Przetłumacz etykiety” to może 20% pracy; reszta to dane, URL-e, formaty liczb i dat, reguły liczby mnogiej i SEO — każde z własnymi pułapkami. Dla sklepu e-commerce sprzedającego na wielu rynkach zrobienie tego dobrze to różnica między poczuciem lokalności w każdym kraju a poczuciem niezdarnego tłumaczenia maszynowego. Oto co naprawdę obejmuje pełna robota w Rails.

Fundament: I18n i pliki lokalizacji

Rails dostarcza gem I18n, a podstawowy mechanizm jest solidny: tekst żyje w plikach lokalizacji kluczowanych lokalizacją, a wyszukujesz go po kluczu:

# config/locales/en.yml
en:
  cart:
    title: "Your basket"
    item_count:
      one: "%{count} item"
      other: "%{count} items"

# config/locales/pl.yml
pl:
  cart:
    title: "Twój koszyk"
    item_count:
      one: "%{count} produkt"
      few: "%{count} produkty"
      many: "%{count} produktów"
<h1><%= t("cart.title") %></h1>
<p><%= t("cart.item_count", count: @cart.size) %></p>

Dwie rzeczy do natychmiastowego przyswojenia. Po pierwsze, liczba mnoga to nie tylko one/other. Angielski ma dwie formy; polski ma cztery (one/few/many/other) z nieoczywistymi regułami; arabski ma sześć. Interpolacja count: i klucze liczby mnogiej per lokalizacja obsługują to, jeśli dostarczysz właściwe formy — wpisanie na sztywno logiki „1 item / N items” w widokach psuje się w chwili, gdy dodasz język słowiański. Pozwól maszynerii liczby mnogiej I18n to zrobić. Po drugie, trzymaj klucze semantyczne (cart.title), nie wyprowadzone z tekstu, by tłumacze i kod pozostali rozdzieleni.

Tłumaczenie danych, nie tylko chromu

Oto część, która oddziela prawdziwą internacjonalizację od przetłumaczonego paska menu: w sklepie treść też potrzebuje tłumaczenia. Nazwy produktów, opisy, nazwy kategorii, strony CMS — to dane w twojej bazie, nie łańcuchy w pliku YAML, i potrzebują wartości per lokalizacja. Częstą odpowiedzią Rails jest gem jak globalize albo mobility przechowujący tłumaczenia w tabelach pobocznych:

class Product < ApplicationRecord
  translates :name, :description     # name/description różnią się lokalizacją
end

I18n.locale = :pl
product.name        # => polska nazwa
I18n.locale = :en
product.name        # => angielska nazwa

To prawdziwa decyzja modelowania danych z konsekwencjami: tłumaczenia żyją w osobnych tabelach kluczowanych rekordem i lokalizacją, co wpływa na twoje zapytania, indeksowanie wyszukiwania (indeksujesz teraz per lokalizacja) i UI admina (edytorzy muszą wpisywać każdy język). Zaplanuj to — tłumaczenie treści jest zwykle największą i najbardziej ciągłą częścią i18n, bo każdy nowy produkt potrzebuje tłumaczenia, na zawsze, podczas gdy łańcuchy interfejsu pisze się raz.

Lokalizacja w URL: routing i SEO

To, jak wybierana jest lokalizacja odwiedzającego i odzwierciedlana w URL, to zarówno decyzja UX, jak i SEO, a zrobienie tego źle po cichu rujnuje twoją widoczność w wyszukiwarce. Solidne wzorce umieszczają lokalizację w URL, by każdy język miał własne adresowalne, indeksowalne strony:

# oparte na ścieżce: /en/products, /pl/products
scope "/:locale", locale: /en|pl|de/ do
  resources :products
end

# ustaw z URL na każdym żądaniu
before_action :set_locale
def set_locale
  I18n.locale = params[:locale] || I18n.default_locale
end

Alternatywami są subdomena lokalizacji (pl.shop.com) albo domena krajowa (shop.pl) — właściwy wybór zależy od tego, jak wyraźnie chcesz brandować rynki i jak ustrukturyzowane jest twoje SEO. Czego nie wolno robić, to serwować różnych języków pod tym samym URL na podstawie samego ciasteczka czy nagłówka Accept-Language: wyszukiwarki nie mogą wtedy indeksować każdego języka osobno, a użytkownik nie może udostępnić linku w swoim języku. Nienegocjowalnym wymogiem SEO są tagi hreflang mówiące wyszukiwarkom, który URL serwuje który język, by Google pokazał właściwy i nie traktował twoich tłumaczeń jako zduplikowanej treści:

<link rel="alternate" hreflang="en" href="https://shop.com/en/product/42" />
<link rel="alternate" hreflang="pl" href="https://shop.com/pl/product/42" />

Formaty: liczby, daty i pieniądze też są lokalizowane

Rynek nie czuje się lokalny, dopóki formaty nie są właściwe. 1,234.56 w UK to 1 234,56 w Polsce i 1.234,56 w Niemczech; kolejność daty, umieszczenie symbolu waluty i pierwszy dzień tygodnia wszystkie się różnią. Rails lokalizuje to też przez I18n:

l(Date.today, format: :long)        # zlokalizowana data
number_to_currency(price)           # szanuje format lokalizacji

To zazębia się z pracą wielowalutową: waluta (co pobierasz) i format (jak wyświetlasz liczbę) to osobne sprawy, obie świadome lokalizacji. Niemiecki klient może płacić w EUR pokazanym jako 19,99 €, podczas gdy francuski widzi 19,99 € z innymi odstępami — ta sama waluta, inne formatowanie, oba napędzane lokalizacją.

Pułapki warte nazwania

Kilka trybów awarii, które gryzą zespoły robiące to po raz pierwszy:

  • Brakujące tłumaczenia zawodzące po cichu lub głośno. Zdecyduj swoją politykę: na produkcji zwykle chcesz rozsądnego fallbacku (do domyślnej lokalizacji), a nie surowego klucza pokazanego użytkownikowi; w developmencie i CI chcesz, by brakujące klucze zawodziły, byś łapał je przed wysyłką. Gem i18n-tasks znajduje brakujące i nieużywane klucze.
  • Sklejanie przetłumaczonych fragmentów. Budowanie zdania przez łączenie przetłumaczonych słów (t(:you_have) + count + t(:items)) produkuje bełkot w językach o innej kolejności słów. Tłumacz całe frazy z interpolacją, nigdy nie składaj ich z części.
  • Wpisany na sztywno angielski we flashach, mailerach, walidacjach i PDF-ach. Łańcuchy ukrywają się w kątach — komunikaty błędów, maile, generowane dokumenty. Prawdziwy przegląd i18n musi dosięgnąć ich wszystkich, nie tylko widoków.
  • Rozszerzanie tekstu w UI. Niemiecki jest ~30% dłuższy niż angielski; przycisk, który mieści się po angielsku, przepełnia się po niemiecku. Projektuj i testuj układy z najdłuższym językiem.

Werdykt

Internacjonalizacja sklepu to znacznie więcej niż tłumaczenie etykiet — to tłumaczenie twoich danych, strukturyzowanie twoich URL-i zarówno dla użytkowników, jak i wyszukiwarek, lokalizowanie każdego formatu, szanowanie liczby mnogiej każdego języka i wyganianie wpisanego na sztywno angielskiego z każdego kąta. Oprzyj się na maszynerii I18n Rails dla interfejsu i formatów, użyj gemu do tłumaczeń treści bazy, umieść lokalizację w URL z hreflang dla SEO i przyjmij narzędzia (i18n-tasks), by trzymać swoje klucze uczciwymi. Potraktuj tłumaczenie treści jako ciągły koszt, którym jest, zaplanuj je w modelu danych, a twój sklep może czuć się naprawdę lokalny na każdym rynku, zamiast jak angielski z podmienionymi słowami. Praca jest znacząca, ale dla biznesu sprzedającego za granicę to różnica między wyglądaniem na rodzimego a wyglądaniem na obcego.