SitePoint Sponsor

User Tag List

Results 1 to 17 of 17
  1. #1
    FreeBSD The Power to Serve silver trophy pippo's Avatar
    Join Date
    Jul 2001
    Location
    Italy
    Posts
    4,514
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Multi-lingual web site.

    Hi there,

    this summer I'm going to renew my bi-lingual web site.
    Every pages can be viewed in all-languages.

    Actually I have something (cutted and pasted) like this:

    PHP Code:
    function get_msg$msg )
    {
       
    // etc
       
    $msg_list = array(
          
    'news' => array( 'it' => 'Novità''en' => 'News' ),
       
    // etc

       // etc
       
    return $msg_list$msg ][ $lang ];

    and from my pages I call something like:

    PHP Code:
       print get_msg'news' ); 
    I was looking for some better ideas to achieve that.
    For example instead of using 'news' I could use a constant such as MSG_NEWS.

    Any hints?


    pippo
    Mr Andrea
    Former Hosting Team Advisor
    Former Advisor of '03

  2. #2
    FreeBSD The Power to Serve silver trophy pippo's Avatar
    Join Date
    Jul 2001
    Location
    Italy
    Posts
    4,514
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    One hint could be to put the array that contains the messages outside from get_msg function, so that I will not have to build that array each time I call the get_msg function ( get_msg is called many times from a page ).
    Mr Andrea
    Former Hosting Team Advisor
    Former Advisor of '03

  3. #3
    ********* Celica Lover Coomer's Avatar
    Join Date
    Apr 2002
    Location
    Not worth the drive
    Posts
    474
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Can you go into more detail on how you use multiple languages on your website? I'm actually in the process of writing a website that will be available in spanish and english, so whatever tips anyone has would be very helpful.
    + Celica =
    6G Celicas :: My '94-99 Toyota Celica resource

  4. #4
    SitePoint Columnist Skunk's Avatar
    Join Date
    Jan 2001
    Location
    Lawrence, Kansas
    Posts
    2,066
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This looks like an ideal application for PHP's parse_ini_file() function.

    http://www.php.net/manual/en/functio...e-ini-file.php

    You could set up an external ini file with the different language translations for your site messages:
    Code:
    [italian]
    news = "Novità"
    ; more Italian messages here
    
    [english]
    news = "News"
    ; more English messages here
    To display messages you can then use the following:
    Code:
    $language = 'english'; // or 'italian'
    $messages = parse_ini_file('messages.ini', true);
    echo $messages[$language]['news']; // Outputs "News"
    Hope that helps,

    Simon

  5. #5
    FreeBSD The Power to Serve silver trophy pippo's Avatar
    Join Date
    Jul 2001
    Location
    Italy
    Posts
    4,514
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Skunk,
    that seems a very good idea ( store messages into an external file, .ini for example ).
    Making this suggestion you refreshed my memory, I used .ini in VB for something similar.
    You did a good point, I didn't thought about that!

    I have a doubt if using a constant to identify the message name such as:
    PHP Code:
    define'MSG_NEWS''news' ); 
    or using directly 'news'.
    The constant approach will make it more independent,
    I think that I will adopt it.

    Can you go into more detail on how you use multiple languages on your website?
    That was my first and only php application ( I used it for fan for my personal web site ), actually I'm going to rewrite it with some adjustments and trying to following some tips from people here about oop, etc.
    Two centes [ of euro ] about that,
    Skunk I liked your explanation of "is-a" and "has-a" about intheritance etc.

    Actually:

    The site url layout is:
    mywebsite.com
    ( without www, if you type www I will redirect it to mywebsite.com )
    that is the default language, italian in my case because 90% of visitors are italians

    en.mywebsite.com
    for english language, in the future If I will add a language I will have de.mywebsite.com for example.

    So I'm using a subdomain to store a different language,
    but I use only one script for each page.

    No cookies for storing language,
    even if it could be an idea to store the last language visited so that if someone ( an english man ) type mysite.com he will be redirected to en.mywebsite.com directly.

    No extensions or file name modifications or subdirectories,
    such as /en/index.html or /index.it.html or /index-it.html etc

    The directory layout will be the same for all languages.
    To detect the language I will check HTTP_HOST enviroment var.
    I will check if I'm calling the page from "en.mywebsite.com or mywebsite.com" and I will set a constant
    PHP Code:
    define'APP_LANG''en' 
    accordingly.

    To display messages in every page I will use something like:
    PHP Code:
    //etc
    print '<a href="/">'.get_msg'home_link' ).'</a>&nbsp;';
    print 
    '<a href="/article">'.get_msg'article_link' ).'</a>';
    //etc 
    That will make my pages language independent,
    and I have only one copy of them.

    In addition I will use this url path layout:
    /article to display all articles
    /article/33 to display article 33
    that is done using apache's mod_rewrite

    my database will be something like:
    table article
    id
    pub_date
    published
    author1
    auhtor2

    table article_en
    id
    title
    description
    content

    table article_it
    id
    title
    description
    content

    that is an excursus of what I did many months ago (last year),
    now I'm planning to do it better....or trying...

    Suggestions are always welcomed...

    pippo


    p.s.
    *** edited ***
    After reading what I write maybe this could be antoher table layout:

    table article
    id
    pub_date
    published
    author1
    auhtor2

    table article_data
    id
    lang
    title
    description
    content
    primary key(id, lang)
    Last edited by pippo; Jul 30, 2002 at 00:52.
    Mr Andrea
    Former Hosting Team Advisor
    Former Advisor of '03

  6. #6
    FreeBSD The Power to Serve silver trophy pippo's Avatar
    Join Date
    Jul 2001
    Location
    Italy
    Posts
    4,514
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Following Skunk's suggestion,
    this could be an implementation of a message class:

    PHP Code:
    <?php
    /*
     ** File Name  : msg.inc.php
     ** Description: Message handling
    */


    /*
     ** Class Name : Msg
     ** Description: Generic message class
    */
    class Msg
    {
        var 
    $lang;
        var 
    $msg_list;

        
    /*
         ** Function Name: Msg
         ** Parameters   : $file_name, file name where messages are stored
         **                $lang     , language id
         ** Return       : none
         ** Description  : Class constructor
        */
        
    function Msg$file_name$lang )
        {
            
    $this->lang     $lang;
            
    $this->msg_list = @parse_ini_file$file_nameTRUE );
        }

        
    /*
         ** Function Name: getText
         ** Parameters   : $msg_id, message id
         ** Return       : Text of message
         ** Description  : Get the text of the message
        */
        
    function getText$msg_id )
        {
            
    /*
             An empty message is returned when:
             a) the language id does not exist
             b) the messages id does not exist
             c) the ini file does not exist
            */
            
    if ( !isset( $this->msg_list$this->lang ][ $msg_id ] ) )
            {
                return 
    '';
            }

            return 
    $this->msg_list$this->lang ][ $msg_id ];
        }
    }

    /*** TEST ***/
    /* pippo.ini is:
    [en]
    my_name = "my name is pippo"

    [it]
    my_name = "il mio nome e' pippo"
    */

    define'MSG_MYNAME''my_name' );

    $msg = new Msg"pippo.ini"'it' );

    print 
    $msg->getTextMSG_MYNAME );
    /*** END OF TEST ***/


    /* End Of File msg.inc.php */
    ?>
    corrections are welcomed

    p.s.
    Eh, eh be tolerant for my english comments...
    Mr Andrea
    Former Hosting Team Advisor
    Former Advisor of '03

  7. #7
    SitePoint Columnist Skunk's Avatar
    Join Date
    Jan 2001
    Location
    Lawrence, Kansas
    Posts
    2,066
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That class is an excellent idea. The only thing I would question is the need for the MSG_MYNAME constant - why not just call $msg->getText('my_name') ? Constants are best used for defining a human-readable name for an otherwise meaningless number - there is little point in using them to define a name for a string that is already similar to the name you define with the constant

  8. #8
    FreeBSD The Power to Serve silver trophy pippo's Avatar
    Join Date
    Jul 2001
    Location
    Italy
    Posts
    4,514
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Uhmm, I will follow your tips.

    In addition,
    it will be usefull in some rare situations where I have to build the name of the message too.

    Example:

    PHP Code:
    $msg->getText"day_of_week$day
    Last edited by pippo; Jul 30, 2002 at 03:39.
    Mr Andrea
    Former Hosting Team Advisor
    Former Advisor of '03

  9. #9
    SitePoint Addict richard_h's Avatar
    Join Date
    May 2002
    Location
    London
    Posts
    301
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm currently writting a multi language website.

    The method I'm using just includes files depending on their language preference. I have seperate files for english,
    portuguese, spanish, german and french.

    A typical file would look like this:
    PHP Code:
    $navOrder   'order online';

    $navContact 'contact';

    $navCopy    'copyright';

    $navSearch  'search'
    My templates then reference these variables.

    It makes things very simple this way, the above examples
    seem like overkill in my opinion.

  10. #10
    SitePoint Evangelist
    Join Date
    Oct 2001
    Posts
    592
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    The general way of supporting multiple languages is like this:

    - Store translations for each language in some dictionary; each language has its own dictionary.
    - At the start of the program, find out which language is being used and select the right dictionary.
    - At run-time translate texts using the selected dictionary..

    This is more or less what pippo describes. A few observations, however:

    - There's a ready-to-use system available in PHP called 'GNU Gettext'. On Unix this can work almost out of the box, as Gettext is almost certainly installed on those systems. On Windows it's possible as well, but a bit harder. (I never got it to work on any platform, by the way.)

    - Translating an identifier takes (a lot of) time, especially when implementing it yourself. You have to implement some lookup-function that traverses through your dictionary to find the translation for some identifier. If you sort the dictionary on the identifiers you can use a binary search, which goes very fast (O (log n)), but...

    - Storing the dictionary costs (a lot of) memory. Normally you have some file with the dictionary in it, and on program startup you read the dictionary into memory (with parse_ini_file for example, although this is a bad idea, as you're loading translations for ALL languages, even the ones you're not going to use!). A large dictionary costs a lot of memory; and when your site is large (has a lot of pages), you'll be memorizing a lot of translations you don't use all the time. You could be loading a dictionary with a 1000 translations in it into memory, only to look up 10 identifiers for some page. That's a waste. (Imagine you used parse_ini_file for this and you supported 5 languages. That would mean reading 5 * 1000 translations into memory, while using only 10!)

    - Both the time- and memory-problems can be solved by implementing a binary search on file descriptors: at program startup you open the right language-specific dictionary file (with translations sorted on identifier), and the search is implemented right on the file, using fseek to find identifiers and their translations. The only problem with this approach is that it produces a lot of disk accesses, and that it isn't trivial to implement.

    Partly because I couldn't get GNU's gettext to work, I implemented a dictionary myself some time ago. It uses simple textfiles to lookup translations given an identifier, and there's a file for every language I support on the site. A typical dictionary file looks like this:

    PHP Code:
    identifier            translation
    SITE_TITLE            
    Name of the site
    PAGE_NEWS_TITLE       
    News
    PAGE_NEWS_DESCRIPTION 
    The latest news for this site
    .... 
    At program startup I read the language-specific dictionary into memory for the selected language, and then I can do this:

    PHP Code:
    $title Translator::getText('PAGE_NEWS_TITLE');
    $descr Translator::getText('PAGE_NEWS_DESCRIPTION'); 
    (Note: class Translator is a Singleton class with static methods. This makes the translator easily accessible to all parts of the code, while no global variables are used.)

    The textfiles needn't be sorted on identifier, because I read the whole file into memory at application startup, and use linear search to lookup translations. As you can imagine, this is very slow and memory-consuming. To overcome both problems I cache the static parts of the pages on a site; there's a cache file for every page and every language. Only if no cache file exists the dictionary module is loaded to translate identifiers on the page to language-specific messages. Once that is done I save it in the cache, so that I don't have to use the dictionary anymore. I have found that this works extremely well. I already used a cache to store other static information I didn't want to compute every time, and all I did was make it language-specific. It's simple, fast, and introduces little overhead. For example:

    PHP Code:
    $page = new Page('news');
    if (!
    Cache::loadObject($page))
    {
        include_once(
    'Translator.php');
        
    $page->setTitle(Translator::getText('NEWS_TITLE'));
        
    Cache::saveObject($page);

    Class Cache has static-methods only (it's very simple and has no member variables), and uses the currently selected language to find out which file it should load from the cache directory. If anyone wants more information on how this all works, just let me know.

    Finally, I'd like to comment on this little post by pippo:

    I will check if I'm calling the page from "en.mywebsite.com or mywebsite.com" and I will set a constant define('APP_LANG', 'en') accordingly.
    Although this probably works very well, this is not good coding. A constant is a value that never changes, not only during one run of an application, but during EVERY run of an application. This is not true in this case (the language can change), so it shouldn't be a constant at all.

    Vincent

  11. #11
    FreeBSD The Power to Serve silver trophy pippo's Avatar
    Join Date
    Jul 2001
    Location
    Italy
    Posts
    4,514
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Vincent,
    first of all thank you for what you said.

    I heard about gettext function and I found an article that explained how to use it with php, but it seemed too complicated at first glance.
    http://www.onlamp.com/lpt/a/2429

    About what you said about constants, that's right.

    I like the Translator static class.

    I'm interested about the Page class and the cache object too.

    About Pages I was thinking to have a page base class like:

    Class Page
    - ShowHeader()
    - ShowFooter()
    - ShowNavbar()
    - ShowLayout( $nav_bar, $body )

    and then a specific page like:

    Class PageArticle
    - ShowArticle()
    - ShowArticlesList()

    so from my article page have something like:

    PHP Code:
    $page = New PageArticle(); // "derived" from Page class

    $page->ShowArticle$article_obj ); // url: /article/3
    //or
    $page->ShowArticleList(); // url: /article
    //or
    $page->ShowArticlePrinted$article_obj ); // url: /article/33/printed 
    I'm curios that you are using Page( 'article' ).

    I have some different kinds of article pages,
    /article/33 // show article 33
    or
    /article // show article list
    /article/33/printed // a printed version of article 33

    I was just asking if my approach of using Page and PageArticle classes was right.

    Maybe instead I could have something like:

    PHP Code:
    main.php // catch all pages
    $page = new Pageexplode"/""$REQUEST_URI) );
    $page->Show(); 
    but I have no idea how to implement the show method...

    Could you suggest some approaches ?!?

    Thanks,

    pippo
    Mr Andrea
    Former Hosting Team Advisor
    Former Advisor of '03

  12. #12
    will code HTML for food Michel V's Avatar
    Join Date
    Sep 2000
    Location
    Corsica
    Posts
    552
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally posted by voostind
    - There's a ready-to-use system available in PHP called 'GNU Gettext'. On Unix this can work almost out of the box, as Gettext is almost certainly installed on those systems. On Windows it's possible as well, but a bit harder. (I never got it to work on any platform, by the way.)
    I was about to post about GNU GetText, heh.
    One possible reason you didn't get it to work is that in addition to having it installed on the system, you have to compile PHP with support for it (--with-gettext, IIRC). Or you didn't use the fmt-something utility. I got it to work on Windows (I only had phpdev4, not GNU GetText installed so it's still a wonder it worked), and one webhost.

    I was considering using the GNU GetText functions in my news script, but the very low number of webhosts that have support for it is a great deterrent

  13. #13
    SitePoint Addict MarekS's Avatar
    Join Date
    Jan 2003
    Location
    Estonia / Tallinn
    Posts
    201
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi pippo, voostind and others,

    My previous approach (way to do it) was to keep a different dict file for each page/language. But the problem was that ... as the site grew I often found myself lost between N dict files.

    Quote Originally Posted by voostind
    The text files needn't be sorted on identifier, because I read the whole file into memory at application startup, and use linear search to lookup translations. As you can imagine, this is very slow and memory-consuming. To overcome both problems I cache the static parts of the pages on a site; there's a cache file for every page and every language. Only if no cache file exists the dictionary module is loaded to translate identifiers on the page to language-specific messages. Once that is done I save it in the cache, so that I don't have to use the dictionary anymore. I have found that this works extremely well.
    This seems to be fairly good approach.

    But before I dive into it... I'd like to consider other options.

    I sure plan to write a small admin interface to help to change (update) the textual content. This makes me think if I should use the DB for the dictionary (and not file)? Although there are ways to insert/update/delete text/ini files too... What say you? How well does DB suit for this job?

    If I put the dict into DB table - then this basically means I should put the available languages also into DB (another table). And in the lang table I set one language as the default... this is already like a configuration? Nope - I cannot put the default lang setting into DB - that would sort of breaks up my beautiful configuration logic

    What if I simply (duplicate) put the available languages and the default language into the PHP file and access the DB only when I get some text? This way I do not need to connect to DB in the execution phase where I set user language. The problem is… I have same information (available/supported languages) in 2 places. Tis does not sound/feel good. What if I only use DB for the translations and keep the languages/encodings (fi=iso-8859-1) in PHP side? Then there is a chance that some languages that are defined in PHP are not actually supprted (translated) in DB?

    OK, here is another Q: are all your translations in a table/dict sorted somehow? With some naming convention or… I mean you sometimes do have different page titles for different pages?
    <TITLE>www.demo.com/articles/jup/</TITLE>
    <TITLE>www.demo.com/feedback/</TITLE>
    How do you NOT get lost in your dictionary? How do you know if a certain record in your dict is in use at all? (this is not a big problem though) How do you call for a certain record?

    sry... for thinking loud in here - again
    I’m currently redesigning my PHP framework and I have so damn many things running in my head


    - MarekS -

  14. #14
    SitePoint Enthusiast
    Join Date
    Jun 2003
    Location
    Ljubljana, Slovenia
    Posts
    83
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm usualy doing it like this

    create a table

    PHP Code:
    create table dictionary (
        
    id integer unsigned not null primary key auto_increment,
        
    code varchar(255not null,
        
    lng_si text,
        
    lng_en text,
        
    lng_de text,
        
    lng_yu text,

    and the function
    PHP Code:


    //lets suppose SESSION has language info stored in $_SESSION['lng']=en

    function get_dictionary($code=""){
      if(
    $code){
         
    $sql="select lng_".$_SESSION['lng']." as label from dictionary where code='".$code."'";
         
    //submit query

        
    return $this_query_row['label'];
     }

    bye Armando

  15. #15
    ********* wombat firepages's Avatar
    Join Date
    Jul 2000
    Location
    Perth Australia
    Posts
    1,717
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    on 2 different threads .. 1)have you seen how fast SQLite is ?

    I am using it in a GUI with a static call sq_trans::trans($lang,$key);

    and fetching from the $lang table the appropriate content as indexed by $key (though $lang could be a constant of course)

    as sqlite supports triggers you could also get it to grab data from the default language if no translation is available (save all those possible duplications) or whatever.

    just a thought , as I said I use it in a GUI and the speed of the thing (ok we are only talking a few hundred records here) is impressive , I would suggest on a par with flatfile access.

    But 2) if your site is likely to be really busy why not take a peek at apache's content negotiation built into apache with little configuration needed ?

  16. #16
    SitePoint Enthusiast
    Join Date
    May 2002
    Posts
    27
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I've no problem using gettext on windows or linux but there's a problem if you're using solaris because they using non compatible version of gettext

    there's a good tutorial about this

    http://zez.org/article/articleview/42

    I think gettext is the _way_ to go if you want to do a translation or multilanguage site and it's really simple and fast.

    That's why horde framework www.horde.org using only gettext to do their translation

  17. #17
    SitePoint Member
    Join Date
    Aug 2002
    Location
    Linz, Austria
    Posts
    17
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    problem with gettext is, that if you change one of the translation files you have to recompile them and have to restart the server. also you can use only one language per page. if you want to create a dropdown menu with all the available languages in the corresponding language (for example: English, Deutsch, Français,…) it won’t work. Further if you want to use more than one translation file per page you have to hand the domain with the translation string to the gettext function.
    too many disadvantages for that 0,1 sec increased speed if you ask me

    http://sourceforge.net/projects/php-flp/


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •