如何在角度中使用服务器端渲染(通用)中的xi18n(AOT)

时间:2018-06-11 17:05:46

标签: angular internationalization angular-universal

我希望提供多语言角度应用程序,同时使用服务器端呈现来实现SEO和性能。 为此,我目前有以下情况:

package.json脚本:

"build:ssr": "npm run build:client-and-server-bundles && npm run webpack:server",
"serve:ssr": "node dist/server",
"build:client-and-server-bundles": "npm run build-i18n && ng run my-web:server",
"webpack:server": "webpack --config webpack.server.config.js --progress --colors",
"build-i18n:de": "LOCALE=de && npm run build-i18n:locale",
"build-i18n:en": "ng build --output-path=dist/browser/en --aot --prod --base-href /en/ --i18n-locale=en",
"build-i18n": "npm run build-i18n:en && npm run build-i18n:de"

server.ts:

// Locale to get best language match
import { Locales } from 'locale';

app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
}));

app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));

const supportedLanguages = ['de', 'en'];

for (const language of supportedLanguages) {
  // Server languages static files from /browser
  app.get('/' + language + '/*.*', express.static(join(DIST_FOLDER, language, 'browser'))); // todo

  // All languaged regular routes use the Universal engine
  app.get('/' + language + '/*', (req, res) => {
    console.log(language);
    res.render(language + '/index', {req});
  });
}

我的问题是我只使用正确的base-href得到了正确的index.html,但没有得到.xlf文件中正确的翻译文本(只有组件HTML中的文本)。我在dist / server中只有一个main.js。 使用xi18n和AOT,我得到dist / browser / de和dist / browser / en。 我找不到任何教程或示例能够将xi18n和服务器端渲染角度结合起来。

我的 angular.json 有以下架构师条目:

"server": {
          "builder": "@angular-devkit/build-angular:server",
          "options": {
            "outputPath": "dist/server",
            "main": "src/main.server.ts",
            "tsConfig": "src/tsconfig.server.json"
          }
        }

如果有人可以帮我解决这个问题,那将会很棒。

我的角度版本是:6.0.3

提前谢谢。

拉​​马

1 个答案:

答案 0 :(得分:1)

这是我为朝鲜语版本做的事情:

package.json

"build:client-and-server-bundles-ko": "ng build --prod --base-href=/ko --deploy-url=/ko/ --output-path=dist/ko/browser --i18n-file=src/locale/messages.ko.xlf --i18n-locale=ko --i18n-format=xlf  && ng run rendercore-www:server --outputPath=dist/ko/server",
"build:client-and-server-bundles-staging-ko": "ng build --prod --base-href=/ko --deploy-url=/ko/ --output-path=dist/ko/browser --i18n-file=src/locale/messages.ko.xlf --i18n-locale=ko --i18n-format=xlf  --configuration=staging && ng run rendercore-www:server:staging --outputPath=dist/ko/server",
"build:client-and-server-bundles-prod-ko": "ng build --prod --base-href=/ko --deploy-url=/ko/ --output-path=dist/ko/browser --i18n-file=src/locale/messages.ko.xlf --i18n-locale=ko --i18n-format=xlf  --configuration=production && ng run rendercore-www:server:production --outputPath=dist/ko/server",
"build:ssr-ko": "npm run build:client-and-server-bundles-ko && npm run webpack:server-ko",
"build:ssr-staging-ko": "npm run build:client-and-server-bundles-staging-ko  && npm run webpack:server-ko",
"build:ssr-prod-ko": "npm run build:client-and-server-bundles-prod-ko  && npm run webpack:server-ko",
"webpack:server-ko": "webpack --config webpack.server.config.js --env.lang=ko --progress --colors",

server-ko.ts

// These are important and needed before anything else
import "zone.js/dist/zone-node";
import "reflect-metadata";
import { enableProdMode } from "@angular/core";
import * as express from "express";
import { join } from "path";

// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express server
const app = express();

const PORT = process.env.PORT || 4000;
// const DIST_FOLDER = join(process.cwd(), 'dist');
const DIST_FOLDER = join(process.cwd(), "");  //here..i changed

// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require("./dist/ko/server/main");//here..i changed add ko

// Express Engine
import { ngExpressEngine } from "@nguniversal/express-engine";
// Import module map for lazy loading
import { provideModuleMap } from "@nguniversal/module-map-ngfactory-loader";

app.engine(
    "html",
    ngExpressEngine({
        bootstrap: AppServerModuleNgFactory,
        providers: [provideModuleMap(LAZY_MODULE_MAP)]
    })
);

//here..i changed
app.use(function(req, res, next) { 
    if (req.url.slice(0, 4) === "/ko/") {
      req.url = req.url.slice(3);
    }
    next();
});

app.set("view engine", "html");
app.set("views", join(DIST_FOLDER, "browser"));

// Server static files from /browser
app.get("*.*", express.static(join(DIST_FOLDER, "browser")));

// All regular routes use the Universal engine
app.get("*", (req, res) => {
    console.log(`GET: ${req.originalUrl}`);
    res.render("index", {
      req: req,
      res: res
    });
});

// Start up the Node server
app.listen(PORT, () => {
    console.log(`Node server listening on http://localhost:${PORT}`);
});

webpack.server.config.js

const path = require("path");
const webpack = require("webpack");

module.exports = env => {
    //console.log(env);
    let langWithDash = env == undefined ? "" : "-" + env.lang;
    let langWithSlash = env == undefined ? "" : "/" + env.lang;
    return {
      entry: { server: "./server" + langWithDash + ".ts" },
      resolve: { extensions: [".js", ".ts"] },
      target: "node",
      mode: "none",
      // this makes sure we include node_modules and other 3rd party libraries
      externals: [/node_modules/],
      output: {
        path: path.join(__dirname, "dist" + langWithSlash),
        filename: "[name].js"
      },
      module: {
        rules: [{ test: /\.ts$/, loader: "ts-loader" }]
      },
      plugins: [
        // Temporary Fix for issue: https://github.com/angular/angular/issues/11580
        // for 'WARNING Critical dependency: the request of a dependency is an expression'
        new webpack.ContextReplacementPlugin(
          /(.+)?angular(\\|\/)core(.+)?/,
          path.join(__dirname, "src"), // location of your src
          {} // a map of your routes
        ),
        new webpack.ContextReplacementPlugin(/(.+)?express(\\|\/)(.+)?/, path.join(__dirname, "src"), {})
      ]
    };
};

使用npm run build:ssr-ko

然后它将在dist文件夹下创建一个子文件夹,如下所示:

/dist/ko/xxxxx

您必须从Web服务器重写URL。我使用IIS,所以这是一个IIS示例:

<rule name="Lang-EN" stopProcessing="true">
    <match url="^en[\/].*?" />
    <action type="Rewrite" url="dist/en/server.js" />
</rule>

<rule name="L51ang-KO" stopProcessing="true">
    <match url="^ko[\/].*" />
    <action type="Rewrite" url="dist/ko/server.js" />
</rule>

另外,由于当前角度有错误,因此/assets URL也将导致错误。

即使您使用base-hrefdeploy-url,某些HTML仍具有/assets路径。

所以我试试这个:

<rule name="Asset Redirect" stopProcessing="true">
    <match url="^assets\/.*" />
    <action type="Rewrite" url="dist/en/server.js" appendQueryString="true" />
</rule>

另一个提示,如果客户端访问网站的/,请重定向到其浏览器语言设置:

<rule name="Redirect To KO"  stopProcessing="true">
    <match url="^$" />
        <conditions>
          <add input="{HTTP_ACCEPT_LANGUAGE}" pattern="^ko" />
        </conditions>
    <action type="Redirect" url="{R:0}/ko/home" appendQueryString="true" />
</rule>

然后,如果您想添加其他语言,请再次执行此步骤。

我的构建脚本如下:

cd www
rmdir /S /Q dist
call npm i 
call npm run build:ssr-staging-en || exit /b -1
call npm run build:ssr-staging-ko || exit /b -1
call npm run build:ssr-staging-ja || exit /b -1

build en ko ja,它将创建以下内容:

/dist/ko/server.js
/dist/en/server.js
/dist/ja/server.js

只需将其上传到Web服务器,然后正确重写设置即可。

希望您能理解。谢谢。