<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"># written by Rob Hartill &lt;robh@imdb.com&gt;
# Weeeeeeeeeeeeeee

package Apache::AcceptLanguage;
use Apache::Options;
use Apache::Constants;
use strict;

sub handler {
    my $r = shift;
    my $DEBUG = 0;

    # declare all the variables we intend using, so that's out of the way
    my ($i, $offset, $ext, $extensions, $best, @matching, $acceptables,
	$file, $dir, $len, $serve, %prefs);

    # take an early bath if there's nothing to do
    return DECLINED if 
	!($r-&gt;allow_options &amp; OPT_MULTI)  # No Multiviews ? no point	
	    || !$r-&gt;is_main                      # a sub request
		|| -e $r-&gt;filename;                  # file exists

    # What extensions are we allowed to glue on to add and serve ?
    $extensions = $r-&gt;dir_config("AddOnExtensions");
    return DECLINED unless $extensions;  # Not allowed to add anything ?

    # Rip open the Accept-Language header and create a hash of
    #  lang=q  pairs for q&gt;0 and where "lang" is in 'extensions' (allowed)

    foreach (split(/,/, $r-&gt;header_in("Accept-Language"))) {
	s/\s//g;  # strip spacing, leave only the interesting stuff
	if (m#;q=([\d\.]+)#) {
	    $prefs{$`}=$1 if $1 &gt; 0 &amp;&amp; index($extensions, $`)&gt;=0;
	} else {
	    $prefs{$_}=1  if index($extensions, $_)&gt;=0;
	}
    }
    # prepend the user's ordered prefs to the server's ordered prefs
    $extensions = 
	join(" ", sort {$prefs{$b} &lt;=&gt; $prefs{$a}} keys %prefs)
	    . " " . $extensions;

    # create a string list of extensions we accept ( | separated)
    ($acceptables = $extensions) =~ s/ /|/g;

    print STDERR "Serve Preferences = $extensions\n" if $DEBUG;

    # Open the directory containing the file
    ($dir = $r-&gt;filename) =~ s#[^/]*$##;
    $file = $&amp;;
    return DECLINED if 
	$file eq "" || !opendir(DIR, $dir);  # nowt to wildcard match

    print STDERR "WILDCARDing ^$file(\\.($acceptables))+\$\n" if $DEBUG;
    # Grep out only files with allowed extensions that match the filename
    $len = length($file);   # +1 for the first \.

    @matching = map substr($_, $len+1),
    grep(/^$file(\.($acceptables))+$/, readdir(DIR));
    closedir(DIR);
    return DECLINED unless @matching;  # give up if nothing matches

    $best = 999;  # lowest scoring file wins. Start high
    $serve = 0;   # assume the first file is the winner

    for($i=0; $i &lt;= $#matching; $i++) {
	print STDERR "Found file with extension(s): $matching[$i]\n" if $DEBUG;
	foreach $ext (split(/\./, $matching[$i])) {
	    # check the score (low = better) for each extension
	    $offset = index($extensions, $ext);
	    if ($offset &lt; $best) {
		$best = $offset;
		$serve = $i;
		last if $best == 0; # it doesn't get any better
	    }
	}
	print STDERR " Best score so far = $best\n" if $DEBUG;
    }
    print STDERR "Best file was ".$r-&gt;filename.".$matching[$serve] with $best\n" if $DEBUG;

    # Tell Apache we have a new filename
    $r-&gt;filename("$dir$file.$matching[$serve]");
    # Give Apache the new files "stat" info
    $r-&gt;refresh_finfo;

    # Let other modules have their wicked way with the request.
    return DECLINED;
}

1;

__END__

Basically it works with Options 'Multiviews' and 

  PerlSetVar AddOnExtensions "en fr it de html"


This line tells the handler that give a request for "foo" that fails to
find a match, it is allowed to add on .en .fr  etc (and combinations
of them of course) to find a matching file.
The code looks for an "AcceptLanguage" header from the client to give some
languages a priority over others. So if I have

Accept-Language: en-gb;q=0.8, da, fr;q=0.9

then I prefer "Danish, French then 'British English'". If the server
doesn't have any of these, it goes through the allowed extensions and
picks whichever file contains the earliest match, so this forces ".en"
to be a default if there's nothing better, and we'll always be happy to
add on ".html", so ".en.html" is what I prefer people to default to if
they don't find a better preference.

The reason I wrote this is that mod_negotiation.c is HUGE and it doesn't
allow defaults, e.g. if a browser asks for Danish and we don't have any,
they get an error message (the behaviour is correct but unfriendly). I don't
know how HTTP kosher my algorithm is but it's better for all the MSIE
users with broken Accept-Language settings.


PerlTypeHandler Apache::AcceptLanguage


</pre></body></html>