Multilanguage site: language on URL as dir, file e

2020-07-30 04:17发布

问题:

By now my site was only a one-language site.

I had my files on (suppose):

www.mysite.com/files/index.php
www.mysite.com/files/header.php
www.mysite.com/files/page1.php
www.mysite.com/files/page2.php

Now I added a multilanguage support to those files. All the words/texts are stored on the database and a php class complete the stuff.

By now I had (badly) on header.php this line:

define('LNG', 'en');

and I had a header.php file placed on each language directory:

www.mysite.com/en/header.php
www.mysite.com/de/header.php
...

and then I included the files depending on the LNG constant, e.g.

include_once "/files/page1.php?l=".LNG

but I know this is a very bad practise!

How can I solve this problem? I would mantain my files on www.mysite.com/files/... and then write the user language in the URL, so that the file can read the language from the url and load the right textes and words from the databases.

Sort of:

www.mysite.com/en/index.php

which in fact loads

www.mysite.com/index.php

with the correct language en. I'm able to get the en from the URL and define a constant on the page, but if I point to www.mysite.com/en/index.php I obviously get a 404...

回答1:

Dealing with multi-language web-applications

LANGUAGE CONSTANTS could be placed in language files within a language directory like this

/ languages
  en.php
  it.php
  ...

while language files may look as following

// en.php
define("THANK_YOU", "Thank you!");
define("PRODUCTS", "Products");
define("PRODUCT", "Product");
...

// it.php
define("THANK_YOU", "Grazie!");
define("PRODUCTS", "Prodotti");
define("PRODUCT", "Prodotto");
....

or they could be stored into database as shown below. I've used both but I prefer the first solution

+------------+------------+------------+-----
| constant   | en         | it         | ...
+------------+------------+------------+-----
| THANK_YOU  | Thank you! | Grazie!    |
| PRODUCTS   | Products   | Prodotti   |
| PRODUCT    | Product    | Prodotto   |
| ...        | ...        | ...        |
+------------+------------+------------+-----

FRIENDLY URLs may look like these below

http://www.domain.com
http://www.domain.com/en
http://www.domain.com/it
http://www.domain.com/en/products
http://www.domain.com/it/prodotti
http://www.domain.com/en/product/our-best-product/555
http://www.domain.com/it/prodotto/il-nostro-miglior-prodotto/555

having in mind rules that should be defined in .htaccess file

RewriteRule ^([a-z]+)(/)?$                                    index.php?lang=$1 [QSA,L]
RewriteRule ^([a-z]+)/(products|prodotti)(/)?$                index.php?lang=$1&action=$2 [NC,L]
RewriteRule ^([a-z]+)/(product|prodotto)/(.*)/([0-9]+)(/)?$   index.php?lang=$1&action=$2&id=$4&mode=details [NC,L]

# these rules are for a demonstration purpose; otherwise, all three directives 
# could be written in one singe line / rule   

or you can redirect the request (as following) to an entry point and parse it with parse_url().

RewriteCond  %{REQUEST_FILENAME} !-f
RewriteCond  %{REQUEST_FILENAME} !-d
RewriteRule  ^(.*)$ index.php?param=$1 [QSA,L] 

ACCESSING THE lang VARIABLE - On multi-language projects variable lang has a special significance, otherwise it would be treated and passed around just like any other variable using GET, POST, COOKIE, XMLHttpRequest, HTTP headers, HTTP referer, URL hash parameter, database session, APC (Alternative PHP Cache) ...

If you care about UX and SEO the lang variable should be written within URL. I prefer using $_SESSION or $_GET and it should be pointed that some of the methods mentioned above doesn't work always or may get blocked by a Proxy.

The snippet below shows how to access it using $_SESSION or $_GET

// config.php
// ...
$cfgObj->default_language = "en";
// ...
$_SESSION["lang"] = isset($_GET["lang"]) ? $_GET["lang"] : $cfgObj->default_lang;
include($cfgObj->dir_languages . $_SESSION["lang"] . ".php");

// index.php
switch(isset($_GET["action"]) ? $_GET["action"] : "default"){
    case PRODUCTS: case PRODUCT: case 3:
         // deal with products
         break:
    case ARTICLES: case ARTICLE: case 5:
         // deal with articles
         break:
    default: 
         // deal with short-links 
         // domain.com/3/555 <-- this would retrieve product with id 555 
         break;
}

DATABASE SCHEMA could be designed like following

+-----------------+       +-----------------------+       +--------------------+
| products        |       | products_to_languages |       | languages          |
+-----------------+       +-----------------------+       +--------------------+
| product_id [PK] |---|   | ptl_id           [PK] |   |---| language_id   [PK] |
| price           |   |---| product_id       [FK] |   |   | language_code      |
| status          |       | languge_id       [FK] |---|   +--------------------+
| ...             |       | product_name          |  
+-----------------+       | product_description   |
                          +-----------------------+

* I use language_code as a primary key as well, and an index on all foreign keys