Ich habe mit i18n eine Angular-5-Anwendung erstellt, die sowohl Französisch als auch Englisch unterstützt. Ich habe dann für jede unterstützte Sprache eine separate Version der App bereitgestellt

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

Ich habe auch die folgende Nginx-Konfiguration hinzugefügt, um die Anwendung in beiden Sprachen bereitzustellen.

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

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

Ich wollte beide Anwendungen bedienen und den Wechsel zwischen der englischen und der französischen Version ermöglichen.

Nehmen wir zum Beispiel an, ich bin auf host.local/en/something Wenn ich zu host.local/fr/something wechsle, sollte ich die französische Version der Seite "etwas" erhalten.

Bei der von mir freigegebenen Nginx-Konfiguration wird bei jedem Aktualisieren von Seiten beim Durchsuchen meiner Apps ein Fehler 404 nicht gefunden angezeigt, der mich auch daran hindert, meine Apps unabhängig voneinander zu durchsuchen und zwischen ihnen zu wechseln.

Was habe ich verpasst? Was ist die geeignete Nginx-Konfiguration, um dies zu erreichen?

14
Ahmed Siouani 18 Apr. 2018 im 02:51

5 Antworten

Beste Antwort

Ich habe das gleiche auf iis gemacht, zuerst müssen Sie Ihre App mit der Option "base-href" erstellen:

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

Und für Nginx verwenden Sie diese Konfiguration

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;
}

Und für die Navigation von / en / someroute nach / fr / someroute können Sie die aktuelle Router-URL in Ihrer Komponente abrufen, in der Sie den Sprachumschalter haben

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

Und wenn Sie auf eine Sprachauswahl klicken, leiten Sie mit der ausgewählten Sprache auf dieselbe Route um:

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

Sprachmethode ändern

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);
    }
}
15
Fateh Mohamed 24 Apr. 2018 im 08:02

Nach dem Bauen mit:

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

Kopieren Sie die Builds in das Nginx-Serve-Verzeichnis:

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

Dann habe ich 2 verschiedene NGINX-Konfigurationen gefunden, die für mich funktionieren:

Root-Pfad verwenden

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;
    }
}

Alias ​​verwenden

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;
    }
}

Hinweis : In der Lösung Root Path können Sie die Option autoindex on entfernen, müssen aber auch den Teil $uri$args/ aus dem try_files oder Sie erhalten "Verzeichnisindex von" [Verzeichnis] ​​"ist verbotener Fehler".

Zu Ihrer Information : Sie finden nützliche diese netten Erklärungen auf ROOT vs ALIAS.

Versionen

Angular CLI: 6.0.7
Node: 8.11.2
Angular: 6.0.3
6
nyxz 15 Juni 2018 im 15:14

Dies ist meine Lösung, um dies für mehrere Projekte zu lösen:

nginx.conf

http {
    server {

        # Sets our default language (it's the angular template default language)
        set $defaultLang "de";

        listen 80;

        root /usr/share/nginx/html;
        index index.html;
        include /etc/nginx/mime.types;

        ################## IMPORTANT (don't change this) ##################

        # Make sure when routing to location, server uses the correct angular project subfolder
        # Matches the following urls:
        # http://localhost/de
        # http://localhost/de/
        # http://localhost/de/login
        # http://localhost/notexist/login => In this case, try_files doesn't found a matching index.html and jumps into the @languageFallback
        location ~ "^(/([a-z]{2,2})/)(/?.*)?$" {
          try_files $uri $uri /$2/index.html @languageFallback;
        }

        # Make sure when routing to the root the root index is used (and we redirect through the small JS script -> redirect.js)
        # Matches the following urls:
        # http://localhost
        # http://localhost/
        location / {
          try_files $uri $uri/ /index.html;
        }

        # Language fallback which is used when user tries to open a language which doesn't exist
        # E.g When user trying to open http://localhost/notexist/login but it doesnt exist, then we rewrite the url to
        # http://localhost/de/login
        location @languageFallback {
          rewrite "^(/([a-z]{2,2})/)(/?.*)?$" $scheme://$http_host/$defaultLang/$3 last;
        }
    }
}

Dann habe ich eine zusätzliche index.html mit diesem kleinen Skript, das in das Nginx-Stammverzeichnis kopiert wird:

Zusätzliche index.html

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta content="IE=edge" http-equiv="X-UA-Compatible"/>
  <title></title>
  <script>
    (function()
    {
      let redirectUrl;
      const supportedLanguages = ['de', 'en'];
      const fallbackLanguage = 'de';

      // Read browser locale and use this as default language (only when no locale in localstorage was found)
      let locale = (navigator.language || navigator['userLanguage']).slice(0, 2);
      const storedLocale = localStorage.getItem('locale');

      console.info('BROWSER LOCALE: ', locale);
      console.info('STORED LOCALE: ', storedLocale);

      //Check if a locale was already set in localstorage and use this or set the default language by default
      //and browsers locale is not supported by app we fallback to the fallback language
      if (!storedLocale)
      {
        if (supportedLanguages.indexOf(locale) === -1)
        {
          locale = fallbackLanguage;
        }
      }
      else
      {
        locale = storedLocale;
      }

      redirectUrl = location.origin + '/' + locale + '/';
      console.info('REDIRECT TO: ', redirectUrl);

      // Redirect to correct language
      location.replace(redirectUrl);
    })();

  </script>
</head>
<body>
</body>
</html>

Diese Datei wird verwendet, um einen Benutzer abhängig von seiner Browsersprache an das richtige Sprachunterprojekt weiterzuleiten oder wenn der Benutzer die Sprache in der Anwendung ändert, die im lokalen Speicher gespeichert ist, und diese Sprache mit höherer Priorität verwendet wird.

Und mein eckiger Sohn

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "",
  "projects": {
    "my-project": {
      "i18n": {
        "sourceLocale": {
          "code": "de",
          "baseHref": "/"
        },
        "locales": {
          "en": {
            "translation": "src/locales/messages.en.xlf",
            "baseHref": "/"
          }
        }
      },
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "sg",
      "schematics": {
        "@schematics/angular:component": {
          "style": "scss"
        }
      },
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "aot": true,
            "outputHashing": "all",
            "outputPath": "dist/my-project",
            "resourcesOutputPath": "assets/fonts",
            "baseHref": "/",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "assets": [
              "src/assets"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": [],
            "stylePreprocessorOptions": {
              "includePaths": [
                "src/app"
              ]
            }
          },
          "configurations": {
            "production": {
              "optimization": true,
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "i18nMissingTranslation": "error",
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "6kb"
                }
              ]
            },
            "dev": {
              "budgets": [
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "6kb"
                }
              ],
              "i18nMissingTranslation": "error"
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "my-project:build"
          },
          "configurations": {
            "dev": {
              "browserTarget": "my-project:build:dev"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "my-project:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "styles": [
              "src/styles.scss"
            ],
            "scripts": [],
            "assets": [
              "src/assets"
            ]
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "tsconfig.app.json",
              "tsconfig.spec.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        },
        "xliffmerge": {
          "builder": "@ngx-i18nsupport/tooling:xliffmerge",
          "options": {
            "xliffmergeOptions": {
              "srcDir": "src/locales",
              "genDir": "src/locales",
              "i18nFile": "messages.xlf",
              "i18nBaseFile": "messages",
              "i18nFormat": "xlf",
              "encoding": "UTF-8",
              "defaultLanguage": "de",
              "languages": [
                "en"
              ],
              "removeUnusedIds": true,
              "supportNgxTranslate": false,
              "ngxTranslateExtractionPattern": "@@|ngx-translate",
              "useSourceAsTarget": true,
              "targetPraefix": "",
              "targetSuffix": "",
              "beautifyOutput": true,
              "allowIdChange": false,
              "autotranslate": false,
              "apikey": "",
              "apikeyfile": "",
              "verbose": false,
              "quiet": false
            }
          }
        }
      }
    }
  },
  "defaultProject": "my-project"
}

Und zum Schluss noch ein wichtiges npm-Skript für dieses Zeug aus meiner package.json

"build": "ng build --prod --localize",
"i18n": "ng xi18n --format=xlf --output-path=src/locales --out-file=messages.xlf",
"xliffmerge": "ng run my-project:xliffmerge",
"translate": "npm run i18n; npm run xliffmerge"
1
dyingangel666 27 Apr. 2020 im 12:23

Es gibt ein häufiges Missverständnis in der Funktionsweise von http://nginx.org/r/try_files. Wenn Sie sich die Dokumente genauer ansehen (über den obigen Link), werden Sie feststellen, dass die ersten und Zwischenparameter in der Direktive try_files zwar vom Typ "Datei" sind, der letzte jedoch als "uri" bezeichnet wird. Dies gilt in Ihrem Fall, wenn Sie Ihre http://nginx.org/r/location auf Behandeln Sie den regulären Ausdruck angemessen (in Ihrem location - Code fehlt der Modifikator ~). Dies kann zu einer Endlosschleife führen, wie Sie in Ihren Kommentaren bestätigen.

Beachten Sie, dass die Verwendung der regulären Ausdrücke in Nginx im Allgemeinen nicht empfohlen wird, um eine maximale Leistung in einfachen Situationen zu erzielen, in denen die Verwendung von Regex genauso gut vermieden werden kann. Ich würde Ihnen daher empfehlen, zwei unabhängige Speicherorte zu haben, jeweils einen für Englisch und Französisch.

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

Beachten Sie, dass der obige Code davon ausgeht, dass Sie die korrekte URL-Behandlung in Ihrem Webapp-Frontend selbst ausführen. Wenn eine bestimmte Ressource von den Versionen /en/ und /fr/ gemeinsam genutzt wird, wird sie direkt über die / Basis ohne /en/ oder /fr/ Spezifizierer. Der =410 Teil des Codes verhält sich ähnlich wie =404, außer dass sich der Fehler geringfügig unterscheidet, um das Debuggen zu erleichtern, welche Direktive für den Fehler verantwortlich ist.

0
cnst 22 Apr. 2018 im 01:48

Angular 9 bietet eine neue Option zum gleichzeitigen Erstellen aller Sprachversionen. Außerdem wird die Basis-HREF für jede Version der Anwendung festgelegt, indem das Gebietsschema zur konfigurierten Basis-Referenz hinzugefügt wird.

ng build --prod --localize

Anschließend müssen Sie alle Builds in das Nginx-Serve-Verzeichnis kopieren

COPY /dist/my-app/ /usr/share/nginx/my-app/

Und konfigurieren Sie nginx wie in den vorherigen Antworten gezeigt.

0
lardum 29 Feb. 2020 im 06:38