Angular 路由和本地化

Angular routing, and localization

我有一个 angular 应用程序,我已经本地化了 多种语言(比如英语和法语)。

我想根据 url:

提供不同区域设置的应用程序版本

我尝试按照 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 副作用之一。