如何部署到托管多语言(i18n)的 Angular Universal 应用程序的 Firebase?

问题描述 投票:0回答:1

希望那里有一些了不起的灵魂可以帮助我。

我有一个简单的 ANGULAR 16 应用程序,可以为搜索引擎生成 SEO 友好的页面,并且我的应用程序是用英语编写的并翻译为 es。

当我运行

build:ssr
时,会在“dist/appName/browser”文件夹内创建 2 个文件夹(en、es)。 这些文件夹在本地完美运行,当我查看每个页面的源代码时,我可以看到呈现给 SEO 的页面。

当我将这 2 个文件夹部署到 Firebase 托管时,该应用程序运行完美,但源代码似乎没有通用、没有 SEO 友好的页面。我究竟做错了什么? 我阅读了那里的每一个教程,但没有任何更新足够。

我遵循官方Firebase文档(https://firebase.google.com/docs/hosting/frameworks/angular),其中没有提到使用函数,并启用了webframeworks

firebase experiments:enable webframeworks
,运行
firebase init hosting
,回答了问题如下并得到最终错误:

cd projects/appName firebase init hosting
Detected an existing Angular codebase in the current directory, should we use this? No
Do you want to use a web framework? (experimental) Yes
What folder would you like to use for your web application's root directory? dist/appName/browser
Could not determine the web framework in use.
Please choose the framework: Angular
Error: This command is not available when running the Angular CLI inside a workspace.

任何帮助将不胜感激,我是初学者,所以请善意:) 以下是我在该项目中拥有的更重要的文件:

角度.JSON

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "appName": {
      "projectType": "application",
      "schematics": {
        "@schematics/angular:component": { "style": "scss" }
      },
      "i18n": {
        "sourceLocale": {
            "baseHref": "/",
            "code": "en"
        },
        "locales": {
          "es": {
            "translation": "src/i18n/messages.es.xlf",
            "baseHref": "es/"
          }
        }
      },
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "localize": true,
            "outputPath": "dist/appName/browser",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": [ "zone.js" ],
            "tsConfig": "tsconfig.app.json",
            "inlineStyleLanguage": "scss",
            "assets": [
                "src/assets"
            ],
            "styles": [
              "src/assets/scss/styles.scss"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "optimization": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "10mb",
                  "maximumError": "10mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "6kb",
                  "maximumError": "10kb"
                }
              ],
              "outputHashing": "all"
            },
            "development": {
              "buildOptimizer": false,
              "optimization": false,
              "vendorChunk": true,
              "extractLicenses": false,
              "sourceMap": true,
              "namedChunks": true,
              "localize": ["en"]
            },
            "es": { "localize": ["es"], "baseHref": "/es/" }
          },
          "defaultConfiguration": "production"
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "configurations": {
            "production": {
              "browserTarget": "appName:build:production"
            },
            "development": {
              "browserTarget": "appName:build:development"
            },
            "es": { "browserTarget": "appName:build:es" }
          },
          "defaultConfiguration": "development"
        },
        "extract-i18n": {
          "builder": "ng-extract-i18n-merge:ng-extract-i18n-merge",
          "options": {
            "browserTarget": "appName:build",
            "format": "xlf",
            "outputPath": "src/i18n",
            "targetFiles": [
                "messages.es.xlf"
            ]
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "polyfills": [
              "zone.js",
              "zone.js/testing"
            ],
            "tsConfig": "tsconfig.spec.json",
            "inlineStyleLanguage": "scss",
            "assets": [
              "src/assets"
            ],
            "styles": [
               "src/assets/scss/styles.scss"
            ],
            "scripts": []
          }
        },
        "server": {
          "builder": "@angular-devkit/build-angular:server",
          "options": {
            "outputPath": "dist/appName/server",
            "main": "server.ts",
            "tsConfig": "tsconfig.server.json",
            "inlineStyleLanguage": "scss"
          },
          "configurations": {
            "production": {
              "outputHashing": "media"
            },
            "development": {
              "optimization": false,
              "sourceMap": true,
              "extractLicenses": false,
              "vendorChunk": true,
              "buildOptimizer": false
            }
          },
          "defaultConfiguration": "production"
        },
        "serve-ssr": {
          "builder": "@nguniversal/builders:ssr-dev-server",
          "configurations": {
            "development": {
              "browserTarget": "appName:build:development",
              "serverTarget": "appName:server:development"
            },
            "production": {
              "browserTarget": "appName:build:production",
              "serverTarget": "appName:server:production"
            }
          },
          "defaultConfiguration": "development"
        },
        "prerender": {
          "builder": "@nguniversal/builders:prerender",
          "options": {
            "routes": [ "/" ]
          },
          "configurations": {
            "production": {
              "browserTarget": "appName:build:production",
              "serverTarget": "appName:server:production"
            },
            "development": {
              "browserTarget": "appName:build:development",
              "serverTarget": "appName:server:development"
            }
          },
          "defaultConfiguration": "production"
        },
        "lint": {
          "builder": "@angular-eslint/builder:lint",
          "options": {
            "lintFilePatterns": [
              "src/**/*.ts",
              "src/**/*.html"
            ]
          }
        }
      }
    }
  },
  "cli": {
    "schematicCollections": [ "@angular-eslint/schematics" ]
  }
}

服务器.JS

import 'zone.js/node';

import { APP_BASE_HREF } from '@angular/common';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { existsSync } from 'fs';
import { join } from 'path';

import { AppServerModule } from './src/main.server';
import { LOCALE_ID } from '@angular/core';

// The Express app is exported so that it can be used by serverless Functions.
export function app(lang:string): express.Express { 
  const server = express();
  const distFolder = join(process.cwd(), `dist/appName/browser/${lang}`);
  const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';

  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)
  server.engine('html', ngExpressEngine({
    bootstrap: AppServerModule,
    extraProviders: [{ provide: LOCALE_ID, useValue: lang}] 
  } as any));

  server.set('view engine', 'html');
  server.set('views', distFolder);

  // Example Express Rest API endpoints
  // server.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get('*.*', express.static(distFolder, {
    maxAge: '1y'
  }));

  // All regular routes use the Universal engine
  server.get('*', (req, res) => {
    res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
  });

  return server;
}

function run(): void {
  const port = process.env['PORT'] || 4000;

  // Start up the Node server
  const server = express(); 
  const appEs = app('es'); 
  const appEn = app('en');

  server.use('/es', appEs); 
  server.use('/en', appEn); 
  server.use('', appEn); 
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  run();
}

export * from './src/main.server';

FIREBASE.JSON

{
  "hosting": {
    "target": "site", 
    "public": "dist/appName/browser",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "/es/**",
        "destination": "/es/index.html"
      },
      {
        "source": "**",
        "destination": "en/index.html"
      }
    ],
    "i18n": {
      "root": "/"
    }
  }
}

包.JSON

{
  "name": "appName",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve --open",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test",
    "dev:ssr": "ng run appName:serve-ssr",
    "serve:ssr": "node dist/appName/server/main.js",
    "build:ssr": "ng build && ng run appName:server",
    "prerender": "ng run appName:prerender",
    "build:universal": "npm run build:ssr && npm run serve:ssr",
    "lint": "ng lint",
    "start-es": "ng serve --configuration=es"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^16.2.9",
    "@angular/cdk": "^16.2.8",
    "@angular/common": "^16.2.9",
    "@angular/compiler": "^16.2.9",
    "@angular/core": "^16.2.9",
    "@angular/forms": "^16.2.9",
    "@angular/material": "^16.2.8",
    "@angular/platform-browser": "^16.2.9",
    "@angular/platform-browser-dynamic": "^16.2.9",
    "@angular/platform-server": "^16.2.9",
    "@angular/router": "^16.2.9",
    "@nguniversal/express-engine": "^16.2.0",
    "express": "^4.15.2",
    "rxjs": "~7.8.1",
    "tslib": "^2.3.0",
    "zone.js": "~0.13.3"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^16.2.6",
    "@angular-eslint/builder": "16.2.0",
    "@angular-eslint/eslint-plugin": "16.2.0",
    "@angular-eslint/eslint-plugin-template": "16.2.0",
    "@angular-eslint/schematics": "16.2.0",
    "@angular-eslint/template-parser": "16.2.0",
    "@angular/cli": "~16.2.6",
    "@angular/compiler-cli": "^16.2.9",
    "@angular/localize": "^16.2.9",
    "@nguniversal/builders": "^16.2.0",
    "@types/express": "^4.17.0",
    "@types/jasmine": "~5.1.0",
    "@types/node": "^20.8.4",
    "@typescript-eslint/eslint-plugin": "^6.7.5",
    "@typescript-eslint/parser": "^6.7.5",
    "eslint": "^8.39.0",
    "jasmine-core": "~5.1.1",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.2.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.1.0",
    "ng-extract-i18n-merge": "^2.7.1",
    "typescript": "~5.1.6"
  }
}
localization internationalization firebase-hosting angular-universal angular16
1个回答
0
投票

好吧,Angular 17 解决了所有问题!一切都完美运行并部署到 Firebase 托管,无需使用生成 SRR 文件的函数。谢谢你,角!

© www.soinside.com 2019 - 2024. All rights reserved.