Wróć do strony głównej
Angular

Internacjonalizacja – Czyli jak otworzyć aplikacje na świat – część 2

Przeczytaj częśc pierwszą

Czym jest pluralizacja?

Popularnym problemem występującym w sferze i18n jest pluralizacja – występuje gdy potrzebujemy wyświetlić inny tekst w zależności od wartości zmiennej. W większości bibliotek służących do internacjonalizacji spotkamy się z implementacją formatu ICU, a co za tym idzie 2 formami pluralizacji: select’a oraz plural’a.

Notka: W przypadku @angular/localize i ngx-translate format ICU jest zaimplementowany domyślnie. Do transloco możemy go dodać instalując paczkę transloco-messageformat.

Plural

Z tego formatu skorzystamy w przypadku, kiedy chcemy wyświetlić tekst w zależności od wartości liczby. W większości języków spotkamy się z 2 formami – liczbą pojedynczą (“singular”) oraz liczbą mnogą “plural”. Warto pamiętać, że niektóre języki mogą mieć więcej form, przykładem tego jest chociażby ukraiński, który ma 4 formy.

Mimo to w naszych aplikacjach najczęściej spotkamy się z sytuacją, w której wyświetlimy tekst w 3 różnych przypadkach:

  • Braku danych
  • Pojedynczego rekordu
  • Wielu rekordów

Syntax plurala:

Jak możemy zobaczyć powyżej, plurala otwieramy za pomocą klamr. Jako 1 argument podajemy nazwę parametru, w naszym przypadku jest to “amount”. Jako 2 argument podajemy typ plurala (do wyboru mamy plural/select). Pozostałe argumenty są warunkami logicznymi.

  •  ={liczba} jest warunkiem, po którym w klamrowych nawiasach podajemy tekst, który będzie zwracany, jeżeli liczba z argumentu będzie taka sama jak liczba w warunku
  • other jest else’m – Tekst wewnątrz niego zostanie pokazany jeżeli żaden inny warunek nie zostanie spełniony
  • W każdym warunku możemy wykorzystać znak # , który jest zamieniany na liczbę, przekazaną w argumencie.

Select

Format typu select jest przydatny jeżeli potrzebujemy wyświetlić tekst, w zależności od wartości stringa. Format ICU selecta, jest bardzo podobny do plurala amount.

Plural typu select otwieramy za pomocą klamrowych nawiasów, następnie podajemy nazwę parametru i typ plurala – “select”. Podobnie tak jak w przypadku plurala, pozostałe argumenty są warunkami logicznymi.

Pluralizacja w praktyce

Dodajmy paczkę transloco-messageformat do naszej aplikacji. Możemy to zrobić poprzez wykonanie komendy

npm i @ngneat/transloco-messageformat

Po zainstalowaniu paczki musimy nadpisać transpiler transloco. Zrobimy to poprzez providowanie MessageFormatTranspilera w AppModule.

Po initial setupie utwórzmy przykładowe dane, które będziemy wyświetlać w naszej aplikacji do zarządzania magazynem. Zacznijmy od stworzenia interfejsu w pliku types/storage-item.interface.ts:

Następnie utwórzmy kilka przykładowych danych w naszym komponencie:

Oraz wyświetlamy je w templatce za pomocą dyrektywy ngFor. Przy tworzeniu paragrafów z danymi, jako drugi argument funkcji t podajemy obiekt z interpolowalnymi parametrami.

Po tym procesie powinniśmy widzieć widok podobny do screena poniżej w naszej przeglądarce.

Problemy związane ze skalowaniem aplikacji

Wraz ze skalowaniem aplikacji wykorzystujących bibliotekę typu runtime możemy się spotkać z kilkoma wyzwaniami. Są to między innymi klucze zombie, wzrost czasu wymaganego na initial load aplikacji czy brakujące klucze

Klucze Zombie

W dzisiejszych czasach utrzymanie porządku w plikach z translacjami jest niemałym wyzwaniem. Klucze zombie, to klucze z translacjami w repozytorium, które nie są nigdzie wykorzystywane. Możecie sobie zadać pytanie, skąd one się wzięły? Odpowiedź jest prosta – ktoś zapomniał je usunąć z plików i18n, pomimo, że usunął ich zastosowania w kodzie. Posiadając kilka nieużywanych kluczy nie jest jakimś dużym problemem. Dopiero wraz z rozszerzeniem się tej małej “epidemii” coraz bardziej odczujemy potencjalne straty – niepotrzebnie zwiększony bundle size. Pamiętajmy o tym, by monitorować wykorzystania kluczy na bieżąco, możemy to zrobić np poprzez dodatki do naszych edytorów, które pomogą nam znaleźć i usunąć klucze zombie obecne w naszej aplikacji.

Lazy Loading Translacji

Wraz ze wzrostem ilości kluczy w naszej aplikacji, możemy napotkać kolejny duży problem. Czas potrzebny na załadowanie pliku json znacznie się wydłuża. Jest to szczególnie problematyczne, jeżeli posiadamy już tysiące kluczy z translacjami w jednym pliku. Najprostszym rozwiązaniem, które możemy w takim przypadku zastosować jest podzielenie translacji na moduły oraz skorzystanie z lazy-loadingu translacji wraz z modułem.

Brakujące Klucze

Zdarza się, że wraz z natłokiem dodawania nowych translacji, zapomnimy dodać klucz w jednym języku. Na nasze szczęście, rozwiązanie tego problemu nie jest bardzo trudne. Zasadniczo mamy kilka opcji:

  • Skorzystać z dodatku do edytora, który pokazuje brakujące klucze. To rozwiązanie sprawdzi się w małych projektach, gdzie nie znajduje się dużo plików z tłumaczeniami.
  • Wiele zewnętrznych serwisów pokazuje statystyki ilości przetłumaczonych kluczy oraz które klucze jeszcze nie są przetłumaczone. To rozwiązanie sprawdza się świetnie w przypadku średnich i dużych projektów, ale może być również zaimplementowane w mniejszych aplikacjach. Więcej informacji o tym w dalszej części artykułu
  • Możemy również stworzyć skrypt sprawdzający, czy w jakimś pliku nie brakuje translacji.

Stworzenie skryptu znajdującego brakujące klucze

Jednym ze sposobów na znalezienie brakujących kluczy w projekcie jest stworzenie prostego skryptu. Zaletą tego rozwiązania jest z pewnością jego elastyczność. Możemy go uruchomić w dowolnym momencie, a nawet połączyć go z husky, tak aby zablokować commita gdy skrypt wykryje brakujące translacje. Dzięki temu możemy być pewni, że każdy commit zawiera wszystkie translacje.

missing-keys-finder.js

Powyższy skrypt zbiera wszystkie translacje z plików, znajdujących się wewnątrz folderu o ścieżce zadeklarowanej w zmiennej i18nFolderPath. Po wczytaniu wszystkich translacji wykrywa on brakujące translacje, a następnie wyświetla wynik wyszukiwania w konsoli.

Skoro już wiemy jak działa nasz skrypt, to dodajmy go do package.json, tak aby można było go uruchomić za pomocą komendy npm run.

Aby przetestować skrypt, wykonaj w terminalu następującą komendę

npm run find-missing-keys

Powinieneś zobaczyć output podobny do tego na screenie poniżej.

O czym jeszcze warto pamiętać?

  • Powinniśmy przetłumaczyć każdy dostępny tekst na naszej stronie. Szczególnie atrybuty takich jak placeholder, title, aria-label czy alt.
  • Dodawajmy alternatywne wersje dla obrazków i infografik zawierających tekst. Src do takiego obrazka powinniśmy zawrzeć w pliku z translacjami, a następnie odwołać się do niego za pomocą naszej biblioteki w atrybucie src.
  • Jeżeli używamy biblioteki Angular Material, powinniśmy przetłumaczyć wszystkie elementy, z którymi ma do czynienia nasz użytkownik. Poza oczywistymi rzeczami takimi jak tekst wewnątrz tooltipów czy snack barów nie warto zapominać o mat-paginatorze.  Komponent ten posiada defaultowe tooltipy, które należy przetłumaczyć z użyciem providera MatPaginatorIntl.
  • Ustawiajmy tytuł strony w zależności od języka użytkownika. Możemy to zrobić poprzez stworzenie customowej klasy rozszerzającą klasę TitleStrategy. W tej klasie powinniśmy nadpisać metodę updateTitle, w której będziemy ustawiać tytuł w zależności od tytułu strony zadeklarowanego w route’cie. Przykładową implementację takiego rozwiązania z użyciem biblioteki ngx-translate znajdziecie pod tym linkiem. https://itnext.io/manage-angular-page-titles-translation-d1384bbede86

Internacjonalizacja wyświetlanych danych

Czy to wszystko, co powinniśmy wiedzieć o i18n? Nie do końca – internacjonalizacja aplikacji to coś więcej niż tylko przetłumaczenie tekstu na różne języki. Proces ten jest znacznie bardziej skomplikowany i składa się z wielu etapów (https://www.w3.org/International/questions/qa-i18n). Jedną, z rzeczy która wchodzi w skład internacjonalizacji jest na dostosowaniu formatu wyświetlanych danych pod regionalne normy.

Angular dostarcza nam różne pipe’y, które z łatwością pozwalają nam wyświetlić zinternacjonalizowane dane. W naszym arsenale mamy do dyspozycji następujące narzędzia:

  • DatePipe – formatuje wyświetlane daty
  • CurrencyPipe – formatuje wyświetlane waluty
  • DecimalPipe – formatuje wyświetlane liczby
  • PercentPipe – formatuje wyświetlane procenty

Dlaczego LOCALE_ID jest taki ważny?

LOCALE_ID to injection token, za pomocą którego możemy ustawić globalny kod językowy w naszej aplikacji. Wspomniane wyżej pipe’y używają tego tokenu przy formatowaniu danych.

Na powyższym screenie możemy zobaczyć, jak bardzo dane potrafią się różnić w zależności od wartości tokenu. Różnica jest od razu widoczna, szczególnie w przypadku walut i daty. Skoro już wiemy jak token wpływa na naszą aplikację, wystarczy tylko zmienić jego wartość w trakcie działania aplikacji. To nie może być takie trudne, prawda?

Zmiana LOCALE_ID w trakcie pracy aplikacji

Problemy ze zmianą LOCALE_ID, zaczynają się już na samym starcie. Token ten przechowuje wartość typu string, a co za tym idzie nie możemy go edytować po inicjalizacji aplikacji, tak jak zrobilibyśmy to w przypadku typu referencyjnego na przykład obiektu.

Problem ten nie występuje w natywnej bibliotece od angulara, gdyż ta używa innej wersji aplikacji dla każdego języka. Warto wziąć to pod uwagę, jeżeli wybieramy bibliotekę typu run-time.

Mimo tego problemu istnieje pewien trik, który pozwala nam stworzyć dynamiczny LOCALE_ID. Dla tych którzy chcą używać wbudowanych pipe’ów do formatowania danych zamieszczam link do repozytorium z workaroundem. https://github.com/armanozak/angular-dynamic-locale

Edycja kluczy wewnątrz IDE i zewnętrzne serwisy

Edytowanie kluczy przez GUI

Praca na plikach z tłumaczeniami bywa problematyczna, wymaga od nas częstego scrollowania przez pliki w celu dodania nowych lub edycji istniejących tłumaczeń. Problem ten zaczyna się nasilać wraz ze wzrostem ilości kluczy i dodawaniu nowych plików z translacjami. W takich momentach możemy skorzystać z dodatków do naszych edytorów, które pozwalają nam na edycję tłumaczeń z poziomu interfejsu graficznego. 

Rozwiązanie znacznie poprawia DX, szczególnie kiedy wybrany dodatek zestawia tłumaczenia w kolumnach. Gwarantuje wam, że gdy zaczniecie używać edytora UI czas poświęcony na zbędne skakanie pomiędzy plikami zostanie drastycznie zredukowany. Z łatwością również będziecie w stanie przeglądać, dodawać oraz edytować nowe klucze. 

Dodatki do VS Code

Dodatki do JetBrains IDE

  • Easy i18n, dodatek pozwala na edycję kluczy do translacji w tabelce, tree view. Nie zabrakło w nim również takich featurerów jak podświetlanie brakujących kluczy na czerwono czy edycji tłumaczenia prosto z templatki po użyciu skrótu ctrl + b na kluczu. https://plugins.jetbrains.com/plugin/16316-easy-i18n

Zewnętrzne Serwisy Zarządzające Kluczami

Na rynku można znaleźć wiele zewnętrznych serwisów, które skupiły się na zarządzaniu translacjami. Osobiście uważam, że technologia ta jest game-breakerem. Odnosi się to szczególnie w przypadku ogromnych projektów, w których zaangażowane są dziesiątki osób – wliczając w to tłumaczy. To właśnie w tego typu projektach deweloperzy często są zmuszeni bezmyślnie przekopiowywać otrzymane od tłumaczy tłumaczenia do plików json. Na dłuższą metę skutkuje to stratą czasu oraz otwarciem aplikacji na mikro błędy związane z typingiem.

Używając zewnętrznego serwisu jesteśmy w stanie przenieść naszą pracę na nowy poziom. Stworzenie kont użytkownikom oraz przypisanie ich do konkretnych plików pozwala nam na optymalizację zadań oraz śledzenie postępów. Poniżej napisałem inne ciekawe funkcjonalności, które oferują nam zewnętrzne serwisy. Warto zaznaczyć, że poszczególne serwisy mogą się od siebie różnić ofertą oraz dostępnymi funkcjonalnościami.:

  • Historia edycji poszczególnych translacji
  • Zarządzanie releasami aplikacji
  • Integracja z CI/CD
  • Automatycznie tłumaczenie plików z wykorzystaniem deepl, google translate czy amazon translate
  • Różnego rodzaju ostrzeżenia np: o brakujących translacjach
  • Grupowanie translacji, dodawanie do nich tagów, komentarzy oraz ustawianie flag. Pozwala to na rozszerzenie kontekstu dla tłumaczy bez impaktu na bundle size.
  • Możliwość postawienia takiego rozwiązania na własnym serwerze

Do przykładowych zewnętrznych serwisów zarządzających translacjami można zaliczyć:

Podsumowanie

Jak z pewnością mogłeś zauważyć, temat internacjonalizacji aplikacji mimo swojej prostoty, ma swoje tajemnice. Przed wprowadzeniem i18n do naszej aplikacji powinniśmy się zastanowić, jakiej biblioteki potrzebujemy oraz znać jej wady.

A jakie wy macie doświadczenia z internacjonalizacją aplikacji? Z jakich bibliotek oraz serwisów mieliście okazję korzystać?

 

Źródła:

O autorze

Dawid Kostka

Angular Developer w House of Angular. Software development dla mnie to coś więcej niż tylko klepanie kodu. Kocham eksplorować i bawić się wysokiej jakości kodem. Uwielbiam adrenalinę i pozytywne nastawienie. W wolnych chwilach jeżdzę na rolkach agresywnych.

Zapisz się do naszego newslettera. Bądź na bieżąco z najnowszymi trendami, poradami, meetupami i stań się częścią społeczności Angulara w Polsce. Rynek pracy docenia członków społeczności.

Leave a Reply

Your email address will not be published. Required fields are marked *