Žymės įrašai

Rugpjūtis16

Daugiakalbiškumas PHP (Komentarai 920)

Žymės: php,utf,unicode,icu,i18n,l10n

Pasidalink!

 Kuriant daugiakalbišką programinę įrangą, viena aktualiausių sprendžiamų problemų yra programos pritaikymas skirtingų šalių kalboms (internationalization; I18n) bei kitoms kultūrinėms ypatybėms, pvz.: skaičių, datų ir kt. rašymas (localization; L10n).

 
Pavyzdžiui:
  • tiesioginis teksto vertimas, teksto rikiavimas, specialių simbolių vertimas lotyniškais (transliteration);
  • daugiskaitinės formos, pvz.: lietuvių kalboje yra trys daugiskaitinės formos - vienas obuolys, du obuoliai, dešimt obuolių; anglų kalboje dvi - one apple, two apples, ten apples;
  • datos rašymas, pvz.: Europoje 2013-08-16, JAV 8/16/13;
  • valiutos rašymas, pvz.: 10 Lt, $10
  • ilgio (metrai, pėdos), svorio (kg, svarai), tūrio (litrai, galonai) ir kt. standartai (metric/imperial);
  • teksto iš kairės (left-to-right; ltr), teksto iš dešinės (right-to-left; ltr) rašymas;
  • ir kt.
 
Su tekstu susijusios problemos dažniausiai sprendžiamos naudojant standartines operacinės sistemos priemones - lokales.
Pvz.:
 
setlocale(LC_TIME, 'lt_LT.UTF-8');
echo strftime('%c'); // 2013 m. rugpjūčio 16 d. 11:50:29
setlocale(LC_TIME, 'en_US.UTF-8');
echo strftime('%c'); // Fri 16 Aug 2013 11:50:29 AM EEST
 
Visgi, šios priemonės neatitinka visų poreikių, be to, lokalės turi būti įdiegtos pačioje operacinėje sistemoje.
Linux OS gauti įdiegtų lokalių sąrašą galima komanda locale -a
 
Projektas, sprendžiantis daugiau problemų, susijusių su unikodo (UTF) internacionalizacija, vadinamas ICU (Internationalization Components for Unicode): http://site.icu-project.org/
Norint naudotis ICU, operacinėje sistemoje turi būti įdiegt ICU biblioteka (Ubuntu atveju libicu*). PHP biblioteka vadinasi intl, ją galima įdiegti iš PECL repozitorijos arba Ubuntu aveju kaip paketą php5-intl.
 

Transliteravimas

 
Dažnai tenka ne lotyniškus simbolius paversti lotyniškais, pvz.: formuojant lotyniškais simboliais pagrįstus nuorodų fragmentus (slug).
Tam galima pasinaudoti Transliterator klase:
 
$id = "Any-Latin; NFD; [:Nonspacing Mark:] Remove; NFC; [:Punctuation:] Remove; Lower();";
$transliterator = Transliterator::create($id);
$string = "ąčiū!?_-&% ĄČĘĖĮŠŲŪŽ";
echo $transliterator->transliterate($string);
// aciu aceeisuuz
 
Slug atveju tarpo simbolius paversti brūkšneliais galima naudojant Regxp:
 
echo preg_replace('/\s+/', '-', 'tekstas   tekstas2');
// tekstas-tekstas2
 
Transliterator::create() perduodamą argumentą galima susiformuoti pagal ICU transformacijos gidą: http://userguide.icu-project.org/transforms/general
NFC, NFD - unikodo normalizacijos ir denormalizacijos funkcijos. Plačiau: http://en.wikipedia.org/wiki/Unicode_equivalence
 

Rikiavimas

 
Standartiškai masyvas PHP rikiuojamas (sort) taip:
 
$arr = ['urvas', 'ūkas', 'Ukmergė', 'ugnis'];
sort($arr);
print_r($arr);
// Array ( [0] => Ukmergė [1] => ugnis [2] => urvas [3] => ūkas ) 
 
Matome, jog rezultatas nėra teisingas (pirma rikiuojama didžioji raidė, lietuviška ū rikiuojama paskutinė).
 
Įprastu atveju galima sort() funkcijai nurodyti, kad būtų naudojama sistemos lokalė (perduodant SORT_LOCALE_STRING konstantos reikšmę kaip antrą parametrą):
 
setlocale(LC_ALL, 'lt_LT.UTF-8');
$arr = ['urvas', 'ūkas', 'Ukmergė', 'ugnis'];
sort($arr, SORT_LOCALE_STRING);
print_r($arr);
Array ( [0] => ugnis [1] => ūkas [2] => Ukmergė [3] => urvas )
 
Rezultatas teisingas, tačiau tai turi neigiamų pasėkmių, nes lokalė greičiausiai bus nustatoma globaliai, be to, ji turi būti įdiegta operacinėje sistemoje.
 
Kitas variantas yra naudoti Collator klasę:
 
$arr = ['urvas', 'ūkas', 'Ukmergė', 'ugnis'];
$collator = new Collator('lt_LT');
$collator->sort($arr);
print_r($arr);
// Array ( [0] => ugnis [1] => ūkas [2] => Ukmergė [3] => urvas )
 

Skaičių formatai

 
Skirtingose kalbose dažnai naudojami skirtingi skaičių (taip pat valiutos) formatai. Tinkamai juos atvaizduoti galima naudojant NumberFormatter klasę:
 
$ltNum = new NumberFormatter('lt_LT', NumberFormatter::CURRENCY);
echo $ltNum->formatCurrency(1234567890.25, 'LTL');
// 1,234,567,890.25 Lt
echo $ltNum->formatCurrency(1234567890.25, 'EUR');
// 1,234,567,890.25 €
echo $ltNum->formatCurrency(1234567890.25, 'USD');
// 1,234,567,890.25 US$

$enNum = new NumberFormatter('en_US', NumberFormatter::CURRENCY);
echo $enNum->formatCurrency(1234567890.25, 'LTL');
// LTL1,234,567,890.25
echo $enNum->formatCurrency(1234567890.25, 'EUR');
// €1,234,567,890.25
echo $enNum->formatCurrency(1234567890.25, 'USD');
// $1,234,567,890.25
 
Matome, jog skirtingos lokalės skirtingai atvaizduoja valiutas.
 
NumberFormatter klasė taip pat leidžia skaičių versti tekstu:
 
$ltNum = new NumberFormatter('lt_lt', NumberFormatter::SPELLOUT);
echo $ltNum->format(1234567890.25);
// vienas milijardas du šimtai trisdešimt keturi milijonų penki šimtai šešiasdešimt septyni tūkstančiai aštuoni šimtai devyniasdešimt kablelis du penki

$enNum = new NumberFormatter('en_US', NumberFormatter::SPELLOUT);
echo $enNum->format(1234567890.25);
// one billion two hundred thirty-four million five hundred sixty-seven thousand eight hundred ninety point two five
 
Tiesa, skaičius išverstas ne idealiai, tačiau žiūrint pozityviai, yra galimybė contribute'inti į ICU projektą ir prisidėti prie ICU tobulinimo :)
 

Teksto formatavimas

 
Verčiant tekstą į skirtingas kalbas svarbu ne tik užtikrinti paties teksto fizinį vertimą, bet ir datų, skaičių bei jų daugiskaitinių formų tinkamą atvaizdavimą.
Tam tinkama naudoti MessageFormatter klasė. Pavyzdžiui, atvaizduojant datas:
 
$enDate = new MessageFormatter('en_US', 'Today {0,date,short}');
echo $enDate->format(array(time()));
// Today 8/16/13
$enDate = new MessageFormatter('en_US', 'Today {0,date,long}');
echo $enDate->format(array(time()));
// Today August 16, 2013

$ltDate = new MessageFormatter('lt_LT', 'Šiandien {0,date,short}');
echo $ltDate->format(array(time()));
// Šiandien 2013-08-16
$ltDate = new MessageFormatter('lt_LT', 'Šiandien {0,date,long}');
echo $ltDate->format(array(time()));
// Šiandien 2013 m. rugpjūtis 16 d.
 
MessageFormatter konstruktoriui perduodame lokalę ir teksto formatą, o metodui format() masyvą duomenų, kuriuos apdoroja formateris. {0,date,short} reiškia, jog pirmą format() metodui perduoto atributo masyvo elementą naudosime kaip trumpo formato datą.
 
MessageFormatter taip pat aktualu naudoti su daugiskaitinėmis formomis, pvz.:
 
$x = new MessageFormatter('lt_LT', 'Parduodu {0, plural, one{{0,number} obuolį} few{{0,number} obuolius} other{{0,number} obuolių}} ir {1, plural, one{{1,number} bandelę}few{{1,number} bandeles}other{{1,number} bandelių}} už {2,number,currency}');
echo $x->format(array(15, 2, 15));
// Parduodu 15 obuolių ir 2 bandeles už 15.00 Lt
 
Daugiau informacijos apie teksto formatavimą: http://userguide.icu-project.org/formatparse/messages
 
Kadangi PHP manual'e intl funkcijos nėra pilnai dokumentuotos, jei rasite daugiau naudingų panaudojimo atvejų, pasidalinkite komentaruose. Būsiu dėkingas :)

 

« 1 »

Žymės RSS Žymės RSS