我使用支持法语和英语的i18n构建了一个angular-5应用程序。然后,我为每种支持的语言部署了一个单独的应用程序版本
- dist |___ en/ | |__ index.html |___ fr/ |__ index.html
我还添加了以下nginx配置,以两种语言提供应用程序;
server { root /var/www/dist; index index.html index.htm; server_name host.local; location ^/(fr|en)/(.*)$ { try_files $2 $2/ /$1/index.html; } }
我想要做的是为两个应用程序提供服务,并允许在英语和法语版本之间切换。
比方说我在host.local/en/something
如果我切换到host.local/fr/something
我应该得到法语版的“某事”页面。
使用我共享的nginx配置,每次刷新我的应用程序时刷新页面都会出现404未找到的错误,这也会阻止我独立浏览我的应用程序并在它们之间切换。
我错过了什么?什么是适当的Nginx conf来实现这一目标?
答案 0 :(得分:6)
我在iis上做了同样的事情,首先你必须使用" base-href"来构建你的应用程序。选项:
ng build --output-path=dist/fr --prod --bh /fr/
ng build --output-path=dist/en --prod --bh /en/
并且对于nginx使用此配置
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;
}
并且从/ en / someroute导航到/ fr / someroute,您可以在组件中获取当前路由器URL,其中包含语言切换器
getCurrentRoute() {
return this.router.url;
}
当点击语言选择器时,您将使用所选语言重定向到相同的路线:
<li *ngFor="let language of languages;let i=index" >
<a href="/{{language.lang}}/#{{getCurrentRoute()}}" (click)="changeLanguage(language.lang)">
{{language.lang}}
</a>
</li>
更改语言方法
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);
}
}
答案 1 :(得分:4)
建成之后:
ng build --prod --base-href /fr/ --output-path dist/fr
ng build --prod --base-href /en/ --output-path dist/en
将构建复制到nginx服务目录:
cp -r dist/* /usr/share/nginx/my-app
然后我发现2个不同的NGINX配置对我有用:
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;
}
}
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;
}
}
注意:在根路径解决方案中,您可以删除autoindex on
选项,但您还必须删除$uri$args/
部分try_files
或者你会得到“directory index of "[directory]" is forbidden error”。
仅供参考:您可以找到有用的those nice explanations on ROOT vs ALIAS。
Angular CLI: 6.0.7
Node: 8.11.2
Angular: 6.0.3
答案 2 :(得分:2)
Angular 9提供了一个新选项,可以立即构建所有语言版本。它还通过将语言环境添加到已配置的baseHref中来为应用程序的每个版本设置基本HREF。
ng build --prod --localize
然后,您需要将所有内部版本复制到nginx服务目录
COPY /dist/my-app/ /usr/share/nginx/my-app/
并按照之前的答案配置nginx。
答案 3 :(得分:2)
这是我为多个项目解决的解决方案:
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;
}
}
}
然后我还有一个附加的index.html,里面有那个小脚本,它被复制到nginx根目录:
其他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>
此文件用于将用户路由到正确的语言子项目,具体取决于其浏览器语言,或者当用户更改应用程序中存储在本地存储中的语言时,该语言将具有较高的优先级。
还有我的angular.json
{
"$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"
}
最后是我的package.json中一些重要的npm脚本
"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"
答案 4 :(得分:0)
http://nginx.org/r/try_files的工作方式存在共同的误解。如果你仔细观察文档(从上面的链接),你会注意到虽然try_files
指令中的第一个和中间参数是“file”类型,但最后一个参数叫做“uri”;在您的情况下,一旦您修复了http://nginx.org/r/location以正确处理正则表达式(您的location
代码缺少~
修饰符),可能会导致无限循环,如您在你的意见。
请注意,一般情况下,建议不要在nginx中使用正则表达式,以便在可以避免使用正则表达式的简单情况下获得最佳性能,因此,我建议您有两个独立的位置,每个位置用于英语和法语。
location /fr/ {
try_files $uri /fr/index.html =410;
}
location /en/ {
try_files $uri /en/index.html =410;
}
请注意,上面的代码假定您从webapp前端本身执行正确的URL处理 - 如果在/en/
和/fr/
版本之间共享给定资源,则会直接通过/
基础,没有/en/
或/fr/
说明符。代码的=410
部分与=404
的行为类似,只是错误略有不同,以便更容易调试哪个指令导致错误。