Angular & Firebase Hosting: Font Loading Issue in Firefox for Missing i18n "en" Locale

Discussão em 'Angular' iniciado por Alex, Dezembro 19, 2024.

  1. Alex

    Alex Guest

    I use Angular in combination with Firebase Hosting to host my website, which supports multiple i18n locales. Everything works fine in modern web browsers except for Firefox, where I encounter a strange issue: the browser fails to load a font.

    The following error appears in the Firefox console:

    downloadable font: rejected by sanitizer (font-family: "Roboto" style:normal weight:400 stretch:100 src index:0) source: http://localhost:5000/assets/fonts/roboto_regular.woff2

    Observations


    1. This issue only occurs if the locale en does not exist. When I also generate a locale for en, everything works fine in Firefox as well. It also doesn't matter if I have just one locale or several, the only important thing is that the en locale exists.


    2. Without the en locale, when I try to directly access the font's path, Firefox redirects me back to the default path (in my case, /de/). However, when the en locale is added, Firefox downloads the font directly without any issues. Note that in both cases, the path to the font didn't contain a locale-id.
    Possible Causes

    • The issue seems to be related to how Firebase Hosting or Angular handles locale-specific routes and serves assets.
    • Firefox might reject the font because it is being redirected or served incorrectly when the "en" locale is missing.


    As a temporary solution, i'm also hosting the en route, even though it is not being used.


    Below are additional information that may be relevant.

    Angular version is 19.0.5


    firebase.json

    {
    "hosting": {
    "target": "app-name",
    "public": "dist/app-name/browser",
    "ignore": [
    "firebase.json",
    "**/.*",
    "**/node_modules/**"
    ],
    "headers": [
    {
    "source": "*.[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].+(css|js)",
    "headers": [
    {
    "key": "Cache-Control",
    "value": "public,max-age=31536000,immutable"
    }
    ]
    },
    {
    "source": "/@(ngsw-worker.js|ngsw.json)",
    "headers": [
    {
    "key": "Cache-Control",
    "value": "no-cache"
    }
    ]
    }
    ],
    "rewrites": [
    {
    "source": "/de/**",
    "destination": "/de/index.html"
    },
    {
    "source": "**",
    "destination": "/de/index.html"
    }
    ],
    "i18n": {
    "root": "/"
    }
    }
    }


    angular.json

    {
    "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
    "version": 1,
    "newProjectRoot": "projects",
    "projects": {
    "app-name": {
    "projectType": "application",
    "schematics": {
    "@schematics/angular:component": {
    "style": "scss"
    }
    },
    "root": "",
    "sourceRoot": "src",
    "prefix": "app",
    "architect": {
    "build": {
    "builder": "@angular-devkit/build-angular:application",
    "options": {
    "outputPath": {
    "base": "dist/app-name"
    },
    "index": "src/index.html",
    "polyfills": [
    "src/polyfills.ts"
    ],
    "tsConfig": "tsconfig.app.json",
    "inlineStyleLanguage": "scss",
    "assets": [
    "src/assets"
    ],
    "styles": [
    "src/assets/style/global-styles.scss"
    ],
    "scripts": [],
    "ssr": false,
    "localize": true,
    "serviceWorker": "ngsw-config.json",
    "browser": "src/main.ts",
    "stylePreprocessorOptions": {
    "includePaths": [
    "."
    ]
    }
    },
    "configurations": {
    "production": {
    "budgets": [
    {
    "type": "initial",
    "maximumWarning": "500kb",
    "maximumError": "5mb"
    },
    {
    "type": "anyComponentStyle",
    "maximumWarning": "2kb",
    "maximumError": "10kb"
    }
    ],
    "fileReplacements": [
    {
    "replace": "src/environments/environment.ts",
    "with": "src/environments/environment.prod.ts"
    }
    ],
    "outputHashing": "all",
    "serviceWorker": "ngsw-config.json"
    },
    "development": {
    "optimization": false,
    "extractLicenses": false,
    "sourceMap": true,
    "namedChunks": true,
    "localize": [
    "de"
    ],
    "baseHref": "/"
    },
    "de": {
    "localize": [
    "de"
    ],
    "baseHref": "/de/"
    }
    },
    "defaultConfiguration": "production"
    },
    "serve": {
    "builder": "@angular-devkit/build-angular:dev-server",
    "configurations": {
    "production": {
    "buildTarget": "app-name:build:production"
    },
    "development": {
    "buildTarget": "app-name:build:development"
    },
    "de": {
    "buildTarget": "app-name:build:de"
    }
    },
    "defaultConfiguration": "development"
    },
    "extract-i18n": {
    "builder": "@angular-devkit/build-angular:extract-i18n",
    "options": {
    "buildTarget": "app-name:build"
    }
    },
    "test": {
    "builder": "@angular-devkit/build-angular:karma",
    "options": {
    "main": "src/test.ts",
    "polyfills": "src/polyfills.ts",
    "tsConfig": "tsconfig.spec.json",
    "karmaConfig": "karma.conf.js",
    "inlineStyleLanguage": "scss",
    "assets": [
    "src/assets"
    ],
    "styles": [
    "src/assets/style/global-styles.scss"
    ],
    "scripts": []
    }
    }
    },
    "i18n": {
    "sourceLocale": "de"
    }
    }
    }
    }


    font in scss

    @font-face {
    font-family: "Roboto";
    font-style: normal;
    font-weight: 300;
    font-display: swap;
    src:
    url("/assets/fonts/roboto_light.woff2") format("woff2"),
    url("/assets/fonts/roboto_light.woff") format("woff");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC,
    U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
    }

    Continue reading...

Compartilhe esta Página