Search code examples
facebookvue.jsnuxt.jsvue-i18n

change facebook sdk lang code dynamically in vue (nuxt)


i am currently working on a simple like implementation in nuxt. when i change the language with 1i8n, i want to change the facebook sdk language accordingly, so the button renders in the given language code when i change the overall app language. my code looks like this:

import config from '@/config'

export default {
    data() {
        return {
            FB_APP_ID: config.appname.FB_APP_ID
        }
    },
    mounted() {
        var langua;

        if (this.$i18n.locale == 'en') {
            langua = "en_US";
        }

        if (this.$i18n.locale == 'de') {
            langua = "de_DE";
        }

        window.fbAsyncInit = () => {
            FB.init({
                appId: this.FB_APP_ID,
                cookie: true,
                xfbml: true,
                version: 'v2.8'
            })
        }

        (function(d, s, id){
            var js, fjs = d.getElementsByTagName(s)[0];
            if (d.getElementById(id)) {return;}
            js = d.createElement(s); js.id = id;
            js.src = "//connect.facebook.net/" + langua + "/sdk.js";
            fjs.parentNode.insertBefore(js, fjs);
        }(document, 'script', 'facebook-jssdk'));
    }
}

it works but the dynamic change is not happening, do i miss something like async on the button sdk here ??? no idea, i am new to vue, help is appreciated thanks a lot.


Solution

  • That won’t work, the SDK can only be embedded and initialized once.

    This is not entirely true: there is a workaround.

    If you add a new script to the head, it will be executed once loaded. This is true for any script. If it doesn't, it means the script has a guard that prevents running the same code again most likely by checking if one of its variables is already defined.

    In the case of Facebook SDK, it will check if FB is already defined globally so you simply need to delete it before adding your new sdk script with a different locale.

    My code looks a bit different, but I'm also using Nuxt:

    <template>
      <div
        :key="`fb-chat-${$i18n.locale}`"
        class="fb-customerchat"
        :page_id="pageId"
        theme_color="#4586ff"
        greeting_dialog_display="hide"
        :logged_in_greeting="$t('greeting')"
        :logged_out_greeting="$t('greeting')"
      ></div>
    </template>
    
    <script>
    export default {
      name: 'FacebookChat',
    
      data() {
        return {
          pageId: process.env.FACEBOOK_PAGE_ID,
        }
      },
    
      watch: {
        '$i18n.locale': 'resetFacebookSdk',
      },
    
      mounted() {
        this.initFacebookSdk()
      },
    
      methods: {
        initFacebookSdk() {
          if (!process.browser) return
    
          const locale = this.$i18n.locale === 'de' ? 'de_DE' : 'en_US'
          delete window.FB // needs to be undefined when inserting a second script with different locale
    
          window.fbAsyncInit = function () {
            window.FB.init({
              appId: process.env.FACEBOOK_APP_ID,
              autoLogAppEvents: true,
              xfbml: true,
              version: 'v10.0',
            })
          }
          ;(function (d, s, id) {
            let js = d.getElementById(id),
              fjs = d.getElementsByTagName(s)[0]
            if (js) js.parentNode.removeChild(js) // remove script tag if exists
            js = d.createElement(s)
            js.id = id
            js.src = `https://connect.facebook.net/${locale}/sdk/xfbml.customerchat.js`
            fjs.parentNode.insertBefore(js, fjs)
          })(document, 'script', `facebook-jssdk-${this.$i18n.locale}`)
        },
    
        resetFacebookSdk() {
          const fbRoot = this.$el.closest('#fb-root')
          if (!fbRoot) return
    
          // Move fb-customerchat element outside of fb-root (created by Facebook SDK)
          fbRoot.parentNode.insertBefore(this.$el, fbRoot)
          // Delete fb-root to let Facebook SDK create it again
          fbRoot.parentNode.removeChild(fbRoot)
    
          this.initFacebookSdk()
        },
      },
    }
    </script>
    

    What you should pay attention to:


    • The locale is part of the key attribute of my element to make it re-render on locale change in order to remove what the sdk has added (attributes, child elements)
    • There is a watcher to call resetFacebookSdk on locale change
    • I modified the sdk code snippet to remove the script tag if it was already present instead of doing nothing. This way, we can create it again.
    • The element .fb-customerchat is cleaned up with the key attribute, but the sdk has wrapped it with a new element #fb-root. The method resetFacebookSdk is taking care of moving .fb-customerchat outside of that wrapper and deleting it.

    You can see it in action on our website (you can switch language in the footer).

    Hope it helps. Let me know if something is not clear.