How to internationalize/localize your FCM push notifications, especially topics?

Viewed 9072

I'd like to use Firebase to send push notifications to both Android and iOS devices which are localized.

I realized we didn't really have a solution for sending localized messages to subscribed topics. Pretend I have a message 'North Korea leader threatens Guam' that I want to send to people subscribed to the 'News' topic and 1000 people are subscribed (and they all speak different languages). I was hoping Firebase records the devices locale when requesting a FCM token, and that I could just send an array of messages per locale to this topic and let FCM/firebase handle, like so:

{
  "default_message": "North Korea leader threatens Guam",
  "en_US": "North Korea leader threatens Guam",
  "en_GB": "North Korea leader threatens Guam",
  "es_MX": "Líder de Corea del Norte amenaza a Guam",
  "fr_FR": "Le chef de la Corée du Nord menace Guam",
  "de_DE": "Nordkorea-Führer droht Guam"
}

I have seen some references to maybe using title_loc_key, body_loc_key but don't see a good example on how the request would look like. These 2 params also imply maybe they are just used as a translation lookup key that the app also has to store and lookup locally, and I can't send a brand new off-the-cuff localized message to people (since the translation would have to be stored in the app beforehand like 'april_newsletter_text')? Not sure how it works, just throwing some thoughts out there.

Note: I am using a php library to do send firebase push notifications (Laravel-FCM). I believe some devs in our team have also extended that library a bit to do some custom tasks so if there is a way to do it directly via GCM (gcm-http.googleapis.com/gcm/) instead of FCM, then I can add.

2 Answers

You can combine topics using "conditions" (see docs). These allow you to send your notification to a combination of topics, eg.

const message = {
    notification: {
        title: 'North Korea leader threatens Guam',
        body: '...'
    },
    condition: `'KoreaNews' in topics && 'LangEnGB' in topics`
};

const messageFr = {
    notification: {
        title: 'Le chef de la Corée du Nord menace Guam',
        body: '...'
    },
    condition: `'KoreaNews' in topics && 'LangFrFR' in topics`
};

If you are using firebase you have code like this in your JS worker

let messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function (payload) {
# Here  the callback for web push notification

Typically you would show the message in this callback. The task is to localise it. So you may just send users the message about a new message. Fetch localised message and then show it to the user. On fetch you would get Accept-Language http header or you can send the user id. Here is my example from my project:

messaging.setBackgroundMessageHandler(function (payload) {

    let data = [];
    try {
        if (payload.data && payload.data.data) {
            data = JSON.parse(payload.data.data);
        }
    } catch (e) {
        Sentry.captureException(e);
    }
    let query = Object.keys(data).map(k => encodeURIComponent(k) + '=' + encodeURIComponent(data[k])).join('&');

    return fetch(self.location.origin + "/web-push/run.json?" + query)
        .then(function (response) {
            # ........................
            #.  Skipped working with https status codes code.
            # ........................
            return response.json();
        })
        .then(function (response) {
            #
            #  Here we have an localised response from the server
            # 
            if (response) {
                return self.registration.showNotification(response.title, {
                    priority: "high",
                    tag: 'renotify',
                    requireInteraction: true,
                    body: response.description,
                    icon: response.icon,
                    image: response.image,
                    url: response.link,
                    click_action: response.link,
                    data: {
                        url: response.link
                    },
                    vibrate: [500, 110, 500, 110, 450, 110, 200, 110, 170, 40, 450, 110, 200, 110, 170, 40, 500]
                })
            }
        })
        .catch(function (err) {
            Sentry.captureException(err);
        });
});
Related