Angular 路由和本地化

标签 angular nginx angular-routing

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

我想根据网址提供不同区域设置版本的应用程序:

  • myapp.io/en/account
  • myapp.io/en/settings
  • myapp.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 错误:

Screenshot error

有人知道我做错了什么吗?或者任何人都可以指出一个开源工作示例或一个好的教程吗?

更新

我已经按照建议更改了 nginx 配置 here ;这解决了屏幕截图中的问题,但仍然存在问题:当我尝试加载主机( https://myapp.io/ )时,我被重定向到 https://myapp.io:4200/en/

更新2

修复了更改此 block 的问题:

location = / {
  try_files $uri /$accept_language/index.html;
}

最佳答案

您没有正确遵循官方文档。至少您错过了最后一个 try_files 指令参数中的语言代码:

location ~ ^/(en|it)/ {
    try_files $uri /$1/index.html;
}

下一部分将是我对 aforementioned 的思考来自 Angular 官方文档的 nginx 示例,我发现它的质量非常差(我的意思只是 nginx 示例,而不是整个文档)。

按照该示例中的建议,在最后一个 try_files 指令参数中使用 ?$args 后缀没有任何意义:

try_files $uri /$1/index.html?$args;

Angular 应用程序将继续看到浏览器地址栏中已经存在(或不存在)的查询参数,因此后缀除了 nginx 在变量插值上花费额外的 CPU 周期外什么也不做。当请求反过来用一些 PHP 脚本处理时,该后缀有意义,但这里的情况并非如此。

我在这里没有看到任何使用正则表达式的合理理由:

if ($accept_language ~ "^$") {
    set $accept_language "it";
}

此处可以使用性能更高的精确匹配:

if ($accept_language = "") {
    set $accept_language "it";
}

如果您将 default 项目添加到您的 map block 中,那么即使这个也不是真正需要的:

map $http_accept_language $accept_language {
    ~*^en en;
    ~*^it it;
    default it;
}

但是,如果未将受支持的语言指定为第一个语言,则它将无法工作。想象一下像 Accept-Language: de;q=0.9, en;q=0.8 这样的请求 header 。即使未在首选语言列表中指定(而不是英语),也会为此用户选择默认的意大利语。如果您想考虑用户在浏览器设置中设置的语言优先级,对于两种支持的语言,您可以使用以下内容:

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

(正则表达式 providedWiktor Stribiżew )

考虑到优先顺序,三种语言将需要以下 map block :

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

出于同样的效率原因,我还考虑将主位置 block 分成两个(或更多,取决于支持的语言数量):

location /en/ {
    try_files $uri /en/index.html;
}
location /it/ {
    try_files $uri /it/index.html;
}
...

Nginx 并不是那种DRY 原则永远都是好的东西。这种配置的另一个优点是能够将其与 alias 指令一起使用,而无需其他解决方法(如果您尝试使用 try_files $uri/$1/index.html; 使用 alias 指令而不是 root 指令定义 Angular 应用程序目录,您将面临 this nginx trac 中描述的众所周知的副作用之一票)。

关于 Angular 路由和本地化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72352536/

相关文章:

php - 如何提高 MYSQL 在网络上的性能

angular - 如何在 Angular2 中追加/更新查询参数

Angular 6 : Adding @Input to component doesn't work

Angular 4 : Routing rule with an undefined number of parameters

nginx - Safari为什么显示奇怪的符号而不是HTML内容?

redirect - nginx.conf 重定向多个条件

javascript - Angular 2 Child Routing (v3) 'Cannot read property ' 注释' of undefined'

Angular 单元测试 - 在 HttpInterceptor 中测试 retryWhen

Angular:如何在RouteGuard中相对于目标路由调用router.navigate()?

javascript - 路由在 Angular js 中无法正常工作