The Slim Framework support forum has moved to http://discourse.slimframework.com. This Tender forum is no longer maintained or monitored.

Multilingual site with Slim

Yury's Avatar

Yury

23 Jun, 2012 07:55 PM

Hello!

At first, thank you for the framework!

Could you help to understand how to create a multilingual site with Slim?

For example, I want my site to have such languages: en (default), de, ru.
Language identifier is presented in URL: http://example.com/ru/hello/
But the default language ('en' for this example) has not to be presented in URL.
An URL http://example.com/hello/ means English page, but http://example.com/en/hello/ means 404 error.

How it is better to achieve this?
I found that slim.before.dispatch hook, probably, may be used for this. Is it the right direction?

Or maybe you can advice some other techniques to create multilingual site with Slim?

Thank you and good luck!

Sincerely,
Yury Plashenkov

  1. Support Staff 1 Posted by Brian Nesbitt on 24 Jun, 2012 03:20 AM

    Brian Nesbitt's Avatar

    My first thought would be to have the language always in the URL. I think this will in general simplify your template logic. You can check if the language exists and if not assume english but have all languages (even default english) generate URLs with the language in it.

    I think the hook is a good idea and is my first instinct as well. I think you need to get higher up in the hook chain than slim.before.dispatch. I am thinking slim.before.router and potentially parse the lang out of the URI so your routes are kept simple.

    Let me try and whip something quick up and I'll re-post when I have something worth showing.

  2. 2 Posted by Yury on 24 Jun, 2012 04:27 AM

    Yury's Avatar

    Brian, thank you very much!
    Some example would be very helpful.

    I.e. I must take URL value, detect language by language ID in it, then remove the language ID, and URL will go to routes already without language ID. Is that right?

  3. 3 Posted by Yury on 25 Jun, 2012 06:48 AM

    Yury's Avatar

    I'm trying to detect the language in 'slim.before.router' hook, and that's not a problem.
    But the problem is to remove the language ID from the URL to have the same routes for different languages. I do not know how I can do that and it is possible or not.

    And if we will remove this language ID, then urlFor() or some similar functions (sorry, I have not learned this area yet) will return incorrect result. Am I right?

  4. 4 Posted by Yury on 25 Jun, 2012 08:48 AM

    Yury's Avatar

    I have come to the following solution.
    Created file multilingual.php with the following content:

    <?php
    
    class Lang {
    
        public static $languages = array();
    
        public static $in_url  = '';
        public static $current = '';
        public static $default = '';
    
        public static function detect_lang($app)
        {
            if (empty(Lang::$languages))
                return FALSE;
    
            // Find default language
            if ( ! Lang::$default)
            {
                foreach (Lang::$languages as $lang_id => $lang_data)
                {
                    if ($lang_data['default'])
                    {
                        Lang::$default = $lang_id;
                        break;
                    }
                }
                if ( ! Lang::$default)
                {
                    reset(Lang::$languages);
                    Lang::$default = key(Lang::$languages);
                }
            }
    
            // Detect current language based on request URI
            $lang_ids = array_keys(Lang::$languages);
            $lang_ids = array_diff($lang_ids, array(Lang::$default));
            $url = $app->request()->getResourceUri();
            if (preg_match('~^/('.implode('|', $lang_ids).')/~', $url, $matches))
            {
                Lang::$in_url  = '/'.$matches[1];
                Lang::$current = $matches[1];
            }
            else
            {
                Lang::$in_url  = '';
                Lang::$current = Lang::$default;
            }
    
            // Set locale if defined
            if ($locale = Lang::$languages[Lang::$current]['locale'])
            {
                setlocale(LC_ALL, $locale);
            }
        }
    
    }
    

    Then require_once it in index.php, fill the language list and call Lang::detect_lang($app). After that all the routes which must be multilingual, prepend with Lang::$in_url.

    Here is the example of index.php.

    <?php
    
    require_once 'Slim/Slim.php';
    $app = new Slim();
    
    // *** Define languages ***
    
    require_once 'multilingual.php';
    Lang::$languages = array(
        // Default language
        'en' => array(
            'name'    => 'English',
            'locale'  => 'en_US.utf-8',
            'default' => TRUE,
        ),
        // Other languages
        'de' => array(
            'name'    => 'Deutsch',
            'locale'  => 'de_DE.utf-8',
        ),
        'es' => array(
            'name'    => 'Español',
            'locale'  => 'es_ES.utf-8',
        ),
        'fr' => array(
            'name'    => 'Français',
            'locale'  => 'fr_FR.utf-8',
        ),
        'ru' => array(
            'name'    => 'Русский',
            'locale'  => 'ru_RU.utf-8',
        ),
    );
    Lang::detect_lang($app);
    
    // *** Set routes ***
    
    $app->get(Lang::$in_url.'/(:url)', 'test_get_func')
        ->conditions(array(
            'url'  => '.*'
        ));
    function test_get_func($url = 'index')
    {
        echo Lang::$current.'<br>';
        echo $url.'<br>';
    }
    
    $app->run();
    

    And it seems that this solution works well.
    Hope this will be useful for somebody.
    Please if you can advice or correct something I will be very grateful.

    Sincerely,
    Yury Plashenkov

  5. 5 Posted by Yury on 25 Jun, 2012 08:57 AM

    Yury's Avatar

    Defining the locale is optional, as well as the 'default' value. If no language is marked as 'default', the default language will be the first language in array.
    You can also set default language before Lang::detect_lang($app):

    Lang::$default = 'en';
    Lang::detect_lang($app);
    
  6. Support Staff 6 Posted by Brian Nesbitt on 26 Jun, 2012 01:13 AM

    Brian Nesbitt's Avatar

    You got the idea of parsing the language from the PATH_INFO (ie. $app->request()->getResourceUri()) but not extracting it. This means you must keep the language in the route definition which I think you can get away from, even though its a variable.

    I am working on a blog post on how I would approach this. I will be posting it tomorrow morning (EST) and I'll post a fully working 3 page application on github with a few enhancements. Hope it helps!

  7. 7 Posted by Yury on 26 Jun, 2012 04:24 AM

    Yury's Avatar

    Dear Brian,

    Does extracting language from URL mean removing it from there?
    But how is it possible?

    As far as I could see, when we write: $app = new Slim(), the first thing Slim does is gets request string by reading it from $_SERVER['REQUEST_URI']. And after that we cannot change its value. When Slim searches for a suitable route, it uses this value which I do not see the way to change (remove the language from it).

    Or maybe I simply do not understand your idea (because of level of my English) or do not understand Slim good enough :-)

    Therefore I am looking forward to your blog post. It will help without doubt.

  8. Support Staff 8 Posted by Brian Nesbitt on 26 Jun, 2012 12:34 PM

    Brian Nesbitt's Avatar

    You can read in the environment PATH_INFO variable and modify it. As long as it is done before the router starts to process it.

    Here is the blog posted I promised that should explain it all :-)
    http://nesbot.com/2012/6/26/multilingual-site-using-slim

  9. 9 Posted by Yury on 26 Jun, 2012 06:36 PM

    Yury's Avatar

    Dear Brian,

    Thank you! I did not guess to subclass the Slim application and other classes. Looks nice and clean.

  10. Brian Nesbitt closed this discussion on 29 Jun, 2012 01:03 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac