Latest forum activity...
Nike AIR MAX 2017 Blancas Baratas Online
Forum: Raster
(0 Replies) View latest post
ewwHarley
prije 1 dan, 14h
Adidas Ultra Boost 3.0 Mystery Bleu soldes
Forum: Raster
(0 Replies) View latest post
ewwHarley
prije 1 dan, 14h
Vans Authentic Azules Baratas Online
Forum: WIP
(0 Replies) View latest post
wsxnat
prije 2 dana, 15h
Adidas Pro Model Green Online Shopping
Forum: WIP
(0 Replies) View latest post
wsxnat
prije 2 dana, 15h
These new "Rolesor" models are also
Forum: FFA
(0 Replies) View latest post
jafanshen
prije 7 dana, 15h
No one in the movie even ever glances
Forum: FFA
(0 Replies) View latest post
jafanshen
prije 7 dana, 15h
I was asked to write an article making
Forum: FFA
(0 Replies) View latest post
jafanshen
prije 7 dana, 15h
Time and Gems offers six DLC covered
Forum: FFA
(0 Replies) View latest post
jafanshen
prije 7 dana, 15h
 
 To the forum... 
Pošalji ovu stranicu prijatelju


Flash (2)
Freehand (4)
HTML (4)
Photoshop (7)
Programiranje - PHP (3)


 
» Username:
» Password:
Autologin?

 
» Registrirajte se!
» Zaboravili ste password?
56 korisnika je trenutno online
 
19 @ 0.026440
x



Izvedba višejezičnog sajta: gettext i php
Gettext je odličan u situacijama gdje postoji puno različitih riječi odnosno fraza koje treba prevesti. Držanje prevodljivih fraza u ovakvim ili onakvim arrayima postaje komplicirano kad njihova ukupna količina naraste.
Programiranje - PHP
05.12.2004 00:48 05.12.2004 00:48 5    


printer friendly version

Izvedba višejezičnog sajta: gettext i php

Ovaj tutorial je ujedno i follow-up na topic: Izvedba višejezičnog sajta

Za i protiv

Gettext je odličan u situacijama gdje postoji puno različitih riječi odnosno fraza koje treba prevesti. Držanje prevodljivih fraza u ovakvim ili onakvim arrayima postaje komplicirano kad njihova ukupna količina naraste.

Za:

  • jednostavno prikupljanje prevodljivih fraza s cijelog sitea, s malom mogućnošću pogreške
  • jednostavno prevođenje - korištenjem softvera koji podržavaju .po kataloge
  • jednostavno ubacivanje prijevoda u web site
  • jednostavno dodavanje novih prevodljivih fraza odnosno promjena postojećih - gettext prepoznaje ako se radi o djelomičnoj promjeni teksta prevodljive fraze, i kao mogući prijevod automatski nudi prijašnju verziju prijevoda, tako da prevoditelji imaju manje posla
Protiv:
  • potrebno je ponovo kreirati cijeli katalog za svaku promjenu - zna biti nezgodno ako se radi o sitnim promjenama
  • na malim projektima ponekad overhead, druga rješenja mogu biti prikladnija ako se radi o samo nekoliko desetaka fraza koje su specifične za pojedini jezik

Preduvjeti

  • web server
  • php
  • gettext modul
    • unix: gettext modul treba biti ukompajliran u php: --with-gettext
    • win: gettext modul treba biti uključen u php.ini datoteci: extension=php_gettext.dll
  • gettext utilities

Malo o gettextu..

Preneseno iz gettext manuala:
Specifically, the GNU gettext utilities are a set of tools that provides a framework within which other free packages may produce multi-lingual messages.
These tools include :

  • A set of conventions about how programs should be written to support message catalogs.
  • A directory and file naming organization for the message catalogs themselves.
  • A runtime library supporting the retrieval of translated messages.
  • A few stand-alone programs to massage in various ways the sets of translatable strings, or already translated strings.
GNU gettext is designed to minimize the impact of internationalization on program sources, keeping this impact as small and hardly noticeable as possible. Internationalization has better chances of succeeding if it is very light weighted, or at least, appear to be so, when looking at program sources.

The Translation Project also uses the GNU gettext distribution as a vehicle for documenting its structure and methods. This goes beyond the strict technicalities of documenting the GNU gettext proper. By so doing, translators will find in a single place, as far as possible, all they need to know for properly doing their translating work. Also, this supplemental documentation might also help programmers, and even curious users, in understanding how GNU gettext is related to the remainder of the Translation Project, and consequently, have a glimpse at the big picture.

Korak po korak..

  • prevodljive fraze se na odgovarajući način označe u source datotekama
  • sve source datoteke se proparsaju s gettext utilitijem 'xgettext' koji kreira .po datoteku (katalog prevodljivih fraza)
  • ako već postoji katalog fraza, njega se spoji s novim katalogom koristeći gettext utility 'msgmerge'
  • .po datoteka se editira tj. u nju se upišu prijevodi za svaku od fraza. To se može napraviti u običnom text editoru ili kroz neku od aplikacija koje podržavaju rad s .po katalozima (npr: poEdit)
  • od prevedene .po datoteke se kreira binarna .mo datoteka korištenjem utilitija 'msgfmt'. Tu datoteku će php funkcija _() koristiti za traženje prijevoda za pojedinu frazu
  • u php-u se setiraju parametri ovisno o trenutno odabranom jeziku
  • svaki poziv na _("fraza") će vratiti frazu prevedenu na trenutno odabrani jezik (ako prijevod za tu frazu postoji). ako prijevod ne postoji, _() funkcija će vratiti originalnu frazu

Označavanje prevodljivih fraza u source datotekama

Sve prevodljive fraze se pišu u defaultnom jeziku, i enkapsuliraju u _(""). npr. Danas je dobar dan postaje _("Danas je dobar dan."). Sintaksa _() je zapravo alias na php funkciju gettext() koja će u katalogu s prijevodima pronaći odgovarajući prijevod za traženu frazu.
Default jezik: ako se radi o siteu primarno na hrvatskom jeziku, čiji sadržaj se treba prezentirati i na drugim jezicima, onda predlažem hrvatski kao defaultni jezik. U ostalim slučajevima, engleski bi trebao biti default.

Primjer A:

<?php
echo _("Danas je dobar dan");
?>
Primjer B:
<div>_("Danas je dobar dan")</div>
Primjer B pokazuje označavanje prevodljive fraze u html sourceu. U tom slučaju, html source treba obraditi prije nego što ga se pošalje klijentu (browseru), odnosno svako pojavljivanje _("fraza") zamijeniti s outputom koji će dati _() php funkcija. To je najlakše napraviti korištenjem output buffera.

<?php
    ob_start();
?>
<div>_("Danas je dobar dan")</div>
<?php
    $buffer = ob_get_contents();
    ob_end_clean();

    $buffer = preg_replace('/_\("(.+?)"\)/se', "_('\\1')", $buffer);
    echo $buffer;
?>
Kad već koristimo output buffering, možemo otići korak dalje i kompresirati output koji šaljemo prema browseru. Tekstualni podaci su iznimno pogodni za kompresiju (za razliku od slika), zbog čega se postižu odlični rezultati sažimanja. U najvećem broju slučajeva output ima 10-15% veličine originala.

Primjer B2:
<?php
    ob_start();
?>
<div>_("Danas je dobar dan")</div>
<?php
    $buffer = ob_get_contents();
    ob_end_clean();

    $buffer = preg_replace('/_\("(.+?)"\)/se', "_('\\1')", $buffer);

    ob_start('ob_gzhandler');
    echo $buffer;
?>

Kreiranje kataloga prevodljivih fraza

Ako postoji veći broj source datoteka koje sadržavaju prevodljive fraze, nezgodno je pokretati xgettext za svaku od source datoteka.
Ovo je skripta koju smo napisali i koristimo ju za automatsko parsanje svih source datoteka unutar direktorijskog stabla pojedinog web sitea.

Napomena: ako se radi o windows platformi, path do gettext utilitija mora biti unesen u PATH environment varijablu.

Workflow:

  • rekurzivno prolazi kroz sve datoteke unutar stabla direktorija od kuda je pokrenuta
  • svaku datoteku koja odgovara definiranim kriterijima (ima odgovarajuću extenziju) parsa korištenjem xgettext utilitija i dodaje u privremeni katalog fraza
  • ako u direktoriju locale/language/LC_MESSAGES postoji .po katalog, spaja novi katalog s postojećim, u suprotnom samo kreira novi katalog
  • ako u direktoriju locale/language/LC_MESSAGES postoji .po katalog, kreira njegovu binarnu .mo verziju

<?php
/*
 * Gettext utility
 *  - creation of .po catalogs - recursive crawl through directory tree
 *  - update of .mo binary catalogs for all available languages
 *
 * (C) Copyright Burza d.o.o. (http://web.burza.hr)
 *               Vanja Bertalan
 *
 * Usage
 *  - should be run as a shell script, from the web sites' root directory
 *  - locale directory should exist, along with coresponding 
 *    language subdirectories
 *  - each language subdirectory should have LC_MESSAGES subdirectory
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, US*
 */

    /* CONFIGURATION START */

    $exclude_dirs = 'images|javascript|sql|tools|css';  // directory names that should be skipped
    $extensions = 'php|html|tpl';                       // filename extensions that should be parsed
    $translations_dir = '/tools/translations';         // directory for storage of created catalogs
    $domain = 'catalog';                                // domain name, basename of .po and .mo files

    /* CONFIGURATION END */


	@set_time_limit (0);
	@ignore_user_abort(1);

    $no_location = 0;
    $file_list = array();
    $file_no = 0;

    $start_dir = preg_replace('/\\\/', '/', getcwd());
    $start_dir = preg_replace('/\/tools$/', '', $start_dir);
    $locale_dir = $start_dir . '/locale/';
    $translations_dir = $start_dir . $translations_dir;

    // get translatable files
    crawl($start_dir);

    chdir($translations_dir);

    // create fresh catalog file from translatable files
    foreach ($file_list as $filename) {
        
        echo "$filename\n";

        $input = file($filename);
        $out = '';
        foreach ($input as $line) {
            $out .= $line;
        }
        $out = preg_replace('/"(_\("[^"]*"\)[^"]*)"/s', "\\1", $out);
        $out = preg_replace('/\'(_\("[^"]*"\)[^"]*)\'/s', "\\1", $out);

        $filename .= '.tmp';

        $handle = fopen($filename, 'wb');
        fwrite($handle, $out);
        fclose($handle);
    
        $command = 'xgettext --keyword=_ --sort-by-file --default-domain=' . $domain . ".allstrings -C --force ";
        if ($file_no) {
            $command .= "--join-existing ";
        }
        if ($no_location) {
            $command .= "--no-location ";
        }
        $command .= $filename;

        system($command, $return_code);
        if ( $return_code != 0 ) {
            echo "error: $filename\n";
	    }

        unlink ($filename);
        $file_no++;

    }

    // get language directories
    if ($handle = opendir($locale_dir)) {
        while (false !== ($file = readdir($handle))) { 
            if (is_dir($locale_dir.$file) && !preg_match('/^\.{1,2}$/', $file)) { 
                $dirs[] = $file;
            } 
        }
        closedir($handle); 
    }

    // create po file for every language
    // if po file already exists, merge it with new catalog
    foreach ($dirs as $d) {

        $def_file_po = $locale_dir . $d . "/LC_MESSAGES/" . $domain . ".po";
        $def_file_mo = $locale_dir . $d . "/LC_MESSAGES/" . $domain . ".mo";
    
        if (is_file($def_file_po)) {
            echo "\ncatalogue $d already exists\nmerging changes to translations directory: \n";
            $command = "msgmerge -o $domain.$d.po $def_file_po $domain.allstrings.po";
            system($command, $return_code);
            $command = "msgfmt -o $def_file_mo $def_file_po";
            system($command, $return_code);
            echo "created binary catalogue for domain $d\n";
        } else {
            echo "\ncatalogue $d does not exists\ncreating new catalog in translations directory\n";
            copy("$domain.allstrings.po", "$domain.$d.po");
        }

    }

    unlink ("$domain.allstrings.po");

    function crawl($dir) {

        global $domain, $file_list, $exclude_dirs, $extensions;

        echo "$dir\n";

        if ($handle = opendir($dir)) {
            while (false !== ($file = readdir($handle))) { 
                if (is_dir($dir.'/'.$file) && !preg_match('/^\.{1,2}$/', $file) && !preg_match("/($exclude_dirs)/", $file)) { 
                    $dirs[] = $file;
                } 
                if (is_file($dir.'/'.$file)) { 
                    $files[] = $file;
                } 
            }
            closedir($handle); 
        }

        if (is_array($files)) {
            foreach ($files as $f) {
                if (!preg_match("/\.($extensions)$/", $f)) { continue; }
                $file = $dir.'/'.$f;
                echo "\t$f\n";
                array_push($file_list, $file);
            }
        }

        if (is_array($dirs)) {
            foreach ($dirs as $d) {
                $file = $dir.'/'.$d;
                crawl($file);
            }
        }

    }

?>


Download skripte: create_translations.php.txt (preimenovati u .php)

Napomena: apache treba restartati nakon što se updatea binarni .mo katalog.

PHP implementacija

Da bi _() funkcija znala za koji jezik treba prikazivati prijevode, treba joj javiti o kojem se jeziku radi i gdje se nalaze katalozi s prijevodima. U primjeru je definiran i $language_code za hrvatski jezik, no on nam zapravo nije potreban, jer će _() po defaultu prikazati originalnu frazu ako prijevod nije pronađen.

<?php

    switch ($language) {

        case 'en':
        $language_code = 'en_US';
        break;

        case 'hr':
        default:
        $language = 'hr';
        $language_code = 'hr_HR';
        break;

    }

    if ((ini_get('safe_mode') == FALSE) && (getenv('LC_ALL') != $language_code)) {
        putenv('LC_ALL=' . $language_code);
    }
    setlocale(LC_ALL, $language_code);
    bindtextdomain('catalog', 'locale/');   // 'catalog' je naziv (basename) .po odnosno .mo datoteka, 'locale/' je naziv direktorija unutar kojih se nalaze prijevodi
    textdomain('catalog');                  // 'catalog' je naziv (basename) .po odnosno .mo datoteka

?>

Cijeli kod bi izgledao ovako:
<?php

    ob_start();

    $language = $_GET['language'] ? $_GET['language'] : ($_SESSION['language'] ? $_SESSION['language'] : 'hr');

    switch ($language) {

        case 'en':
        $language_code = 'en_US';
        break;

        case 'hr':
        default:
        $language = 'hr';
        $language_code = 'hr_HR';
        break;

    }

    $_SESSION['language'] = $language;

    if ((ini_get('safe_mode') == FALSE) && (getenv('LC_ALL') != $language_code)) {
        putenv('LC_ALL=' . $language_code);
    }
    setlocale(LC_ALL, $language_code);
    bindtextdomain('catalog', 'locale/');
    textdomain('catalog');

    echo Danas je dobar dan;

?>

<div>Danas je dobar dan</div>

<?php

    $buffer = ob_get_contents();
    ob_end_clean();

    $buffer = preg_replace('/_\("(.+?)"\)/se', "_('\\1')", $buffer);

    ob_start('ob_gzhandler');
    echo $buffer;

?>

Linkovi

Nastavak rasprave ili pitanja -> na forumu.

Autor: njava


« povratak

Komentari

re: Izvedba višejezičnog sajta: gettext i php
gg! :)
dao ti bog vise vremena da dobijemo jos ovakvih tutorijala :)
 Komentar: che.UP 05.12.2004 02:43
re: Izvedba višejezičnog sajta: gettext i php
There's no better way to spend saturday afternoon : )
 Komentar: maratz 05.12.2004 08:08
re: Izvedba višejezičnog sajta: gettext i php
bravo njava! gg!
 Komentar: ars 06.12.2004 08:20
re: Izvedba višejezičnog sajta: gettext i php
l33t -> mnogo filen dankešn
 Komentar: stripe 07.12.2004 00:22
re: Izvedba višejezičnog sajta: gettext i php
Bravo & hvala!
 Komentar: Already Dead 08.08.2007 19:21
** Trenutno niste ulogirani pa ne možete ni dodavati komentare **