Using Apache2 Content Negotiation To Serve Different Languages - Page 2

Want to support HowtoForge? Become a subscriber!
 
Submitted by falko (Contact Author) (Forums) on Tue, 2011-07-26 16:34. ::

Now let's add further languages to the end of our language preferences (like de and fr):

As you see, Firefox sends a modified Accept-Language header, with en-US and en still being the first and second preference, and de and fr added with lower q values. Therefore, Apache will still serve the en variant:

Now let's change the order of languages in Firefox. For example, move de to the top of the list:

Apache will now serve the de variant, and you see that de has now the top priority in the Accept-Language header:

Now let's delete all languages from our browser's preferences and add languages (like es and it) for which Apache does not have any variants:

Apache will now serve a 406 "Not Acceptable" error because no variant matches:

To solve this problem, we add the LanguagePriority and ForceLanguagePriority directives to our vhost:

vi /etc/apache2/sites-available/default

[...]
        <Directory /var/www/>
                Options Indexes FollowSymLinks MultiViews
                DirectoryIndex index.html
                AllowOverride None
                Order allow,deny
                allow from all
                LanguagePriority en de fr
                ForceLanguagePriority Fallback
        </Directory>
[...]

LanguagePriority specifies a precendence of language variants for cases where the client does not express a preference (list language variants with decreasing priority from left to right, which means that in LanguagePriority en de fr, en has the highest and fr the lowest priority).

ForceLanguagePriority Fallback uses the LanguagePriority list to serve a valid variant instead of a 406 "Not Acceptable" error in cases where the browser accepts only language for which no variants are available. In this case, the en variant would be served (if it is available), then the de variant (if there's no en variant), and then the fr variant (if there are no en or de variants).

Restart Apache...

/etc/init.d/apache2 restart

... and reload the page, and you should get the en variant now instead of the 406 error:

Theoretically it is also possible that a client sends an Accept-Language header where languages have the same priority (e.g. Accept-Language: en;q=0.5,de;q=0.5). In this case Apache would send a 300 "Multiple Choices" result because it cannot decide which of the available variants to serve. To prevent this, add Prefer to the ForceLanguagePriority directive:

vi /etc/apache2/sites-available/default

[...]
        <Directory /var/www/>
                Options Indexes FollowSymLinks MultiViews
                DirectoryIndex index.html
                AllowOverride None
                Order allow,deny
                allow from all
                LanguagePriority en de fr
                ForceLanguagePriority Prefer Fallback
        </Directory>
[...]

This makes that if two or more variants match with the same priority, the first matching variant from the LanguagePriority directive will be served. (In case of the Accept-Language: en;q=0.5,de;q=0.5 header, that would be en.)

Restart Apache:

/etc/init.d/apache2 restart

In general, it's a good idea to use the

LanguagePriority [list of languages]
ForceLanguagePriority Prefer Fallback

directives together.

 

3 Type Maps

Instead of using MultiViews, we can use a type map to explicitliy specify the available variants. Type maps have the extension .var, and we need to add a AddHandler type-map var directive to our configuration. Also, we remove MultiViews from the Options line. Since Apache has no chance of knowing that it should read index.var when we request http://192.168.0.100/, we need to specify index.var in the DirectoryIndex line. (This is a drawback of the type map way - for example, if you have a foo.var file and request foo, Apache has no way of knowing that it should read foo.var. You'd have to add foo.var to the DirectoryIndex line and request http://192.168.0.100/ instead of http://192.168.0.100/foo.)

Our configuration looks as follows:

vi /etc/apache2/sites-available/default

[...]
        <Directory /var/www/>
                Options Indexes FollowSymLinks
                DirectoryIndex index.var
                AllowOverride None
                Order allow,deny
                allow from all
                AddHandler type-map var
                LanguagePriority en de fr
                ForceLanguagePriority Prefer Fallback
        </Directory>
[...]

Restart Apache:

/etc/init.d/apache2 restart

Now we create our /var/www/index.var file as follows:

vi /var/www/index.var

URI: index

URI: index.html.en
Content-Type: text/html
Content-Language: en

URI: index.html.de
Content-Type: text/html
Content-Language: de

URI: index.html.fr
Content-Type: text/html
Content-Language: fr

The type map file should have the same name as the resource it describes (e.g. index.var for index.html.de, index.html.en, and index.html.fr). It must have an entry for each available variant, and entries for different variants must be separated by a blank line (blank lines are not allowed within an entry). Entries consist out of contiguous HTTP header lines (see http://httpd.apache.org/docs/2.0/mod/mod_negotiation.html#typemaps for more details). It is a convention to begin a map file with a line for the entity as a whole, although this is not required and will be ignored (i.e., the line URI: index at the beginning is not required).

That's it! You can now do the same tests as in chapter 2, and if nothing went wrong, the correct variant should be served.

 

4 Links


Please do not use the comment function to ask for help! If you need help, please use our forum.
Comments will be published after administrator approval.