使用功能在Firebase上服务i18n角度应用程序 [英] Serve an i18n angular app on Firebase using functions

查看:62
本文介绍了使用功能在Firebase上服务i18n角度应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试正确处理对Firebase上托管的本地化角度应用程序的请求.目标是根据以下条件加载正确的应用程序包:1)名为locale的cookie. 2)accept-language标头. 3)当其他人不可用时,默认为en-us.

I'm trying to handle requests correctly to a localized angular app hosted on Firebase. The goal is to load the right app bundle depending on: 1) A cookie named locale. 2) The accept-language header. 3) Default to en-us when others are unavailable.

在使用i18n本地化角度应用程序时,angular-cli会为angular.json中存在的每种语言/地区生成捆绑包(您的应用程序的版本). Firebase将您的捆绑包作为静态内容提供,并且具有严格的安全度量,不允许遍历路径.

When localizing angular apps with i18n, angular-cli generates bundles (versions of your app) for each language/locale present in angular.json. Firebase serves your bundles as static content and it has serious security measurements disallowing path traversals.

我尝试在firebase.json中同时设置 rewrites redirects ,但是没有拦截locale cookie的功能.然后,我使用云函数读取Cookie,解析请求并使用expresscookie-parserexpress-locale返回正确的资源.

I tried both setting rewrites and redirects in firebase.json but there is no facilities to intercept the locale cookie. Then, I used cloud functions to read the cookie, parse requests and return the right resource using express, cookie-parser and express-locale.

1.仅使用托管

firebase.json

firebase.json

{
  "hosting": {
    "public": "dist",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "/en-us/**",
        "destination": "/en-us/index.html"
      },
      {
        "source": "/es-co/**",
        "destination": "/es-co/index.html"
      },
      {
        "source": "/zh-hans/**",
        "destination": "/zh-hans/index.html"
      },
      {
        "source": "**",
        "destination": "/en-us/index.html"
      }
    ]
  }
}

2.使用云功能并表达

firebase.json

firebase.json

{
  "functions": {
    "predeploy": [
      "npm --prefix \"$RESOURCE_DIR\" run lint"
    ]
  },
  "hosting": {
    "public": "dist",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "function": "multilang"
      }
    ]
  }
}

functions/index.js

functions/index.js

const express = require('express')
const expressLocale = require('express-locale')
const cookieParser = require('cookie-parser')
const functions = require('firebase-functions')
const path = require('path')
const fs = require('fs')
const _ = require('lodash')

const app = express()

const localeMap = {
  en: 'en_US',
  es: 'es_CO',
  zh: 'zh_TW',
  'en-US': 'en_US',
  'es-CO': 'es_CO',
  'zh-TW': 'zh_TW',
  'zh-Hans': 'zh_TW',
  en_US: 'en_US',
  es_CO: 'es_CO',
  zh_TW: 'zh_TW',
  zh_Hans: 'zh_TW'
}

const defaultLocale = 'en_US'

const baseMap = {
  en_US: 'en-us',
  es_CO: 'es-co',
  zh_TW: 'zh-hans'
}

app
  .use(cookieParser())
  .use(expressLocale({
    priority: ['cookie', 'accept-language', 'map', 'default'],
    default: defaultLocale,
    map: localeMap
  }))
  .use('**', (req, res, next) => {
    const locale = localeMap[req.locale.language]
    const sanitized = req.originalUrl.replace(/\.{2,}/g, '').replace(/\/{2,}/g, '/')
    const url = sanitized.split('/')
    const idx = url.length === 1 ? 0 : 1

    url[0] = _.values(baseMap).includes(url[idx]) ?
      url[idx] : (baseMap[locale] || baseMap[defaultLocale])

    if (url.length > 1 && url[0] === url[1]) {
      url.shift()
    }

    let file = path.resolve(`dist/${url.join('/')}`)

    if (!url[url.length - 1].match(/^[\w,\s-]+\.[a-z0-9]{2,4}$/) || !fs.existsSync(file)) {
      file = path.resolve(`dist/${url[0]}/index.html`)
    }

    res.sendFile(file)
  })

exports.multilang = functions.https.onRequest(app)

最后的设置非常适合index.html或其他任何请求文件的本地Firebase仿真解析路径(例如,对于渐进式应用程序的支持,请说ngsw-worker.js).

The last settings work well for a local Firebase emulation resolving path to index.html or any other requested file (say it ngsw-worker.js for progressive apps support).

部署到Firebase时,即使将路径更改为:

When deploying to Firebase it fails resolving and finding the hosted files (e.g. index.html), even if you change path to:

  • /${url[0]}/index.html
  • ../${url[0]}/index.html
  • /public/${url[0]}/index.html
  • /${url[0]}/index.html
  • ../${url[0]}/index.html
  • /public/${url[0]}/index.html

或将req.sendFile重写为req.redirect.

感谢您的帮助.

推荐答案

对于遇到相同问题的任何人,这是一个使用firebase函数的幼稚解决方案:

For anyone facing the same problem, here is a naive solution using firebase functions:

const express = require('express')
const cookieParser = require('cookie-parser')
const expressLocale = require('express-locale')
const functions = require('firebase-functions')

const localeMap = {
  en: 'en_US',
  es: 'es_CO',
  zh: 'zh_CN',
  en_US: 'en_US',
  es_CO: 'es_CO',
  zh_Hans: 'zh_CN'
}

const prefixMap = {
  en_US: 'en-us',
  es_CO: 'es-co',
  zh_CN: 'zh-hans',
}

const defaultLocale = 'en_US'
const app = express()

app
  .use(cookieParser())
  .use(expressLocale({
    priority: ['cookie', 'accept-language', 'map', 'default'],
    default: defaultLocale,
    map: localeMap
  }))
  .use('*', (req, res, next) => {
    const locale = localeMap[req.locale.language]
    const prefix = prefixMap[locale] || prefixMap[defaultLocale]
    res.redirect(`/${prefix}${req.originalUrl}`)
  })

exports.multilang = functions.https.onRequest(app)

firebase.json

{
  "functions": {
    "predeploy": [
      "npm --prefix \"$RESOURCE_DIR\" run lint"
    ]
  },
  "hosting": {
    "public": "dist/your-app-name",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "/en-us/**",
        "destination": "/en-us/index.html"
      },
      {
        "source": "/es-co/**",
        "destination": "/es-co/index.html"
      },
      {
        "source": "/zh-hans/**",
        "destination": "/zh-hans/index.html"
      },
      {
        "source": "**",
        "function": "multilang"
      }
    ]
  }
}

这篇关于使用功能在Firebase上服务i18n角度应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆