77

Views and language support

Create site2 by copying site1.

  1. /cms
    1. site0
    2. site1
    3. site2

In this chapter, we are going to program how a URL is parsed and dispatched in different views depending on the requested language.

To test the result online, enter http://www.frasq.org/cms/site2 in the address bar of your navigator. End the URL with /fr to display the page in French, with /en to see it in English.

Create the folder views in site2, then the folders en and fr in site2/views.

  1. /cms/site2
    1. includes
    2. library
    3. views
      1. en
      2. fr

Modify index.php after the call to bootstrap:

  1. define('ROOT_DIR', dirname(__FILE__));
  2.  
  3. set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_DIR . DIRECTORY_SEPARATOR . 'library');
  4. set_include_path(get_include_path() . PATH_SEPARATOR . ROOT_DIR . DIRECTORY_SEPARATOR . 'includes');
  5.  
  6. require_once 'dump.php';
  7.  
  8. require_once 'bootstrap.php';
  9.  
  10. bootstrap();
  11.  
  12. require_once 'engine.php';
  13.  
  14. dispatch($supported_languages); // see config.inc

index.php calls the function dispatch defined in the file engine.php with in argument the list of the languages supported by the program. Add the parameter $supported_languages in config.inc:

  1. global $supported_languages;
  2.  
  3. $supported_languages=array('fr', 'en');

The first language defined by $supported_languages is the default language. The code for a language must match the name of a folder in views.

Add the files engine.php, request_uri.php and locale.php in the folder library with the following contents:

  1. /cms/site2
    1. library
      1. engine.php
      2. requesturi.php
      3. locale.php
  1. require_once 'requesturi.php';
  2.  
  3. define('VIEWS_DIR', ROOT_DIR . DIRECTORY_SEPARATOR . 'views');

Loads the code of the request_uri function. Defines the folder in which the files which generate the views are grouped.

  1. function dispatch($languages) {

dispatch determines how the document corresponding to the URL of the request is generated according to the supported languages specified by $languages.

  1.     global $base_path;
  2.  
  3.     $req = $base_path ? substr(request_uri(), strlen($base_path)) : request_uri();
  4.  
  5.     $url = parse_url($req);
  6.     $path = isset($url['path']) ? trim(urldecode($url['path']), '/') : false;
  7.     $query = isset($url['query']) ? $url['query'] : false;
  8.  
  9.     if (empty($path)) {
  10.         $path = false;
  11.     }

Withdraws the prefix $base_path from the normalized URL returned by the request_uri function/ Extracts the access path and the parameters from the request.

  1.     /* site language */
  2.     $p = $path ? explode('/', $path) : false;
  3.     $lang = $p ? $p[0] : false;
  4.  
  5.     if ($lang && in_array($lang, $languages, true)) {
  6.         array_shift($p);
  7.         $path = implode('/', $p);
  8.     }
  9.     else {
  10.         require_once 'locale.php';
  11.  
  12.         $lang=locale();
  13.  
  14.         if (!$lang or !in_array($lang, $languages, true)) {
  15.             $lang = $languages[0];
  16.         }
  17.     }

Computes the display language of the site. If $path begins by one of the languages contained in $languages, the language is extracted from the URL. Otherwise, $lang is set to the value returned by the locale function if it is supported or by default to the first language listed in $languages.

  1.     if (!$path)
  2.         $path='home';
  3.  
  4.     $output=view($path, $lang);
  5.    
  6.     if ($output) {
  7.         echo $output;
  8.     }
  9. }

Takes the view called home by default. Generates the document by calling the function view with the name of the view and the language in argument.

  1. function view($view, $lang=false) {
  2.     $file = $lang ? VIEWS_DIR.DIRECTORY_SEPARATOR.$lang.DIRECTORY_SEPARATOR.$view.'.phtml' : VIEWS_DIR.DIRECTORY_SEPARATOR.$view.'.phtml';
  3.     if (!file_exists($file)) {
  4.         header('HTTP/1.0 404 Not Found');
  5.         exit;
  6.     }
  7.     return render($file);
  8. }

view locates the file corresponding to $view depending on the language specified by $lang. view returns the rendering of the file. If no file is found, view returns an HTTP 404 Not Found error document.

  1. function render($file) {
  2.     global $base_path, $base_url, $base_root;
  3.     ob_start();
  4.     require $file;
  5.     return ob_get_clean();
  6. }

render places the variables $base_path, $base_url and $base_root in the global context before loading the file $file. render returns the document generated by $file.

  1. function request_uri() {
  2.     if (isset($_SERVER['REQUEST_URI'])) {
  3.         $uri = $_SERVER['REQUEST_URI'];
  4.     }
  5.     else {
  6.         if (isset($_SERVER['argv'])) {
  7.             $uri = $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['argv'][0];
  8.         }
  9.         elseif (isset($_SERVER['QUERY_STRING'])) {
  10.             $uri = $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
  11.         }
  12.         else {
  13.             $uri = $_SERVER['SCRIPT_NAME'];
  14.         }
  15.     }
  16.  
  17.     $uri = '/'. ltrim($uri, '/');
  18.  
  19.     return $uri;
  20. }

request_uri normalizes the format of the request passed in a URL.

  1. function locale() {
  2.     if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
  3.         return false;
  4.     }
  5.  
  6.     $httplanguages = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
  7.  
  8.     if (empty($httplanguages) === true) {
  9.         return false;
  10.     }
  11.  
  12.     $lang = false;
  13.     $quality = 0.0;
  14.  
  15.     $accepted = preg_split('/,\s*/', $httplanguages);
  16.  
  17.     foreach ($accepted as $accept) {
  18.         $match = null;
  19.         $result = preg_match('/^([a-z]{1,8}(?:[-_][a-z]{1,8})*)(?:;\s*q=(0(?:\.[0-9]{1,3})?|1(?:\.0{1,3})?))?$/i', $accept, $match);
  20.  
  21.         if ($result < 1) {
  22.             continue;
  23.         }
  24.  
  25.         $q = isset($match[2]) ? (float) $match[2] : 1.0;
  26.  
  27.         if ($q > $quality) {
  28.             $quality = $q;
  29.  
  30.             $lang = current(explode('_', current(explode('-', $match[1]))));
  31.  
  32.             if ($quality == 1.0) {
  33.                 break;
  34.             }
  35.         }
  36.     }
  37.  
  38.     return $lang;
  39. }

locale analyzes the PHP variable $_SERVER['HTTP_ACCEPT_LANGUAGE'] to extract the preferred language of the sender of an HTTP request.

The home page displays a version in English and a version in French. The content of each version is placed in a file called home.phtml in the folders en and fr of the views folder. A file with a .phtml extension contains code in HTML and some code in PHP limited to inserting computed data. In a view, only the presentation of the data is taken care of.

  1. /cms/site2
    1. views
      1. fr
        1. home.phtml
      2. en
        1. home.phtml

Start by writing the version in English:

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <title>Home</title>
  5. </head>
  6. <body>
  7. <h3>Welcome</h3>
  8. </body>
  9. </html>

Add the translation in French:

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <title>Accueil</title>
  5. </head>
  6. <body>
  7. <h3>Bienvenue</h3>
  8. </body>
  9. </html>

Try different addresses like http://localhost/cms/site2, http://localhost/cms/site2/en, http://localhost/cms/site2/fr or http://localhost/cms/site2/en/nowhere.

Comments

To add a comment, click here.