Tato stránka se pokouší shrnout, jak je potřeba psát programy, které jsou schopné se přizpůsobit místnímu prostředí a zvyklostem uživatele. V první části jsou popsány základní problémy při psaní takových programů, v druhé jsou demonstrována jejich řešení zvláště v prostředí operačních systémů vyhovujících standardu POSIX® [susv3], třetí stručně popisuje úkoly překladatele při lokalizaci do nového jazyka. Poslední část ukazuje nejčastější problémy a chyby při implementaci těchto řešení.
Pojmem internacionalizace se rozumí proces úpravy existujícího programu tak, aby byl schopen pracovat v (jazykovém a kulturním) prostředí uživatele, respektovat místní zvyklosti ohledně reprezentace času, čísel apod., a v ideálním případě s uživatelem komunikovat v jeho jazyce.
Přitom se klade důraz na to, aby byl program na konkrétním prostředí nezávislý a bylo jej tedy možné používat i v jazykových prostředích, pro která nebyl původně určen, bez zásahu do programu samotného. Úprava takto připraveného programu pro konkrétní prostředí (což znamená především překlad textů, které program používá), se nazývá lokalizace. O internacionalizaci se tedy stará autor programu, o lokalizaci lokalizační tým.
Protože oba výše uvedené pojmy jsou poměrně dlouhé, používají se zkratky i18n a l10n, které vznikly uvedením prvního a posledního písmene anglické verze pojmu a počtu vynechaných písmen mezi nimi [1].
Na internacionalizaci kladou moderní uživatelská prostředí pro unixové systémy velký důraz, takže pojmem internacionalizace se stále častěji myslí vyžadovaná vlastnost programu, který je ve vývoji, než dodatečný proces po jeho dokončení.
Schopnost programu pracovat v prostředí uživatele není samozřejmostí, i když se tak někdy může zdát. Většina dnes používaných programovacích jazyků a prostředí byla navržena v době, kdy prakticky jediným jazykem používaným pro komunikaci s počítačem byla angličtina a díky tomu jejich základní rozhraní používají konvence podobné lokálním konvencím ve Spojených státech amerických[2].
Z důvodu zpětné kompatibility je pak pro správnou funkci v jiném prostředí často potřeba použít složitější rozhraní než to, které se pro daný úkol přímo nabízí. Kromě toho se ukazuje, že mnoho programátorů si není vědomo existence ani těch základních rozhraní a implementuje odpovídající funkcionalitu ve svých programech ručně.
Prvním a základním problémem je reprezentace textu jako taková. Je přirozené, že text se v počítači reprezentuje čísly, nicméně neexistuje žádný zřejmý způsob, jak tato čísla odpovídajícím částem textu (prozatím se vyhněme pojmům znak nebo symbol) přiřadit. Program by měl být schopen text reprezentovat kódováním, které používá uživatel, ať už je toto kódování je dáno historickou praxí, standardem dané země nebo mezinárodní dohodou.
Jakmile je potřeba zpracovávat textové informace, které nejsou všechny ve stejném kódování, nastává ještě větší problém, jehož řešení obvykle vyžaduje přímou intervenci uživatele. Automaticky rozpoznat kódování dokumentu obecně nelze, protože daný proud bitů může být platnou reprezentací textu v mnoha různých kódováních (nepomohla by ani databáze všech známých světových jazyků, protože systém by měl rozpoznat i kódování textu, obsahujícího jediné slovo, v horším případě jedinou zkratku).
Tradiční reprezentace textu jako posloupnosti kódů používajících stejný počet bitů (v posledních padesáti letech se používalo přinejmenším 6, 7, a 8 bitů) nedostačuje pro reprezentaci textů v čínštině, japonštině, korejštině (jazycích dohromady často označovaných zkratkou CJK), mnoha indických jazycích a dalších textech, nemluvě o matematických symbolech (jejichž počet se navíc stále rozrůstá). Navíc vyvstala potřeba reprezentovat texty používající všechny tyto jazyky zároveň (když už k ničemu jinému, tak pro sepsání překladu věty „Mohu jíst sklo; neškodí mi to.” do všech známých jazyků).
Přímá metoda řešení tohoto problému rozšířením počtu používaných bitů na dostatečný počet (kódování „širokými znaky”) má řadu problémů. V dnešním světě je nemyslitelné, že by znak byl reprezentován např. 14 bity; musí být složen z osmibitových bajtů[3]. Kromě toho z důvodu snadné manipulace s textem pomocí existujících možností jazyků je třeba, aby reprezentace znaku jako čísla odpovídala existujícímu číselnému typu programovacích jazyků, tj. počet bajtů reprezentujících znak by měla být mocnina dvojky.
Takže pro reprezentaci znaku by v praxi připadalo v úvahu 16 nebo 32 bitů. To jsou poměrně vysoká čísla, protože uživatelé, kterým stačí standardní latinská abeceda, pro její reprezentaci v navrhovaném kódování potřebují dvakrát nebo čtyřikrát větší počet bajtů. To znamená větší nároky na interní i externí paměti, na rychlost procesoru a na propustnost datových sítí.
Druhým zásadním problémem této reprezentace a jakéhokoli jiného kódování je potřeba minimalizovat úpravy existujících programů nebo tyto úpravy provést s takovým předstihem a takovým způsobem, že starší verze programů nebudou aktivně narušovat používání nového formátu. (Ještě před patnácti lety byly běžnou praxí programy předpokládající znakovou sadu ASCII a aktivně nulující osmý bit přenášených dat.)
Text reprezentovaný jako posloupnost znaků je také potřeba správně zobrazit a umožnit jeho editaci. To je netriviální v případě, že celý text nemá stejný směr psaní (zleva doprava nebo zprava doleva). V případě, že logický kurzor je na hranici mezi textem psaným zprava doleva a zleva doprava, se vkládaný znak objeví na jiné pozici, je-li tento znak psaný zprava doleva, než znak psaný zleva doprava. V tomto případě program musí nakreslit primární kurzor na pozici, kam se vloží znak psaný v primárním směru textu, a sekundární kurzor na pozici, kam se vloží znak psaný v opačném směru.
Arabština a další jazyky přinášejí ještě jeden problém, existenci clusterů, posloupností znaků, jejichž logické pořadí neodpovídá pořadí na obrazovce (např. přesun samohlásek před souhlásky).
Nastavení rozložení kláves na klávesnici tak, aby odpovídalo místním zvyklostem a umožňovalo vstup všech znaků, je otázka nastavení uživatele a ne internacionalizace. Programy ovšem musí být schopné vstup z klávesnice zpracovat. To za ně většinou obstará operační systém nebo grafické prostředí, ale v X Window System je potřeba tuto obsluhu explicitně inicializovat. Tuto inicializaci provádějí moderní toolkity automaticky, ale „standardní” programy pro X na to v mnoha případech zapomínají.
Pro vkládání znaků CJK ale nemůže klávesnice stačit, což se řeší tak, že uživatel zapíše znak foneticky a z nabídky si pak vybere konkrétní znak. Tuto funkci obvykle provádí externí program nazývaný input method editor. Pro správné a příjemné zadávání textu potřeba s tímto programem komunikovat ve větším rozsahu, než jej jen inicializovat (jeden z možných režimů zadávání znaků zobrazuje menu možných znaků na pozici kurzoru, o které je tedy třeba input method editor informovat), a pokud používaný toolkit tuto komunikaci nepodporuje, není přidávání této podpory snadné.
Systém umožňující lokalizaci textů používaných programem (dále jen zpráv), který má být dostatečně pohodlný pro programátora, překladatele i uživatele, musí splnit několik požadavků. Programátor vyžaduje minimální narušení stávajícího kódu programu. To vylučuje metody, které všechny zprávy očíslují a uvnitř programu vyžadují odkazovat se na ně pomocí tohoto čísla, protože skutečně používaný text zprávy není v místě použití snadno dostupný.
Překladatelé se obvykle věnují většímu počtu programů a proto vyžadují jednotný systém překladu zpráv, bez ohledu na programovací jazyk nebo formát souboru, který zprávy obsahuje. Navíc by systém měl být poměrně flexibilní, umožňovat měnit pořadí údajů ve větě („5 files were deleted from /home.” versus „Z /home bylo odstraněno 5 souborů”) a gramaticky správnou práci s množným číslem („byl odstraněn 1 soubor”, „byly odstraněny 2 soubory”, „bylo odstraněno 5 souborů”).
Uživatelé se nechtějí těmito záležitostmi zabývat, takže program by měl používat správný jazyk podle nastavení locale, a musí být schopen správně fungovat, i když překlad do daného jazyka není k dispozici. Výhodou by byla možnost přidat lokalizaci daného programu prostou instalací jednoho nebo více souborů.
Klasickým problémem v této oblasti je rozdíl mezi desetinnou tečkou a desetinnou čárkou (což je mimochodem asi jediný případ, kdy chyba při internacionalizaci programu může vést nejen k chování programu odporujícímu místním zvyklostem, ale k přímému selhání programu). Dále je třeba respektovat místní zvyklosti ohledně oddělování skupin číslic a konečně v některých jazycích se pro reprezentaci čísel nepoužívají arabské číslice.
Programy pracující s finančními částkami by měly respektovat místní formát jejich zápisu. Zde jsou odlišnosti mezi různými zvyklostmi daleko větší, je potřeba správně obsloužit umístění symbolu měny (na začátku nebo na konci čísla), relativní pozici znaménka a symbolu měny a oddělování prvků zápisu mezerami. Navíc se mnohdy používá jiný formát pro standardní reprezentaci používající místní symboly měn a jiný pro reprezentaci používající standardní tříznakové kódy měn.
Zde je samozřejmě třeba respektovat místní pořadí částí (např. den/měsíc/rok v Evropě, měsíc/den/rok v USA a rok/měsíc/den v Japonsku), používat místní názvy měsíců a dnů v týdnu (ideálně včetně jejich správného skloňování). Při grafické reprezentaci kalendáře se mezi různými zeměmi liší konvence dne, který je první v týdnu. V některých zemích se hodina zapisuje jako jedno číslo mezi 0 až 23, jinde se používá rozsah 0 až 12 a přídavné zkratky indikující odpoledne a dopoledne.
Je to ale daleko složitější. Roky se často počítají jako číslo roku v „éře” (např. období vlády panovníka). Éra samozřejmě nemá číslo, ale jméno, které se nějakým způsobem kombinuje s ostatními částmi data. Kromě zapisování dat v Japonsku a Číně se to týká i klasického gregoriánského kalendáře, kdy programy obvykle vypisují rok -100 místo roku 101 před Kristem.
V některých zemích se pro zápis čísel v datu používá tradiční systém, který nemusí nutně používat arabské číslice (toto se svým způsobem týká i přípon „st”, „nd”, „rd”, „th” v angličtině) a dokonce ani desítkovou soustavu.
Případy, kdy se v dané zemi v dané době vůbec nepoužíval gregoriánský kalendář, ponechme radši stranou.
Místní standardy jednotlivých zemí obvykle definují určité pořadí mezi slovy, které se používá pro uspořádání slov např. ve slovníku. Tato pravidla bývají složitá, často obsahují různá kritéria s různou prioritou, přičemž konkrétní kritéria ignorují některé znaky (např. mezery), považují určité znaky za totožné (velká a malá písmena nebo v češtině existence čárky nad samohláskou), považují několik znaků za jeden (ch v češtině) nebo jeden znak za několik (česká norma požaduje rozepisování ß na ss).
Někdy bohužel tato pravidla vůbec není možné v počítači implementovat. Česká norma vyžaduje řazení slov vyjadřujících čísla v závislosti na významu, takže řetězec „Karel Ⅲ.”. je řazen před řetězec „Karel Čtvrtý”.
Další oblasti, kde by se měl program přizpůsobit místním zvyklostem už nepůsobí tolik obtíží, vyžadují jen existenci databáze patřičných hodnot. Do této kategorie patří standardní velikost kancelářského papíru (A4 nebo Letter), oslovování osob (pan, paní, slečna), standardní zápis poštovní adresy, telefonního čísla, místní telefonní předvolba a používaná jednotka míry (centimetr nebo palec).
Vzhledem k univerzální rozšířenosti jazyků C a C++ bude většina konceptů demonstrována právě na nich. Tím není nijak omezena jejich platnost, mimo jiné protože standardní knihovna jazyka C je téměř nutnou součástí každého programu běžícího v unixovém operačním systému a jiné programovací jazyky mají tyto možnosti k dispozici, takže je obvykle nabízejí ve svých rozhraních.
Klasické řešení, osmibitová „národní” znaková sada, zde už bylo zmíněno. Výhodou takového řešení je, že upravit programy, aby byly „eight-bit clean”, většinou nevyžaduje jejich výrazné změny. Nevýhodami je výše zmíněná omezenost repertoáru a především existence velkého množství takových znakových sad. Standard ISO 2022[4] definuje escape sekvence, které mají identifikovat znakovou sadu používanou následujícími bajty, ale toto řešení efektivně vyžaduje, aby každý program tento standard implementoval a byl interně schopen pracovat se všemi znakovými sadami, které přicházejí v úvahu, najednou, což už vyžaduje zásadní zásah do návrhu programu.
Jazyky, které pro reprezentaci potřebují více než 256 znaků, se obvykle reprezentují vícebajtovými kódy. Tím byla narušena základní vlastnost, že každý znak má stejně velkou reprezentaci. Používané systémy se dají shrnout do dvou kategorií.
První z nich jsou vícebajtová kódování, kde některé znaky nejsou reprezentovány jedním bajtem, ale více (nejčastěji dvěma). První bajt znaku pak tvoří buď celou reprezentaci znaku, nebo jen jeho prefix, za nímž následuje zbylá část. Délka reprezentace se tedy dá rozeznat jejím průchodem od začátku směrem dále, ale průchod v opačném směru (tj. přechod z daného znaku na znak předchozí) nemusí být možný, protože daný bajt může být platnou hodnotou jak pro první bajt jednobajtového znaku, tak pro druhý bajt dvoubajtového. Jediným řešením je projít celý text od začátku nebo známé pozice začátku znaku vpřed, dokud nenarazíme na hledaný předcházející znak.
Druhou kategorií jsou stavová kódování, kde znak reprezentovaný daným bajtem nezávisí jen na hodnotě bajtu, ale i na kontextu, ve kterém se nachází. Obvykle je definován počáteční stav a při průchodu textem jsou bajty interpretovány v tomto stavu. Je-li potřeba reprezentovat znaky, které lze reprezentovat jen v některém jiném stavu, je v textu vložen kód, který způsobuje změnu stavu a tedy i interpretace kódování. (Podobný systém přepínání se používal i na mechanických dálnopisech, kde ale základní jednotka dat měla jen 4 nebo 5 bitů.)
Samotný kód pro změnu stavu ovšem není znak, a proto se logicky přiřazuje ke znaku, který za ním následuje. Stavová kódování jsou tedy nutně vícebajtová a jejich zpracování je ještě složitější než vícebajtová kódování. Zůstává zde problém s pohybem v textu vzad (tentokrát způsoben tím, že po přechodu před kód pro změnu stavu nevíme, ve kterém stavu se nacházíme), navíc nemůžeme řetězce snadno spojovat a rozdělovat. Aby to vůbec šlo, je zavedena konvence žadající, aby řetězce začínaly i končily v počátečním stavu; to ale znamená, že jednoduché spojení dvou řetězců za sebe může obsahovat redundantní změny stavu a reprezentace dané posloupnosti znaků tedy není jednoznačně určena.
Výše byla zmíněna potřeba reprezentovat texty obsahující symboly z mnoha různých jazyků. Když se tento problém objevil, výrobci software vytvořili Unicode Consortium, které si dalo za cíl definovat univerzální znakovou sadu. Podobný cíl měla i skupina vyvíjející standard pro ISO a výsledkem je naštěstí jediný standard, ISO 10646 [iso10646]. Unicode Consortium publikuje standard Unicode samostatně, ale podstatná část jejich obsahu (tabulka přiřazení kódů) je totožná. Standard Unicode přidává další požadavky, např. specifikuje algoritmy pro rozmisťování textu, který kombinuje směry zprava doleva a zleva doprava, na obrazovce.
ISO 10646 definuje kódování používající 31 bitů pro reprezentaci každého znaku. Prvních 128 pozic je identických s kódy standardu ASCII[5], prvních 256 s kódy ISO 8859-1[6]. Původní verze standardu přiřazovala jen kódy, k jejichž reprezentaci stačilo pouze 16 bitů (oblast kódů U+0000 až U+FFFF se souhrnně označuje jako Basic Multilingual Plane, nová verze (a odpovídající standard Unicode 3.1) již tuto hranici překročila. Přesto se zdá, že celý prostor 31 bitů nebude nikdy využit.
ISO 10646 pokrývá všechny dosud používané znakové sady a kromě toho definuje kombinující znaky, které logicky patří k následujícímu nekombinujícímu znaku a ovlivňují jej, např. přidáním diakritického znaménka. To umožňuje používat např. v matematických textech písmena s akcenty, která se ve světových jazycích nevyskytují. To ale také znamená, že jeden znak může být definován posloupností kódů ISO 10646 a navíc popis konkrétního znaku není jednoznačný. Jsou ale definovány kanonické formy zápisu znaků (jedna z nich minimalizuje používání kombinujících znaků, jiná naopak všechny akcenty rozepisuje kombinujícími znaky) a odpovídající převodní tabulky. Kromě toho Unicode rozlišuje tři úrovně vyhovování standardu, nejnižší z nich podporu pro kombinující znaky nepožaduje.
ISO 10646 kromě přiřazení kódů znakům definuje také způsoby reprezentace těchto kódů. Za standardní reprezentaci lze považovat UCS-4, reprezentaci každého znaku čtyřmi bajty. Reprezentace UCS-2, reprezentující každý znak dvěma bajty (používaná pro celé rozhraní systému v Microsoft Windows), dnes už nedokáže reprezentovat všechny přiřazené kódy. To bylo vyřešeno rezervováním rozsahu kódů pod hranicí šestnácti bitů a definováním surrogate pairs, kdy dva dvoubajtové kódy UCS-2 reprezentují jeden znak Unicode; toto kódování se jmenuje UTF-16. Surrogate pairs nemohou obsáhnout celý rozsah 31 bitů, nicméně jejich rozsah je dostatečný a uvažuje se o tom, že příští verze standardu explicitně prohlásí, že nikdy nebudou přiřazeny kódy mimo rozsah pokrývaný surrogate pairs. Celkově je ale UTF-16 nepohodlné kódování, kombinuje všechny nevýhody vícebajtového kódování (i odhlédneme-li od kombinujících znaků) a kódování se širokými znaky.
Jak UCS-2, tak UCS-4 se vyskytují ve variantách little endian a big endian, které se rozlišují příponou, tedy např. UCS-2BE a UCS-2LE. Norma zřejmě jako UCS definuje formát big endian, ale převažující počítačové platformy používají interně formát little endian, takže použití little endian UCS umožňuje používat UCS i pro vnitřní reprezentaci znaků (POSIX vyžaduje, aby hodnota znaků z „přenositelné znakové sady” byla stejná ve standardním typu char i typu pro reprezentaci všech možných znaků wchar_t). Norma problém řeší tak, že soubory by na začátku měly obsahovat znak U+FEFF (zero width no-break space, také nazývaný byte order mark). Protože kód s opačným pořadím bajtů U+FFFE nebude nikdy přiřazen znaku, dají se obě varianty rozpoznat.
Poslední zajímavou alternativou (jiné varianty, např. UTF-7, se neprosadily) reprezentace ISO 10646 je UTF-8. Je to osmibitové vícebajtové kódování, jehož prvních 128 pozic je shodných s ASCII, takže je s ASCII plně kompatibilní. Zbylých 128 pozic se používá pro ostatní znaky ISO 10646, které jsou reprezentovány dvěma až šesti bajty. První bajt každého kódu začíná sekvencí bitů 11…10 (od bitů s vyšší hodnotou), přičemž počet jedničkových bitů je roven počtu bajtů kódu znaku. Další znaky začínají sekvencí bitů 10, hodnotové bity jsou rozmístěny ve zbylých bitech. Důsledkem tohoto kódování je, že počet bajtů znaku se dá snadno určit a je možné rozpoznat první bajty znaku od následujících bajtů, takže v textu se dá pohybovat směrem dozadu. Oproti specializovaným vícebajtovým kódováním má UTF-8 poněkud vyšší režii, ta je ale bohatě vykoupena pohodlnější prací s ním.
Locale je abstrakce regionálních nastavení. Má své jméno, jehož formát není definován v [c99], ale [susv3] definuje tvar území_jazyk.znakovásada@modifikátory, přičemž kromě první položky je možné kteroukoli (spolu s předcházejícím oddělujícím znakem) vynechat. Území je kód země podle ISO 3166[7], jazyk kód jazyka podle ISO 639[8], znaková sada určuje znakovou sadu, pokud se používá jiná, než je obvyklá pro daný jazyk na daném území, modifikátory odlišují locale podle jiného aspektu (například použití azbuky pro srbštinu). Specifikace locale pro češtinu je cs_CZ (používající implicitně znakovou sadu ISO 8859-2), popř. cs_CZ.UTF-8.
Programy v jazyce C implicitně pracují v locale "C" (v [susv3] dostalo další název "POSIX"), které odpovídá historickému chování. Program by měl nastavit používání locale voláním setlocale () podle prostředí, ve kterém je spuštěn; může nastavit locale buď globálně (LC_ALL), nebo pro každou definovanou kategorii zvlášť (LC_COLLATE definuje třídění, LC_CTYPE znakovou sadu používanou programem, LC_MESSAGES jazyk komunikace s uživatelem, LC_MONETARY prezentaci peněžních částek, naproti tomu LC_NUMERIC prezentaci ostatních čísel, a konečně LC_TIME prezentaci času).
Program se uživatele na locale ptát nemusí; locale je předáváno v proměnných prostředí, takže pokud uživatel explicitně locale nezmění, používá celý systém jednotné locale. Používané proměnné prostředí jsou LANG a LC_* (názvy odpovídají výše jmenovaným konstantám). Priorita těchto proměnných je definována tak, že LC_ALL má nejvyšší prioritu, pak následují proměnné specifické pro konkrétní kategorie a nejnižší prioritu má LANG; setlocale () automaticky používá tyto proměnné, pokud jako program jako název locale předá prázdný řetězec. Uživatelské prostředí by mělo nastavovat proměnnou LANG, aby uživateli umožňovalo v případě potřeby prostým nastavením jiné proměnné používané locale změnit.
Zatímco externí kódování textu programu by se mělo řídit nastavením kategorie locale LC_CTYPE, interně může být výhodné reprezentovat texty jinak. Obvykle se používá jedna z následujících tří možností.
Nejjednodušší řešení, vhodné pokud není třeba s textem příliš manipulovat, je reprezentovat text v externě používaném kódování, tj. ve znakové sadě definované LC_CTYPE. Tato znaková sada může být ASCII, jednoduché osmibitové kódování, vícebajtové kódování nebo stavové kódování. Z důvodu kompatibility s existující bází softwaru nemůže externí kódování používat kódování obsahující bajty NUL, tedy kódování širokými znaky.
Pokud je naopak s textem potřeba manipulovat hodně často, je výhodné ukládat text v širokých znacích. ISO C pro tento účel definuje znakový typ wchar_t a odpovídající sadu funkcí (většina funkcí str* má svůj ekvivalent pro široké znaky pojmenovaný wcs*), včetně funkcí pro převod mezi znakovou sadou danou aktuálním locale a širokými znaky. Široké znaky zajišťují uniformitu reprezentace za cenu vyšší paměťové náročnosti (pro reprezentaci ISO 10646 je potřeba dvaatřicetibitový datový typ). Tato alternativa je víceméně vynucena pro programy pracující ve Windows, protože nativní Win32 API očekává řetězce v UCS-2LE.
Poslední alternativou, používanou mj. v toolkitu GTK+ od verze 2.0, je interně text reprezentovat v UTF-8 bez ohledu na kódování definované pomocí locale. Toto řešení také vyžaduje konverzi pro komunikaci s okolím, ale narozdíl od používání širokých znaků má menší režii pro texty reprezentovatelné v ASCII a umožňuje používat více existujících rozhraní bez jejich podstatné změny. Podpůrná knihovna GLib opět poskytuje funkce pro práci s UTF-8 a převod mezi UTF-8 a znakovou sadou locale.
Pro konverzi textu v různých znakových sadách (včetně vícebajtových kódování obsahujících bajty NUL) definuje [susv3] rozhraní iconv (). Bohužel očekává jména kódování ve formátu daném konkrétní implementací standardu, což poněkud omezuje přenositelnost programů, které toto rozhraní využívají.
Touto problematikou se většina programů nemusí zabývat; buď jde o programy pracující se standardními soubory vstupu a výstupu, kdy se o komunikaci s uživatelem stará emulátor terminálu, nebo o programy s grafickým rozhraním, kde tuto úlohu přebírá toolkit (resp. v případě GTK+ pomocná knihovna Pango). Jen programy pracující s textovými soubory bez další struktury (jednoduché textové editory) někdy uživateli nabízejí výběr znakové sady, ve které má být načten nebo uložen soubor.
Druhou problematickou kategorií je komunikace po síti. Síťové protokoly často umožňují přenos osmibitových dat bez jakékoli specifikace použitého kódování. Pak opět nezbývá než nechat uživatele vybrat kódování. Pokud se při komunikaci používá více různých kódování (což se může snadno stát například při skupinovém rozhovoru protokolem IRC), je řešení problému natolik úmorné (definice kódování pro každého partnera při komunikaci), že na něj uživatelé a tedy i programátoři rezignují.
POSIX definuje mechanismus, který bude dále označován názvem jeho nejdůležitější funkce, catgets (). Umožňuje vytvořit katalogy lokalizovaných zpráv v textovém formátu, kde je každá zpráva identifikována číslem sady a číslem zprávy. Na cílové platformě je katalog zkompilován nástrojem gencat do binárního souboru, který je umístěn tak, aby jej bylo možné najít způsobem určeným v proměnné prostředí NLSPATH. V programu je katalog otevřen a pak z něj lze funkcí catgets () číst lokalizované tvary zpráv. Protože zpráva nemusí být k dispozici, dostává catgets () jako přídavný argument implicitní text zprávy, který je v takovém případě vrácen.
Mechanismus catgets sice řeší základní problém, ale je nepohodlný jak pro programátory, kteří musejí každé zprávě definované v programu přiřadit číselný identifikátor, tak pro překladatele, kteří musí překládané řetězce vyhledávat přímo v textu programu nebo v odděleném souboru, kam je programátor sepsal (pokud tento soubor odpovídá skutečnému stavu programu). Kromě toho je potřeba zachovávat kompatibilitu identifikátorů zpráv, aby bylo možné používat katalogy, které nebyly aktualizovány pro nejnovější verzi programu.
Alternativou je mechanismus gettext, poprvé použitý v operačním systému Solaris. Nyní je tento mechanismus standardem konsorcia OpenI18N a používá se převážně jeho implementace z projektu GNU, která jej stále vyvíjí, takže gettext reaguje na nově vznikající problémy[9].
Základní rozhraní je funkce gettext (), která jako argument očekává anglický řetězec a vrací jeho přeloženou podobu. Identifikace katalogu není explicitně předávána, stačí ji před voláním gettext () nastavit voláním funkce textdomain (). Je-li vhodné se na katalog odkazovat explicitně (např. je-li gettext používán uvnitř knihovny), může být místo gettext () používána její varianta dgettext (). Podobně jako u catgets stačí pro přidání nového překladu katalog zkompilovat nástrojem msgfmt a umístit do adresáře, kde jej program očekává.
Druhou důležitou součástí mechanismu je program xgettext, který v zadaných zdrojových souborech najde všechny řetězce, které jsou předávány jako parametr funkci gettext () nebo některé její variantě, a vytvoří kostru katalogu. Narozdíl od catgets obsahují katalogy gettext dvojice anglického a přeloženého textu, takže překladatelé pro svoji práci potřebují jen soubor katalogu.
Tento mechanismus extrakce by ale selhal, pokud řetězec, který se eventuálně stane argumentem gettext (), nebyl jako argument uveden přímo (např. protože je definován jako statické pole znaků). Takové řetězce lze označit pro překlad tím, že se „obalí” voláním makra gettext_noop, které sice jen vrací svůj argument, ale je programem xgettext považováno za jednu z hledaných funkcí.
De facto standardem je pro gettext (), resp. gettext_noop (), definovat makra _ () (podtržítko), resp. N_ (). To vede ke krátkému a přehlednému zápisu překládaných zpráv.
Je-li program změněn a je vytvořena nová verze kostry katalogu, lze ji automaticky sloučit s existujícím překladem pomocí nástroje msgmerge. Ten kromě nalezení textů, které byly přeloženy v dřívější verzi, nalézá přibližně odpovídající překlady, které je často potřeba upravit jen minimálně; zbylé překlady z dřívější verze jsou označeny jako zastaralé. Překlady všech těchto kategorií jsou zapsány zpět do katalogu, takže překladatel stále pracuje jen s jediným souborem, pro jehož editaci má navíc k dispozici silné nástroje: režim PO editoru Emacs nebo program KBabel.
Poslední důležitou schopností systému gettext je podpora plurálů. V programu se zpráva, která obsahuje číselný údaj, překládá voláním specializované funkce ngettext (), které jsou předány anglické texty pro singulár a plurál a hodnota číselného údaje. Není-li překlad dostupný, vrací funkce jeden ze svých argumentů podle anglických pravidel. Pro překlad takových zpráv ovšem nestačí přeložit jen singulár a plurál, [info gettext] popisuje devět různých způsobů, jak světové jazyky tvoří varianty věty v závislosti na hodnotě číselného údaje.
Místo explicitní podpory těchto devíti variant byl mechanismus zobecněn a v hlavičce katalogu (která obsahuje také např. název překládaného programu, cílový jazyk, jméno posledního překladatele a datum překladu) může být uvedeno, kolik různých variant se v jazyce vyskytuje a výraz v syntaxi podobné jazyku C, který pro vstupní parametr n vypočte číslo varianty, která se má použít. Katalog pak obsahuje pro každou variantu jeden překlad zprávy (samozřejmě jen pokud zpráva závisí na číselném údaji), při volání ngettext () je vypočten výraz určující variantu zprávy a ta je použita.
Podpora plurálů je relativně nová vlastnost, nicméně díky své standardizaci OpenI18N se postupně začíná používat a je pravděpodobné, že do několika let ji budou využívat všechny programy používající gettext.
Poslední zmiňovaný problém, změna pořadí údajů ve větě, nevyžaduje zvláštní mechanismus, protože potřebná funkcionalita byla přidána přímo do funkcí printf () a scanf (). Místo prostého specifikátoru %s jako druhého specifikátoru v rámci zprávy se v překladu použije specifikátor %2$s na kterékoli pozici. Analogickou funkci poskytuje toolkit Qt v metodě QString::arg (), kde ale díky přetěžování není potřeba ve formátovacím řetězci určovat typ parametru, takže specifikátory mají jednoduše tvar %2 pro druhý parametr.
Mechanismus gettext je určen pro překlad zpráv v běžících programech, kdy je některá z variant funkce gettext () přímo zavolána. Je podporován v programovacích jazycích C, C++, Python, GNU clisp, Emacs Lisp, librep (další varianta LISPu), GNU Smalltalk, Java, GNU awk, Free Pascal, Tcl, Perl, PHP, Pike, shellu bash a prostředí wxWindows. GNU gettext obsahuje knihovnu libintl, která implementuje potřebné funkce a kterou je vhodné distribuovat jako součást programu, který gettext využívá, aby program nebyl závislý na podpoře gettext v cílovém operačním systému. Programy v C a C++ používající GNU Autoconf lze o tuto knihovnu snadno rozšířit pomocí nástroje gettextize nebo autopoint.
Na druhé straně je mnohdy potřeba vytvořit datový soubor, který v sobě již obsahuje přeložené zprávy (obvykle obsahuje překlady pro všechny dostupné jazyky), např. soubor definující položku menu nebo dokument v XML, kde jsou jazyky označovány atributem xml:lang. Tuto funkci poskytuje balík intltool.
intltool tvoří ze vstupních souborů, kde jsou překládané zprávy (pokud formát souboru nedefinuje jiný mechanismus) označeny předřazením podtržítka před název jejich datové položky (např. elementu XML), výstupní soubory, které obsahují všechny překlady zprávy, označené způsobem specifickým pro daný typ souboru. intltool nalezené zprávy přidává do existujícího katalogu aplikace ve formátu gettext, takže pro překladatele nepřináší žádnou další zátěž. Kromě toho poskytuje jednotné rozhraní, které v sobě zahrnuje program xgettext, takže programátoři a překladatelé používají jen program intltool-update místo nástrojů xgettext a msgmerge.
intltool podporuje formáty souborů XML, .glade (popis okna složeného z widgetů toolkitu GTK+), .desktop (položky menu a „spouštěče” v prostředí KDE a GNOME), .server a .oaf (soubory komponentového rozhraní Bonobo).
Problém správného oddělovače desetinné části lze vyřešit jednoduše nastavením locale. O oddělování skupin číslic je potřeba explicitně požádat přidáním příznaku ' (apostrof) do specifikátoru parametru ve volání printf (), respektive scanf ().
Výpis finančních částek je výrazně složitější, je pro něj k dispozici oddělená funkce strfmon (). Vstup částek takový problém není, protože uživatel obvykle zadává číslo bez symbolu měny nebo oddělovačů řádů.
Vypsání času zajišťuje funkce strftime (). Je řízena formátovacím řetězcem podobným specifikaci pro printf (), který umožňuje přesně určit formát vypisovaného data (zkrácený nebo plný název dne/měsíce, den v týdnu/měsíci, rok, číslo týdne podle ISO 8601:1988, hodiny, minuty, sekundy, název časové zóny, posun časové zóny). Místo explicitního určování formátu je ale většinou lepší použít specifikátory %x pro datum, %X pro čas nebo %c pro obojí; tyto specifikátory zajistí použití reprezentace vžité pro nastavené locale.
Opačnou možnost, tedy čtení času z řetězce v daném formátu, poskytuje funkce strptime (). Pro uživatele může být příjemnější zpracování času pomocí funkce getdate (), která se pokouší vstup zpracovat pomocí několika různých formátů a navíc přesně definuje význam částečných specifikací, takže např. "Úterý 12:30" automaticky znamená nejbližší úterý ode dneška.
Pro řazení textu podle aktuálního locale stačí použít funkce strcoll (), resp. wcscoll () místo strcmp (), resp. wcscmp (). Poskytovány jsou ještě funkce, které ze vstupního řetězce vytvoří jiný řetězec, který nemá žádné použití kromě toho, že výsledek porovnávání dvou takových řetězců pomocí strcmp () je stejný, jako by byl výsledek porovnávání jejich vzorů pomocí strcoll (). Tato konverze by zřejmě měla umožnit kešování transformací potřebných pro porovnávání podle locale, nicméně v praxi příliš používaná není.
Velikost kancelářského papíru, oslovování osob, zápis poštovní adresy nebo telefonního čísla a používaná jednotka míry byly označeny za drobnější problémy mimo jiné proto, že [susv3] ani [c99] se jimi nezabývají. Nicméně oddělený dokument [iso14652] (jen technická zpráva, ne mezinárodní standard) popisuje formát specifikace hodnot pro vytvoření nového locale a [iso15435] (v přípravě) popisuje rozhraní pro použití těchto hodnot v programech a konkrétní rozhraní pro jazyk C. Popsaná rozšíření locale jsou v glibc, základní knihovně Linuxu, implementována, ale není poskytováno rozhraní definované v ISO 15435; místo toho bylo rozšířeno existující rozhraní nl_langinfo ().
U kategorií locale, kde lze nastavení charakterizovat databází dobře definovaných položek (např. názvy dnů v týdnu, ale ne algoritmus porovnávání řetězců), jsou hodnoty většiny takových položek k dispozici. Základní sadu atributů popisujících formáty čísel a finančních částek lze získat voláním funkce ISO C localeconv (). Funkce POSIXu nl_langinfo () umožňuje získat atributy tvaru textového řetězce; nejdůležitější z nich je atribut CODESET, obsahující název znakové sady aktuálního locale.
Pro lokalizaci do nového jazyka je prvním předpokladem existence patřičného locale. [susv3] definuje formát souboru popisujícího locale, který je na daném operačním systému zkompilován do binární podoby programem localedef.
Lokalizace konkrétního programu vzhledem k výše uvedeným skutečnostem většinou zahrnuje pouze překlad katalogu ve formátu gettext. Tento katalog se skládá z dvojic řetězců uvozených slovem msgid pro anglický text a msgstr pro text přeložený. Není-li text dosud přeložen, je text uvozený msgstr prázdný. Řetězce jsou zapisovány ve stejném formátu jako řetězce jazyka C, včetně spojování více po sobě následujících řetězců za sebou.
Kromě těchto řetězců katalog obsahuje poznámky na řádcích začínajících symbolem #. Poznámky se dělí do několika kategorií: poznámky překladatele (začínají # ), poznámky identifikující místo, kde se zpráva objevuje ve zdrojovém kódu programu (začínají #:) a poznámky řídící činnost programu msgfmt. Tyto poznámky začínají #. a obsahují posloupnost čárkami oddělených klíčových slov.
Nejdůležitější klíčové slovo je fuzzy, které znamená, že překlad vznikl nalezením přibližné shody nástrojem msgmerge; takové překlady nejsou zahrnuty do binárního katalogu a tedy se v programu nepoužívají, překladatel musí překlad zkontrolovat a klíčové slovo odebrat.
Druhé důležité klíčové slovo je c-format, které znamená, že zpráva je formátovací řetězec funkce printf () nebo funkce scanf (). Při zpracovávání takových řetězců program msgfmt kontroluje, že oba řetězce jsou platného formátu a očekávají argumenty stejných typů. Toto klíčové slovo přidává xgettext automaticky, ale někdy se stane, že jím označí zprávy, které ve skutečnosti nebudou tímto způsobem použity a msgfmt pak mylně vyžaduje kompatibilitu formátů. To se dá napravit uvedením komentáře /* xgettext:no-c-format */ na řádku zdrojového kódu obsahujícím danou zprávu nebo na řádku předchozím.
Jedna zpráva v katalogu tedy může vypadat takto [gnucash.po]:
# nemá žádné možnosti :-) #: src/report/report-gnome/window-report.c:513 msgid "There are no options for this report." msgstr "Tato sestava " "nemá žádné volby."
Speciální význam má zpráva s msgid "". Odpovídající překlad obsahuje hlavičku katalogu, jejíž hlavní položky již byly zmíněny výše.
Triviální „chybou” je fakt, že program nebyl internacionalizován, k čemuž dochází již jen velice zřídka, nebo že neexistuje lokalizace pro požadovaný jazyk, což je častější, nicméně i pro češtinu existuje lokalizace téměř všech podstatných programů operačního systému Linux, se kterými přijde do styku neprogramující uživatel.
Druhou častou chybou je neznalost dostupných rozhraní a vymýšlení vlastních variant. Několik programů (cups, abiword) používá vlastní systém překladu zpráv. Jiné programy, např. svého času gimp-print, názvy měsíců a dnů v týdnu nezískávají voláním nl_langinfo () nebo strftime (), ale jednoduše je zařadí mezi zprávy překládané překladateli. Tyto zprávy ale obsahují plné názvy měsíců i jejich zkratky a v angličtině se pro květen používá jediný termín „May”. Zatímco informace locale tento rozdíl reprezentují, pomocí gettext nelze přímo přiřadit jedinému anglickému textu dva různé překlady.
Nejzávažnější praktický důsledek má slepé nastavení locale podle prostředí a následný vstup nebo výstup čísel s desetinnou čárkou. Formát čísel totiž závisí na locale a nezřídka se stává, že program spuštěný v jednom locale není schopen načíst data, která sám dříve uložil v jiném locale, nebo dokonce která uložil v tomtéž locale.
Problém, který se začíná projevovat teprve v posledních letech s nástupem UTF-8, je špatná podpora víceznakových kódování. Programy často předpokládají, že mohou vykreslovat znaky bajt po bajtu nezávisle na sobě, nebo že na terminálu je šířka řetězce rovna počtu bajtů (což není pravda také protože jazyky CJK používají pro své ideografy znaky dvojnásobné šířky než znaky ASCII).
Poslední častější chybou, byť jen kosmetickou, je řazení řetězců porovnáváním bajtů místo pořadí definovaného locale. Většina linuxových programů, které uživateli nabízí seznam zemí, ve kterém jsou názvy zemí lokalizované, řadí Českou republiku za Zimbabwe.