Hlavní navigace

Z „leden“ je „ledna“ – glibc mění názvy měsíců

Ondřej Caletka

Od nepaměti jsou v českém nastavení prostředí názvy měsíců prezentovány sice česky, ale v prvním pádu, což není ideální. Tato dlouhotrvající vlastnost byla nedávno opravena, oprava se ale neobejde bez komplikací.

Doba čtení: 4 minuty

Rozmohl se nám tady takový nešvar. Vývojáři velice často opravují chyby, na které jsme si už zvykli a považujeme je za standard.

Jednou z takových chyb je výpis kalendářního data v češtině. Požádáme-li standardní knihovní funkcistrftime(3)  modifikátorem %B o plné jméno měsíce v aktuálním národním prostředí, bývalo zvykem, že funkce vypíše název měsíce v češtině, bez velkého počátečního písmena (na rozdíl od angličtiny se názvy měsíců a dnů v týdnu česky píší bez velkých písmen) a v prvním pádu.

Použití prvního pádu není pro zápis kalendářního data příliš vhodné, jména měsíců běžně zapisujeme a čteme v druhém pádu. Na druhou stranu, má-li aplikace vypsat jméno měsíce jako takové, je použití prvního pádu zcela na místě. Jednoduché nahrazení všech jmen měsíců druhým pádem tedy není bez rizika.

K právě takové změně relativně nedávno v projektu glibc došlo. Knihovna jazyka C, kterou používá v podstatě každá aplikace, změnila názvy měsíců z prvního na druhý pád. Původní názvy měsíců jsou nyní uloženy v nestandardní proměnné  alt_mon. Došlo tak k nastolení souladu s globální databází CLDR, která definuje měsíce v druhém pádu pro formátované datum a zároveň v prvním pádu pro samostatně stojící název měsíce. Změna je v glibc verze 2.28, která vyšla 1. srpna 2018. Některé distribuce ji ale zahrnují i do udržovacích vydání předchozí verze, konkrétně v Gentoo jde o verzi 2.27-r6.

Chování je nepředvídatelné

Jakkoli je oprava chvályhodná, mění chování, které bylo po řadu let standardem a pro aplikace je obtížné poznat, zda aktuální systém používá verzi knihovny před opravou nebo po ní. Spustíte-li tedy například utilitucal  na systému, kde byla nedávno aktualizována knihovna glibc, výstup nebude úplně ideální:

$ cal -3
    prosince 2018          ledna 2019            února 2019
Po Út St Čt Pá So Ne  Po Út St Čt Pá So Ne  Po Út St Čt Pá So Ne
                1  2      1  2  3  4  5  6               1  2  3
 3  4  5  6  7  8  9   7  8  9 10 11 12 13   4  5  6  7  8  9 10
10 11 12 13 14 15 16  14 15 16 17 18 19 20  11 12 13 14 15 16 17
17 18 19 20 21 22 23  21 22 23 24 25 26 27  18 19 20 21 22 23 24
24 25 26 27 28 29 30  28 29 30 31           25 26 27 28
31

Přitom právě nástroj cal dělá všechno pro to, aby použil alternativní názvy měsíců – takové, které se hodí pro samostatně stojící název měsíce. Během kompilace testuje, zda knihovna glibc alternativní názvy podporuje. Pokud ano, použije je, jinak použije běžné. Protože se ale detekce provádí pouze během kompilace, je nutné po aktualizaci knihovny glibc překompilovat i balíček util-linux, jehož součástí cal je. Pak budou měsíce opět zobrazovány v prvním pádu.

Jak vlastně funguje nastavení lokalizace

Každý admin ví, že pro nastavení lokalizace slouží několik proměnných prostředí. První jménem LANG má nejnižší prioritu a představuje výchozí preferenci uživatele. Následuje několik proměnných začínajících LC_, které specifikují nastavení konkrétní oblasti – například LC_TIME ovlivňuje právě lokalizaci související s datem a časem. Nejvyšší prioritu má pak proměnná LC_ALL, která přebíjí všechny ostatní, a proto je dobré ji až na výjimečné případy nenastavovat.

Co už ale není úplně zřejmé, je fakt, že bez ohledu na nastavení proměnných každá aplikace startuje ve výchozím nastavení lokalizace. Aby se začala chovat podle nastavení v proměnných prostředí, musí k tomu aplikace zavolat funkcisetlocale(3) s prázdným řetězcem na místě názvu lokalizace. Jsou-li k dispozici i alternativní názvy měsíců, je možné je získat formátovacím řetězcem %OB funkce strftime(3)  – toto chování bylo převzato z FreeBSD. Na staré verzi glibc ale tento formátovací retězec nefunguje.

Vytvořil jsem demonstrační příklad. Spuštěný na staré verzi glibc vypíše:

Locale: C
Format string: %B
Result: January, February, March, April, May, June, July, August, September, October, November, December
Format string: %OB
Result: %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB

Locale: cs_CZ.UTF-8
Format string: %B
Result: leden, únor, březen, duben, květen, červen, červenec, srpen, září, říjen, listopad, prosinec
Format string: %OB
Result: %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB

Po aktualizaci glibc ten samý program vypíše měsíce v druhém i prvním pádu:

Locale: C
Format string: %B
Result: January, February, March, April, May, June, July, August, September, October, November, December
Format string: %OB
Result: January, February, March, April, May, June, July, August, September, October, November, December

Locale: cs_CZ.UTF-8
Format string: %B
Result: ledna, února, března, dubna, května, června, července, srpna, září, října, listopadu, prosince
Format string: %OB
Result: leden, únor, březen, duben, květen, červen, červenec, srpen, září, říjen, listopad, prosinec

Přechodné období nebude snadné

Nová verze knihovny glibc zcela jistě směřuje správným směrem. Díky přítomnosti obou variant názvů měsíců je možné nově používat knihovní funkce i v aplikacích, kde záleží na kvalitě české jazykové verze; použití knihovní funkce namísto vlastního řešení v aplikační logice nepochybně usnadní výrobu dalších jazykových verzí, kde v ideálním případě bude stačit přepnutí proměnné  LANG.

V následujících několika letech nás ale bohužel čeká období nestability, kdy se nebude možné spolehnout na tvar názvu měsíce. Aplikacím, které závisí na precizním výstupu, tak nezbývá než knihovní funkce nepoužívat, dokud se situace nestabilizuje.

Našli jste v článku chybu?