Localize Messages

This documentation outlines the use of FCM localization fields (*_loc_key and *_loc_args) to deliver notifications that automatically adapt to a user's language settings on Android and iOS. This lets your server send a single, language-agnostic payload, delegating the translation to the client device.

FCM Localization Overview

To localize your app, you can send a key that corresponds to a string resource entry inside the user's application. The device's operating system (OS) handles the lookup and insertion of dynamic arguments.

FCM Field Description Client Action
title_loc_key The key for the title string in the client app's string resources. The OS finds the corresponding string in the app's localized files.
body_loc_key The key for the body string in the client app's string resources. The OS finds the corresponding string in the app's localized files.
title_loc_args An array of dynamic string values to be substituted into the title_loc_key string. The OS inserts these arguments into the localized string's format specifiers.
body_loc_args An array of dynamic string values to be substituted into the body_loc_key string. The OS inserts these arguments into the localized string's format specifiers.

Step 1: Define localized string resources in your apps

To get started with FCM localization, it is important to make sure that you have the necessary translations available in your Android and iOS projects.

Android Setup

Define string resources: Enter your default language strings in res/values/strings.xml. Use format specifiers (%1$s, %2$d, etc.) for any dynamic values you plan to pass in the *_loc_args.

Default (res/values/strings.xml):

<resources>
    <string name="welcome_title">Welcome, %1$s!</string>
    <string name="new_message_body">You have %1$d new message(s) from %2$s.</string>
</resources>

Add translations: Create language-specific directories using the ISO language codes (e.g., values-fr for French, values-es for Spanish) and translate the keys.

French (res/values-fr/strings.xml):

<resources>
    <string name="welcome_title">Bienvenue, %1$s!</string>
    <string name="new_message_body">Vous avez %1$d nouveau(x) message(s) de %2$s.</string>
</resources>

For more information, use the following documentation:

iOS Setup

Define string resources: Define your base strings in the Localizable.strings file (typically in the Base.lproj folder or a String Catalog). Use format specifiers (%@, %ld, etc.) for dynamic values. Keys are often defined in all capital letters for convention.

Default (English Localizable.strings):

"WELCOME_TITLE" = "Welcome, %@!";
"NEW_MESSAGE_BODY" = "You have %ld new message(s) from %@.";

Add translations: Create language-specific .lproj folders (or add localizations using a String Catalog) and translate the keys.

French (fr.lproj/Localizable.strings):

"WELCOME_TITLE" = "Bienvenue, %@!";
"NEW_MESSAGE_BODY" = "Vous avez %ld nouveau(x) message(s) de %@.";

For more information, use the following documentation:

Step 2: Construct the FCM message payload

When sending the notification using the FCM HTTP v1 API, your server constructs a single payload that uses the resource keys (*_loc_key) and the dynamic data (*_loc_args) as an array of strings.

Example FCM HTTP v1 Payload

The localization keys are placed within the platform-specific override blocks (android.notification and apns.payload.aps.alert).

{
  "message": {
    "token": "DEVICE_REGISTRATION_TOKEN",

    "android": {
      "notification": {
        // Android keys match strings.xml resource names
        "title_loc_key": "welcome_title",
        "title_loc_args": ["Alice"],
        "body_loc_key": "new_message_body",
        "body_loc_args": ["3", "Bob"]
      }
    },

    "apns": {
      "payload": {
        "aps": {
          "alert": {
            // iOS uses 'title-loc-key' and 'loc-key' (for the body)
            "title-loc-key": "WELCOME_TITLE",
            "title-loc-args": ["Alice"],
            "loc-key": "NEW_MESSAGE_BODY",
            "loc-args": ["3", "Bob"]
          }
        }
      }
    }
  }
}

Key considerations for payload arguments

  • Order matters: The strings in *_loc_args must be in the exact order required by the placeholders in the string resource file (e.g., %1$s, %2$s).

  • Strings only: All elements in the *_loc_args array must be strings, even if they represent numbers (like "3" in the example). The client OS's string formatter handles the final type conversion based on the format specifier (%ld or %1$d).

Step 3: Client processing and display

When the device receives the notification, the following steps occur automatically:

  1. Language check: The device identifies the user's primary locale (e.g., German, Italian).

  2. Key lookup: The OS uses the *_loc_key value (welcome_title) to look up the corresponding translated string in the app's resource files for the device's locale.

  3. Argument insertion: The OS takes the array from *_loc_args (["Alice"]) and inserts the values into the localized string, respecting the locale's formatting rules (punctuation, word order, etc.).

Device Locale title_loc_key: welcome_title title_loc_args: ["Alice"] Final Title Display
English "Welcome, %1$s!" Alice "Welcome, Alice!"
French "Bienvenue, %1$s!" Alice "Bienvenue, Alice!"
German "Willkommen, %1$s!" Alice "Willkommen, Alice!"

This process makes sure that every user receives a message tailored to their language preferences, using the correct linguistic structure, all while maintaining a standardized payload from your server.

Example: notification message with localization options

The following example send request sends a notification to the Tech topic, including localization options for the client to display localized messages. Here's an example of the visual effect on a user's device:

Simple drawing of two devices displaying text in English and Spanish

Node.js

var topicName = 'industry-tech';

var message = {
  android: {
    ttl: 3600000,
    notification: {
      bodyLocKey: 'STOCK_NOTIFICATION_BODY',
      bodyLocArgs: ['FooCorp', '11.80', '835.67', '1.43']
    }
  },
  apns: {
    payload: {
      aps: {
        alert: {
          locKey: 'STOCK_NOTIFICATION_BODY',
          locArgs: ['FooCorp', '11.80', '835.67', '1.43']
        }
      }
    }
  },
  topic: topicName,
};

getMessaging().send(message)
  .then((response) => {
    // Response is a message ID string.
    console.log('Successfully sent message:', response);
  })
  .catch((error) => {
    console.log('Error sending message:', error);
  });

REST

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA
{
  "message": {
    "topic":"Tech",
    "android": {
      "ttl":"3600s",
      "notification": {
        "body_loc_key": "STOCK_NOTIFICATION_BODY",
        "body_loc_args": ["FooCorp", "11.80", "835.67", "1.43"]
      }
    },
    "apns": {
      "payload": {
        "aps": {
          "alert": {
            "loc-key": "STOCK_NOTIFICATION_BODY",
            "loc-args": ["FooCorp", "11.80", "835.67", "1.43"]
          }
        }
      }
    }
  }
}'

To learn more, see AndroidNotification and ApnsConfig in the HTTP v1 reference documentation for complete detail on the keys available in platform-specific blocks in the message body. For keys supported by APNS, see Apple's Payload Key Reference.