Using Apache2 Content Negotiation To Serve Different Languages - Page 2
On this page
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
- Apache2 Content Negotiation: http://httpd.apache.org/docs/2.0/content-negotiation.html
- mod_negotiation: http://httpd.apache.org/docs/2.0/mod/mod_negotiation.html
- Debian: http://www.debian.org/