Nginx configuration for angular i18n application

2020-02-10 23:40发布

问题:

I built an angular-5 application using i18n that supports both french and english. I then deployed a separate version of the app for each supported language

 - dist
    |___ en/
    |    |__ index.html
    |___ fr/
         |__ index.html

I also added the following nginx configuration to serve the application in both languages;

server {
    root /var/www/dist;
    index index.html index.htm;
    server_name host.local;

    location ^/(fr|en)/(.*)$ {
        try_files $2 $2/ /$1/index.html;
    }
}

What I wanted to do is to serve both applications and allow switching between the english and the french version.

Let's say for example I'm on host.local/en/something if I switch to host.local/fr/something I should get the french version of the "something" page.

With the nginx configuration I shared, I get a 404 not found error every time I refresh pages when browing my apps which also prevents me from browsing my apps independently from one another and switching between them.

What did I miss? What's the appropriate Nginx conf to achieve that?

回答1:

i've done the same thing on iis, first you have to build your app with "base-href" option:

 ng build --output-path=dist/fr --prod --bh /fr/
 ng build --output-path=dist/en --prod --bh /en/

and for nginx use this config

location /fr/ {
  alias /var/www/dist/fr/;
  try_files $uri$args $uri$args/ /fr/index.html;
}
location /en/ {
 alias /var/www/dist/en/;
 try_files $uri$args $uri$args/ /en/index.html;
}

and for navigation from /en/someroute to /fr/someroute , you can get the current router url in your component where you have the language switcher

getCurrentRoute() {
    return this.router.url;
}

and when click on a language selector you redirect to the same route with the selected language :

 <li *ngFor="let language of languages;let i=index" >
    <a  href="/{{language.lang}}/#{{getCurrentRoute()}}"  (click)="changeLanguage(language.lang)">
        {{language.lang}}
    </a>
 </li>

change language method

changeLanguage(lang: string) {
    const langs = ['en', 'fr'];
    this.languages = this.allLanguages.filter((language) => {
        return language.lang !== lang;
    });
    this.curentLanguage = this.allLanguages[langs.indexOf(lang)].name
    localStorage.setItem('Language', lang);
    if (isDevMode()) {
        location.reload(true);
    }
}


回答2:

After building with:

ng build --prod --base-href /fr/ --output-path dist/fr
ng build --prod --base-href /en/ --output-path dist/en

copy the builds to nginx serve directory:

cp -r dist/* /usr/share/nginx/my-app

Then I found 2 different NGINX configs that work for me:

Using root path

server {
    root /usr/share/nginx/my-app;
    location /en/ {
        autoindex on;
        try_files $uri$args $uri$args/ /en/index.html;
    }

    location /fr/ {
        autoindex on;
        try_files $uri$args $uri$args/ /fr/index.html;
    }

    # Default to FR
    location / {
        # Autoindex is disabled here + the $uri$args/ is missing from try_files
        try_files $uri$args /fr/index.html;
    }
}

Using alias

server {
    listen 80 default_server;
    index index.html;

    location /en/ {
        alias /usr/share/nginx/my-app/en/;
        try_files $uri$args $uri$args/ /en/index.html;
    }

    location /fr/ {
        alias /usr/share/nginx/my-app/fr/;
        try_files $uri$args $uri$args/ /fr/index.html;
    }

    # Default to FR
    location / {
        alias /usr/share/nginx/my-app/fr/;
        try_files $uri$args $uri$args/ /fr/index.html;
    }
}

Note: In the root path solution you can remove the autoindex on option but you'll also have to remove the $uri$args/ part from the try_files or else you'll get "directory index of "[directory]" is forbidden error".

FYI: You can find useful those nice explanations on ROOT vs ALIAS.

Versions

Angular CLI: 6.0.7
Node: 8.11.2
Angular: 6.0.3


回答3:

There is a common misunderstanding in the way that http://nginx.org/r/try_files works. If you look closer in the docs (from the above link), you'll notice that although the first and intermediate parameters in the try_files directive are of type "file", the final one is called the "uri"; which, in your case, once you fix your http://nginx.org/r/location to handle the regexp appropriately (your location code is missing the ~ modifier), may result in an infinite loop, as you confirm in your comments.

Note that generally, the regular expressions are not recommended to be used in nginx for maximum performance in simple situations where regex use might as well be avoided, so, I'd recommend you to have two independent locations, one each for English and French.

location /fr/ {
    try_files $uri /fr/index.html =410;
}
location /en/ {
    try_files $uri /en/index.html =410;
}

Note that the above code assumes that you perform correct URL handling from within your webapp frontend itself — if a given resource is shared between the /en/ and /fr/ versions, then it would be requested directly via the / base, without a /en/ or /fr/ specifier. The =410 part of the code behaves similarly to how =404 would, except that the error is slightly different to make it easier to debug which directive is responsible for the error.