angularfirebaseinternationalizationangularfire2firebase-hosting

I18n Get correct language code in baseHref with firebase hosting and Angular


In my angular project I have set up i18n for "sv"(swedish) and "en".

What I want is to when accessing mypage.com, "/sv" or "/en" to be added to baseHref so that correct index.html is loaded from firebase hosting based of "accept-language" in other words users for sweden will get "mypage.com/sv" and everybody else mypage.com/en would be the one to load.

I made some digging and it seems that this is nothing that comes automaticly with firebase hositng and some logic has to be added eaither in angular or firebase functions.

Adding logic in angular failed totaly(keep in mind that I want it to happen automaticly and not something that user picks in a dropdown)

...and logic for firebase functions although it's working on the simplest level it messing up with my routing (adding alot of "/sv" to path when it should not + it feels like a fix). bellow code for this fix:

exports.redirectBasedOnLanguage = functions.https.onRequest((req:any, res:any) => {
  // Default to English
  let language = 'en';

  // Check for the Accept-Language header and prefer Swedish if present
  const acceptLanguage = req.headers['accept-language'];
  if (acceptLanguage && acceptLanguage.startsWith('sv')) {
    language = 'sv';
  }

  // Construct the redirect URL based on the detected language
  // Note: You might want to adjust the path construction based on your URL structure
  const targetUrl = `/${language}${req.path}`;

  // Perform the redirection
  res.redirect(302, targetUrl);
});

my firebase.json: for above solution:

"rewrites": [
  {
    "source": "**",
    "function": "redirectBasedOnLanguage"
  }
];

and for set up without any firebaseFunctions fix (when following docs):

"rewrites": [
  {
    "source": "/sv{,/**}",
    "destination": "/sv/index.html"
  },
  {
    "source": "/sv-SE{,/**}",
    "destination": "/sv/index.html"
  },
  {
    "source": "**",
    "destination": "/en/index.html"
  }
]

What is the correct way to handle?


Solution

  • To solve the problem that you are having, you need to implement on the Angular side and on the serving side.

    So, on the Angular side, you need to build the application using a base href, which can be done in multiple ways, but the one that suits your problem the best, is using the CLI build options.

    // English version
    > ng build --base-href /en/
    
    // Swedish version
    > ng build --base-href /sv/
    

    You should specify the appropriate base href for each build. Since you are using multiple builds for i18n, this static approach works perfectly for your use case.

    Note: A word of caution here, the base href needs to end with a /, otherwise the browser will ignore the last segment of the supplied URL.

    On the Firebase side (never used Firebase hosting, so adjust accordingly), you need to perform a rewrite (routing), which can be achieved using the Firebase cloud functions, but Firebase already offers you a built-in way to serve different i18n builds as mentioned on a different answer. On the documentation, they specify that they use the Accept-Language header to detect the language, which is exactly what you want.

    In your firebase.json file, add the following configuration:

    "i18n": {
        "root": "/localized-files"  // directory that contains your "i18n content"
    }
    

    Create a localized-files directory in the path specified in the firebase.json file, then create 1 directory for each language that you want to serve (en_ALL and sv_ALL) and inside, add your different builds.

    public/
        // Serve this default when the request language is neither `en` nor `sv`.
        index.html  // Your app default main entry point.
        404.html    // Your app custom 404 page.
    
        localized-files/
            en_ALL/
                index.html
                404.html
            sv_ALL/
                index.html
                404.html
    

    Remarks:

    1. The ALL is a firebase keyword that indicates, check the docs for more information.
    2. The name is case-sensitive.

    Ref: https://firebase.google.com/docs/hosting/i18n-rewrites#set-up-i18n-rewrites


    Side note on why your function was misbehaving. You are always prepending the language code, even when it is already in the request URL. You have 2 possible solutions:

    1. Just call that function when no language code is on the URL.
    2. Update your function to only prepend the language code if it is not in the URL.

    Solution 1 seems more appropriate, so you could just update your rewrite configuration to:

    "rewrites": [
      {
        "source": "/sv{,/**}",
        "destination": "/sv/index.html"
      },
      {
        "source": "/sv-SE{,/**}",
        "destination": "/sv/index.html"
      },
      {
        "source": "**",
        "function": "redirectBasedOnLanguage"
      }
    ]