4

I have some multi-language static content which I wish to serve via Apache. The files are cache files generated JIT by the app so if they're there, they should be served, otherwise forward the request to application which will create them in-place.

I specify the language via HTTP's Accept-Language header which can be hr, en, but also en-US,en;q=0.5 (so there isn't a simple "append language to filename to proceed").

The URLs are like:

in my app I have a folder with the exactly the same layout, only the language gets appended, like this:

/static/homepage.html.en
/static/homepage.html.hr
/static/script.js.en
/static/script.js.hr

I've figured out that for the Accept-Language parsing I need mod_negotiation and came up with this:

# Alias
/static /www/app/var/cache/static
<Directory /www/app/var/cache/static>
  FileETag MTime Size
  Options MultiViews
  AddLanguage hr .hr
  AddLanguage en .en
  <IfModule mod_negotiation.c>
    LanguagePriority en hr
  </IfModule>      

  ErrorDocument 404 /index.php
</Directory>

It works, but only for the first miss. So

  1. if I request http://www.example.com/static/homepage.html, Accept-Language: hr, it will create homepage.html.hr (correct)
  2. Now, if I request http://www.example.com/static/homepage.html, Accept-Language: en, it will not thrown a 404 (by which triggering a file generating process) but instead serve homepage.html.hr (which is close enough for Apache, but incorrect for me).

The solutions I see for this:

  • serving static files via PHP (which I'd like to avoid)
  • using mod_rewrite (but I'm missing the Accept-Language parsing part)
  • pre-generating all the files (which seems like a huge drag)

Is there a fourth way or something I'm missing?

Edit: the complete solution as per the accepted answer

# Alias
/static /www/app/var/cache/static
<Directory /www/app/var/cache/static>
  FileETag MTime Size
  Options MultiViews
  AddLanguage hr .hr
  AddLanguage en .en
  <IfModule mod_negotiation.c>
    LanguagePriority en hr
    ForceLanguagePriority none
  </IfModule>      

  ErrorDocument 404 /index.php
  ErrorDocument 406 /index.php
</Directory>
  • In which folder is your htaccess ? Could you paste its code in your question (i guess it's the one for your angular app ?) – Justin Iurman Sep 29 '14 at 10:32
  • @JustinIurman this is done via app-wide config and not via .htaccess. The code which generates the static file is a basic `if ($that not there) generate($that);` and the routing for the app is setup so that the exact same URL is used for the static file and for the application which generates it. This is done like this because we're generating pretty complex markup via backend components (for example, we have a rather complex system which generates a Angular-ready form from a YAML file, complete with translations, client-side validation, etc.). This is the reason we generate these files. – Dalibor Karlović Sep 29 '14 at 10:55
  • I'd suggest you make a small program that would generate your html files.. It might be useful for debugging and reproducing/reloading later? – Matej Sep 29 '14 at 13:41
  • @matejkramny that would be going the "pre-generating all the files" route which is not ideal as it adds unwanted complexity where there shouldn't be any. – Dalibor Karlović Sep 30 '14 at 09:15
  • Would be possible to do it with `mod_rewrite` but the problem is the language priority. Did you simply try to disable `MultiViews` option? Instead of `Options MultiViews` replace it by `Options -MultiViews` which could solve your problem since it's about Apache content negociation – Justin Iurman Oct 01 '14 at 09:15
  • @JustinIurman the purpose of MultiViews is that it is what provides the `Accept-Language` parsing facility here. If I disable it, I need to supplement the way to parse the `Accept-Language` for `mod_rewrite` which is the second solution (and accompanying problem) proposed by me in the question. – Dalibor Karlović Oct 01 '14 at 11:08
  • Well not exactly. MultiViews option is about Apache content negociation, which means if you enable it, it will automatically serve the first corresponding file. That's why you have always the same first file chosen – Justin Iurman Oct 01 '14 at 17:16

2 Answers2

1

Perhaps ForceLanguagePriority is set to "Prefer" or "Prefer Fallback". If you set it instead to "None", Apache will generate a 406 error, which you could configure to trigger the same file generating process as the 404.

Nick Russo
  • 1,522
  • 10
  • 13
  • Spot on! I've added `ForceLanguagePriority none` and `ErrorDocument 406 /index.php` (left 404 handler in there too) and it works. I knew that 406 gets thrown (even had a bug to fix in my Angular app to fix that triggered it) but never occurred to me to use it like this. Thanks man, a bounty well deserved. :) – Dalibor Karlović Oct 06 '14 at 07:25
0

With mod rewrite you can do it like this:

<Directory /www/app/var/cache/static>
  RewriteEngine On

  RewriteCond %{HTTP:Accept-Language} ^pl.*$ [NC]
  RewriteCond %{REQUEST_URI} ^.*(js|css)$ [NC]
  RewriteRule ^(.*)$ %{REQUEST_URI}.pl [L,R=302]

  RewriteCond %{HTTP:Accept-Language} ^hr.*$ [NC]
  RewriteCond %{REQUEST_URI} ^.*(js|css)$ [NC]
  RewriteRule ^(.*)$ %{REQUEST_URI}.hr [L,R=302]

  RewriteCond %{HTTP:Accept-Language} ^en.*$ [NC]
  RewriteCond %{REQUEST_URI} ^.*(js|css)$ [NC]
  RewriteRule ^(.*)$ %{REQUEST_URI}.en [L,R=302]

  # (...) do it for all languages       

  ErrorDocument 404 /index.php
</Directory>
walkeros
  • 4,736
  • 4
  • 35
  • 47