Dlaczego zamiast samodzielnie renderować znaki, powierzyliśmy to platformie
Czy zdarzyło wam się zauważyć, że hangul wygląda zupełnie normalnie, ale znaki hanja wydają się w jakiś sposób obce? Kiedy rezygnujemy z samodzielnego rysowania znaków i powierzamy je silnikowi tekstowemu platformy, naszym zadaniem przestaje być określenie „co narysować”, a staje się nim wskazanie „o jaki krój pisma poprosić”.

Zanim tekst pojawi się na ekranie, trzeba wykonać wiele pracy. Należy zamienić znaki w kształty, podzielić tekst na wiersze i narysować go za pomocą pikseli. Trzeba obsłużyć wszystko: od składania znaków hangul, przez hanja, emoji i pismo arabskie, aż po zastępcze kroje pisma.
Nie stworzyliśmy tego procesu od nowa. Powierzyliśmy go Pango i HarfBuzzowi w Linuksie, DirectWrite w Windowsie oraz TextKit 2 w macOS.
Zrobiliśmy tak, ponieważ systemy operacyjne doskonalą tę dziedzinę od dawna. Gdybyśmy stworzyli własne rozwiązanie, istniałoby duże ryzyko, że jakość pogorszyłaby się właśnie w trudnych obszarach, takich jak znaki CJK czy dostępność. Uznaliśmy też, że tym, co wyróżnia produkt, nie jest sam mechanizm renderujący, lecz typografia i sposób obsługi zgodny z charakterem danej platformy.
Sami zdecydowaliśmy jednak, kiedy tworzyć układ
Tworzenie układu tekstu wymaga czasu i pamięci. Duże dokumenty mają dziesiątki tysięcy wierszy, więc nie mogliśmy tworzyć ich wszystkich.
Dlatego tworzymy tylko wiersze widoczne na ekranie oraz te znajdujące się w ich pobliżu. Wiersze, które oddalą się od ekranu, natychmiast usuwamy. Nawet po długim przewijaniu dokumentu o rozmiarze 1 MB aktywnych pozostaje zaledwie około czterdziestu–sześćdziesięciu układów.
W przypadku wierszy, których jeszcze nie utworzyliśmy, z wyprzedzeniem szacujemy ich wysokość. Pasek przewijania musi przecież znać całkowitą długość dokumentu.
Oczywiście oszacowanie bywa błędne. Jeśli nowo obliczona wysokość różni się od przewidywanej, zawartość ekranu może się przesunąć. Wtedy w tej samej klatce korygujemy pozycję przewijania o powstałą różnicę. Użytkownik nie powinien zauważyć, że cokolwiek się wydarzyło.
Wiersz, w którym składany jest hangul, wyświetlamy bez żadnego formatowania. Gdyby podczas wprowadzania tekstu pojawiły się takie właściwości jak pogrubienie czy ukrycie, liczba znaków znana edytorowi metody wprowadzania mogłaby przestać odpowiadać liczbie znaków widocznych na ekranie.
Pierwszym napotkanym problemem była twarz znaków hanja
Gdy powierzamy coś platformie, otrzymujemy również jej ustawienia domyślne.
Domyślny krój pisma interfejsu w Ubuntu nie zawiera znaków hangul. Dlatego system wybrał w zastępstwie Noto Sans CJK JP. Hangul wyglądał poprawnie, ale niektóre znaki hanja pojawiały się w japońskich formach. Dzieje się tak, ponieważ ten sam znak Unicode może mieć inny kształt w Korei i Japonii.
Rozwiązanie było proste. Umieściliśmy Noto Sans CJK KR na początku listy krojów pisma interfejsu.
Ważniejsze było jednak coś innego. Jeśli sami nie określimy reguł, platforma odpowie na podstawie własnych ustawień domyślnych, a nie języka użytkownika.
Kształty właściwe dla poszczególnych języków tworzymy za pomocą kolejności krojów pisma
Stos krojów pisma wyszukuje znaki od początku listy. Tylko znaki, których nie ma w pierwszym kroju, przechodzą do następnego.
Wykorzystując tę właściwość, można wyświetlać tekst łaciński krojem szeryfowym, a hangul — bezszeryfowym. Jedna linia ustawień pozwala połączyć kroje odpowiednie dla poszczególnych języków.
Jeśli pole kroju do czytania pozostanie puste, używany jest ten sam krój co w tekście głównym. Jeśli zostanie wskazany osobno, zmieni się wyłącznie w trybie czytania. W trybie źródłowym używany jest krój pisma przeznaczony dla kodu.
Rozmiar znaków jest zgodny z ustawieniami dostępności systemu operacyjnego. Użytkownik, który powiększył tekst w systemie, nie musi ustawiać go ponownie w każdej aplikacji.

Czytnik ekranu korzysta z tej samej podstawy. Funkcje dostępności są już połączone z warstwą tekstową platformy, więc wystarczy, że dokładnie przekażemy treść wiersza, o który poprosi.
Brak tłumaczenia powoduje niepowodzenie kompilacji
Teksty interfejsu piszemy po angielsku i używamy ich jako kluczy w tabeli tłumaczeń. Jeśli brakuje tłumaczenia, na ekranie pojawia się angielski tekst.
Problem polega na tym, że nie występuje żaden błąd. Nawet jeśli brakuje tłumaczenia, angielski tekst po prostu po cichu miesza się z pozostałymi.
Dlatego stworzyliśmy skrypt kontrolny. Zbiera on teksty rzeczywiście używane w kodzie i porównuje je z tabelami tłumaczeń dla 12 języków. Jeśli tekst zostanie dodany tylko w jednym języku albo zabraknie któregoś tłumaczenia, kompilacja kończy się niepowodzeniem.
Każda tabela tłumaczeń zawiera 304 klucze, a w kodzie rzeczywiście używane są 282 teksty. Obsługiwane języki to English, 한국어, 日本語, 简体中文, 繁體中文, Deutsch, Français, Español, Português (Brasil), Русский, Italiano, Polski.

Po zmianie języka otwieramy nowe okno z tym samym dokumentem i zamykamy dotychczasowe. Gdybyśmy osobno tłumaczyli setki już wyświetlonych widżetów, łatwo byłoby któryś pominąć. Uznaliśmy, że bezpieczniej jest raz ponownie otworzyć okno w chwili zmiany języka, niż utrzymywać skomplikowaną ścieżkę, która jest używana niezwykle rzadko.
Gdy powierzamy coś platformie, rezultat również ją przypomina
Nawet ten sam dokument nie wygląda identycznie co do piksela we wszystkich systemach operacyjnych. Wynika to z różnic w sposobie wygładzania znaków i w domyślnych krojach pisma.
Nie próbujemy na siłę uzyskać identycznego wyglądu. Zamiast tego zachowujemy te same podstawowe parametry dokumentu, takie jak proporcje rozmiaru znaków, wysokość wiersza i marginesy.
Wynik wyboru zastępczego kroju może się również różnić zależnie od zainstalowanych krojów pisma. Dlatego zamiast obiecywać konkretny rezultat, przejrzyście pokazujemy kolejność, w jakiej żądamy poszczególnych krojów. Użytkownik może ją także samodzielnie zmienić.
To, że nie rysujemy wszystkiego samodzielnie, nie oznacza, że znika nasza odpowiedzialność. Musimy jeszcze precyzyjniej określić, co powierzamy platformie i w jakiej kolejności o to prosimy.