firebase/firebaseui-web

[Feature Request] Provide localized builds in the NPM distribution

Opened this issue ยท 12 comments

There is currently no easy way to add a couple of CDNs for several languages of the UI, then load the correct UI based on a parameter.

The page is in a certain language, how to make sure the correct UI gets used even after the user chooses a different language?

My suggestion is to make it so, the UI with the language can be chosen like so after adding those languages as CDN:

var uiJA = new firebaseui.ja.auth.AuthUI(firebase.auth());
var uiEN = new firebaseui.en.auth.AuthUI(firebase.auth());

or even firebaseui_ja or something.

Now, no matter how many different CDNs with languages, they all point to the same variable: firebaseui so it's difficult to change that after the user switches languages. (especially talking about SPA's with vueJS)

Each internationalized version is a separate build. Including all the versions in one file is not feasible.
Typically if a user switches languages, you would redirect to another page (eg. https://www.example.com/auth?lang=ja") and load the UI version with the selected language.

@bojeil-google
In the case of progressive web apps and single page applications we use just one HTML file. If a language is not selected by the user parts of the data is being held back by webpack.

In this case I'm confused how I can load several languages for the FirebaseUI authentication.

Technically, you could still do this with one HTML file, but you would need to reload the page. I haven't tested dynamically loading the localized binaries and overwriting the previous one. That could be an option. I will need to test that.

Including all localized versions in one file is not feasible.

By the way we provide the ability to build the localized binary with npm. You could use that to build your own multi-language supporting version. Personally I don't recommend that approach and I prefer either reloading the page or dynamically loading the localized binary.

Dear @bojeil-google
I tried to build it from the dev version following these steps:

  1. git clone https://github.com/firebase/firebaseui-web.git
  2. cd firebaseui-web
  3. npm install
  4. npm run build

However, after building it, adding it as a static asset (making webpack copying the file as is), and also disabling babel on the file, I still get an error like this:

2017-11-16 19 30 35

I'm having trouble finding the bug. : S
Any ideas for me?

I haven't tested dynamically loading the localized binaries and overwriting the previous one. That could be an option. I will need to test that.

That would be great!! I think a lot of people writing SPA's or PWA's in multiple languages with one HTML file hosted on Firebase will face similar problems. : )

BTW, this is how I tried to add two languages through Firebaseui files I had built myself:

const firebaseuiEN = require('../statics/firebaseui__en.js')
const firebaseuiJA = require('../statics/firebaseui__ja.js')
const firebase = window.firebase
export default {
  data () {
    return {}
  },
  mounted () {
    // FirebaseUI config.
    let uiConfig = {
      signInSuccessUrl: '<url-to-redirect-to-on-success>',
      signInOptions: [
        firebase.auth.EmailAuthProvider.PROVIDER_ID,
      ],
      // Terms of service url.
      tosUrl: '<your-tos-url>'
    }

    // Initialize the FirebaseUI Widget using Firebase.
    let firebaseui = (this.$i18n.locale.toLowerCase().includes('ja')) ? firebaseuiJA : firebaseuiEN
    let ui = new firebaseui.auth.AuthUI(firebase.auth())
    // The start method will wait until the DOM is loaded.
    ui.start('#firebaseui-auth-container', uiConfig)
  },
}

As I explained earlier, multiple translations in one file is not recommended as it will multiply the size of the file by the number of languages you want to support and it will raise other issues. I described 2 approaches and none of them include multiple languages in one file.
You can either reload the page with the new language, or we can help support client side dynamic loading of the language on selection. I still plan to experiment with that.

@bojeil-google Do you have any advice for me how to load a different language in a Single Page Application?
Since I can only have 1 HTML file, I don't think the "refresh the page" method will work...

I would use lazy-loading of the library. Your app will be too big and it is not good for performance to include all languages in the same bundle.

So you could still use a URL parameter or a has fragment, for instance let say you load

https://www.example.com/auth?lang=en

your code can be:

const locale = 'en' // Read the locale from the URL parameter here!

const firebaseUILoader;
if (locale === 'en') {
  firebaseUILoader = import('../statics/firebaseui__en.js')
} else if ( (locale === 'ja') {
  firebaseUILoader = import('../statics/firebaseui__ja.js')
}

firebaseUILoader.then(firebaseui => {
  // You can use FirebaseUI here.
});

This is using Webpack's dynamic import feature. The different localised versions of Firebase will be generated in different chunks that will be loaded on the fly when requested.

As you can see above the library is loaded asynchroneously so you'll get firebaseui in a Promise. it gets nice though if you use async/await:

const locale = 'en' // Read the locale from the URL parameter here!

async function loadFirebaseUI() {
  const firebaseui;
  if (locale === 'en') {
    firebaseui = await import('../statics/firebaseui__en.js')
  } else if ( (locale === 'ja') {
    firebaseui = await import('../statics/firebaseui__ja.js')
  }

  // You can use Firebase UI here.
}

btw @bojeil-google, it would be cool to provide all the localised JS files in the NPM distrib so taht developers could do that easily.

I use vue + webpack. Localized modules are loaded async / await import successfully,
but when creating a widget instance new firebaseui.auth.AuthUI (appId) - an error crashes:
Uncaught (in promise) ReferenceError: firebase is not defined at new Dn (firebase-ui-auth__uk.js?c470:formatted:1) at eval (Auth.vue?1e10:41)
on row var x = new firebase.auth.RecaptchaVerifier(um ? n.jg() : n.H(),e,An(a).app);

On the top of <script> block of page i have import * as firebase from 'firebase'; and I tried it like import firebase from 'firebase'; ...but without positive result

@yevheniidehtiar Does this error only happen with localized version? And how did you load the module?

Each internationalized version is a separate build. Including all the versions in one file is not feasible.

Oh dear. Looks like you've used the wrong implementation architecture to support i18n for SPA web apps!

Typically if a user switches languages, you would redirect to another page (eg. https://www.example.com/auth?lang=ja") and load the UI version with the selected language."

Except that we're all coding SPA web apps today so we don't reload pages from the server just one static page index.html and our client side "pages" are not server side rendered (remember: Firebase hosting is for static pages!) So making a Firebase UI build per language is inherently broken.

More commonly we wish to provide a feature in our SPA app to allow the user to change their language, dynamically. We expect that we can call a firebaseui.setLanguage() function that will lazy load a small JSON file to import the language changes. You could provide a firebaseui.getLocale() which inspectswindow.navigator.languages and map the browser locale to a matching FirebaseUI locale. If implemented this way, Firebase UI can just work by auto selecting the language.

Workarounds ...

Okay so I can kinda patch this up by inspecting window.navigator.languages, then creating a mapping function to convert real browser locales to the different ones provided by Firebase UI and do a dynamic load from the CDN of the language specific build.

I now need to build just RTL and non-RTL versions of my index.html file since I cannot (currently) dynamically load CSS files with webpack.

Reference

Postlude

I've now had a closer look at how you're doing i18n and I see that you're using Angular's 18n that forces you to do per language SDK builds. I still think you need a way to lazy loading the language build based on browser locale. My SPA framework assists with this restriction so i can workaround this.

assists with this restriction

@tohagan As I'm building a SPA (possibly a PWA, possibly a PWA+SSR) with Quasar, I'm really interested in your workaround.
Could you please share your approach?
Many thanks.

FirebaseUI Team: I find it odd, to say the least, that Google promotes PWA all year round, but then you couldn't come up with some real-time detection/switch for the language used.
This speaks volumes on how some developers (or product managers) think english is the sole spoken language across the world (you'll pardon the rant, it is well deserved).

i18n hot switching should be thought of right from the start and from the ground up,
whatever the web software project.

I use vue + webpack. Localized modules are loaded async / await import successfully,
but when creating a widget instance new firebaseui.auth.AuthUI (appId) - an error crashes:
Uncaught (in promise) ReferenceError: firebase is not defined at new Dn (firebase-ui-auth__uk.js?c470:formatted:1) at eval (Auth.vue?1e10:41)
on row var x = new firebase.auth.RecaptchaVerifier(um ? n.jg() : n.H(),e,An(a).app);

On the top of <script> block of page i have import * as firebase from 'firebase'; and I tried it like import firebase from 'firebase'; ...but without positive result

Same problem with JS binaries, using Quasar (Webpack).

default import works:
import * as firebaseui from 'firebaseui'

This doesn't:
import * as firebaseui from 'firebaseui/dist/firebaseui__it'

nor:
import firebaseui from 'firebaseui/dist/firebaseui__it'

Console error:
TypeError: Cannot read property 'AuthUI' of undefined