Angular 路由和本地化
Angular routing, and localization
我有一个 angular 应用程序,我已经本地化了
多种语言(比如英语和法语)。
我想根据 url:
提供不同区域设置的应用程序版本
- 我的应用程序。io/en/account
- 我的应用程序。io/en/settings
- 我的应用程序。io/fr/account
- ...
我尝试按照 official documentation 中的步骤操作,但没有成功。
所以这是我的 nginx 配置:
http {
# Browser preferred language detection (does NOT require
# AcceptLanguageModule)
map $http_accept_language $accept_language {
~*^en en;
~*^fr fr;
}
server {
listen 4200;
server_name localhost;
root /usr/share/nginx/html;
# index index.html index.htm;
include /etc/nginx/mime.types;
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, POST, PATCH, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Origin, Accept";
add_header Access-Control-Expose-Headers "Content-Length,Content-Range";
# Fallback to default language if no preference defined by browser
if ($accept_language ~ "^$") {
set $accept_language "en";
}
# Redirect "/" to Angular application in the preferred language of the browser
rewrite ^/$ /$accept_language permanent;
# Everything under the Angular application is always redirected to Angular in the
# correct language
location ~ ^/(en|fr) {
try_files $uri $uri/ /index.html;
}
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
}
}
...这就是我配置路由器的方式:
const routes: Routes = [
{
path: "account",
component: AccountComponent,
},
{
path: "settings",
component: SettingsComponent,
},
{
path: "",
redirectTo: "account",
pathMatch: "full",
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
此时我可以访问https://myapp.io/en/。
我可以浏览该网站,并到达 https://myapp.io/en/account。
如果我此时按重新加载,我会收到此 nginx 错误:
有谁知道我做错了什么?或者任何人都可以指出一个开源工作示例或一个好的教程吗?
更新
我已经按照建议更改了 nginx 配置; this fixed the problem in the screenshot, but still a problem remains: when I try to load the host (https://myapp.io/) I get redirected to https://myapp.io:4200/en/
更新 2
修复了此块的更改:
location = / {
try_files $uri /$accept_language/index.html;
}
您没有正确遵循官方文档。至少你错过了最后一个 try_files
指令参数中的语言代码:
location ~ ^/(en|it)/ {
try_files $uri //index.html;
}
下一部分将是我对 Angular 官方文档中的 aforementioned nginx 示例的考虑,我发现它的质量很差(我的意思只是 nginx 示例,而不是整个文档)。
在最后一个 try_files
指令参数中使用 ?$args
后缀没有任何意义,如该示例中所建议的那样:
try_files $uri //index.html?$args;
angular 应用程序将继续查看浏览器地址栏中已经存在(或不存在)的查询参数,因此该后缀除了 nginx 在变量上花费的额外 CPU 周期之外什么都不做插值。当请求又应该用一些 PHP 脚本处理时,这个后缀是有意义的,但这里不是这种情况。
我看不出有任何合理的理由在这里使用正则表达式:
if ($accept_language ~ "^$") {
set $accept_language "it";
}
这里可以使用更高效的精确匹配:
if ($accept_language = "") {
set $accept_language "it";
}
如果您将 default
项添加到 map
块中,那么即使是那个也不是真正需要的:
map $http_accept_language $accept_language {
~*^en en;
~*^it it;
default it;
}
但是,如果不将支持的语言指定为第一个语言,它将无法工作。想象一个请求 header 比如 Accept-Language: de;q=0.9, en;q=0.8
。将为该用户选择默认的意大利语,即使在首选语言列表(而不是英语)中也没有指定。如果您想考虑用户在其浏览器设置中设置的语言优先级,对于两种支持的语言,您可以使用以下内容:
map $http_accept_language $accept_language {
~en.*it(*SKIP)(*F)|it it; # when "it" substring present and not preceded with "en"
~it.*en(*SKIP)(*F)|en en; # when "en" substring present and not preceded with "it"
default it;
}
(正则表达式 provided by Wiktor Stribiżew)
三种语言,考虑到优先顺序,将需要以下 map
块:
map $http_accept_language $accept_language {
~(?:en|fr).*it(*SKIP)(*F)|it it;
~(?:fr|it).*en(*SKIP)(*F)|en en;
~(?:en|it).*fr(*SKIP)(*F)|fr fr;
default it;
}
等等。
而不是
rewrite ^/$ /$accept_language permanent;
我宁愿使用(同样,为了效率)
location = / {
return 301 /$accept_language/;
}
此外,我认为 301 永久重定向代码根本不应该在这里使用。如果用户以任何方式更改他的语言首选项,他将不会根据他的新语言设置收到应用程序页面,因为此重定向已被他的浏览器缓存。我认为 302 临时重定向在这里更合适:
location = / {
return 302 /$accept_language/;
}
出于同样的效率原因,我还考虑将主要位置块一分为二(或更多,具体取决于支持的语言数量):
location /en/ {
try_files $uri /en/index.html;
}
location /it/ {
try_files $uri /it/index.html;
}
...
Nginx 不是那种 DRY 原则永远是好的东西。这种配置的另一个优点是能够将它与 alias
指令一起使用而无需其他解决方法(如果您尝试将 try_files $uri //index.html;
与 angular 应用程序目录一起使用alias
指令而不是 root
指令,您将面临 this nginx trac ticket 中描述的 well-known 副作用之一。
我有一个 angular 应用程序,我已经本地化了 多种语言(比如英语和法语)。
我想根据 url:
提供不同区域设置的应用程序版本- 我的应用程序。io/en/account
- 我的应用程序。io/en/settings
- 我的应用程序。io/fr/account
- ...
我尝试按照 official documentation 中的步骤操作,但没有成功。
所以这是我的 nginx 配置:
http {
# Browser preferred language detection (does NOT require
# AcceptLanguageModule)
map $http_accept_language $accept_language {
~*^en en;
~*^fr fr;
}
server {
listen 4200;
server_name localhost;
root /usr/share/nginx/html;
# index index.html index.htm;
include /etc/nginx/mime.types;
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, POST, PATCH, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Origin, Accept";
add_header Access-Control-Expose-Headers "Content-Length,Content-Range";
# Fallback to default language if no preference defined by browser
if ($accept_language ~ "^$") {
set $accept_language "en";
}
# Redirect "/" to Angular application in the preferred language of the browser
rewrite ^/$ /$accept_language permanent;
# Everything under the Angular application is always redirected to Angular in the
# correct language
location ~ ^/(en|fr) {
try_files $uri $uri/ /index.html;
}
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
}
}
...这就是我配置路由器的方式:
const routes: Routes = [
{
path: "account",
component: AccountComponent,
},
{
path: "settings",
component: SettingsComponent,
},
{
path: "",
redirectTo: "account",
pathMatch: "full",
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
此时我可以访问https://myapp.io/en/。 我可以浏览该网站,并到达 https://myapp.io/en/account。 如果我此时按重新加载,我会收到此 nginx 错误:
有谁知道我做错了什么?或者任何人都可以指出一个开源工作示例或一个好的教程吗?
更新
我已经按照建议更改了 nginx 配置
更新 2
修复了此块的更改:
location = / {
try_files $uri /$accept_language/index.html;
}
您没有正确遵循官方文档。至少你错过了最后一个 try_files
指令参数中的语言代码:
location ~ ^/(en|it)/ {
try_files $uri //index.html;
}
下一部分将是我对 Angular 官方文档中的 aforementioned nginx 示例的考虑,我发现它的质量很差(我的意思只是 nginx 示例,而不是整个文档)。
在最后一个 try_files
指令参数中使用 ?$args
后缀没有任何意义,如该示例中所建议的那样:
try_files $uri //index.html?$args;
angular 应用程序将继续查看浏览器地址栏中已经存在(或不存在)的查询参数,因此该后缀除了 nginx 在变量上花费的额外 CPU 周期之外什么都不做插值。当请求又应该用一些 PHP 脚本处理时,这个后缀是有意义的,但这里不是这种情况。
我看不出有任何合理的理由在这里使用正则表达式:
if ($accept_language ~ "^$") {
set $accept_language "it";
}
这里可以使用更高效的精确匹配:
if ($accept_language = "") {
set $accept_language "it";
}
如果您将 default
项添加到 map
块中,那么即使是那个也不是真正需要的:
map $http_accept_language $accept_language {
~*^en en;
~*^it it;
default it;
}
但是,如果不将支持的语言指定为第一个语言,它将无法工作。想象一个请求 header 比如 Accept-Language: de;q=0.9, en;q=0.8
。将为该用户选择默认的意大利语,即使在首选语言列表(而不是英语)中也没有指定。如果您想考虑用户在其浏览器设置中设置的语言优先级,对于两种支持的语言,您可以使用以下内容:
map $http_accept_language $accept_language {
~en.*it(*SKIP)(*F)|it it; # when "it" substring present and not preceded with "en"
~it.*en(*SKIP)(*F)|en en; # when "en" substring present and not preceded with "it"
default it;
}
(正则表达式 provided by Wiktor Stribiżew)
三种语言,考虑到优先顺序,将需要以下 map
块:
map $http_accept_language $accept_language {
~(?:en|fr).*it(*SKIP)(*F)|it it;
~(?:fr|it).*en(*SKIP)(*F)|en en;
~(?:en|it).*fr(*SKIP)(*F)|fr fr;
default it;
}
等等。
而不是
rewrite ^/$ /$accept_language permanent;
我宁愿使用(同样,为了效率)
location = / {
return 301 /$accept_language/;
}
此外,我认为 301 永久重定向代码根本不应该在这里使用。如果用户以任何方式更改他的语言首选项,他将不会根据他的新语言设置收到应用程序页面,因为此重定向已被他的浏览器缓存。我认为 302 临时重定向在这里更合适:
location = / {
return 302 /$accept_language/;
}
出于同样的效率原因,我还考虑将主要位置块一分为二(或更多,具体取决于支持的语言数量):
location /en/ {
try_files $uri /en/index.html;
}
location /it/ {
try_files $uri /it/index.html;
}
...
Nginx 不是那种 DRY 原则永远是好的东西。这种配置的另一个优点是能够将它与 alias
指令一起使用而无需其他解决方法(如果您尝试将 try_files $uri //index.html;
与 angular 应用程序目录一起使用alias
指令而不是 root
指令,您将面临 this nginx trac ticket 中描述的 well-known 副作用之一。