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?
5 Antworten
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);
}
}
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
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"
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.
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.
Verknüpfte Fragen
Neue Fragen
angular
Fragen zu Angular (nicht zu verwechseln mit AngularJS), dem Webframework von Google. Verwenden Sie dieses Tag für Winkelfragen, die nicht für eine einzelne Version spezifisch sind. Verwenden Sie für das ältere AngularJS (1.x) -Webframework das AngularJS-Tag.