Implementing Internationalization in Apps: How to Translate Notifications

Prosper Otemuyiwa - Mar 1 - - Dev Community

Making your digital products accessible to people in different regions around the world opens up new markets for your business and introduces you to a new audience.

Novu recently shipped with a Translation Management feature to make translating your app's notification content to different languages a breeze. In this article, I'll show you a step-by-step technical approach on how to accomplish this in your app.

Prerequisites

Before diving into the article, make sure you have the following:

  • Node.js installed on your development machine.
  • A Novu account. If you don’t have one, sign up for free at the web dashboard.

If you don’t want to explore the code right away, you can view the completed code  on GitHub.

Set up a Next.js App

To create a Next.js app, open your terminal, cd into the directory you’d like to create the app in, and run the following command:

npx create-next-app@latest notifier
Enter fullscreen mode Exit fullscreen mode

Go through the prompts:

Go through the prompts

cd into the directory, notifier and run to start the app in your browser:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Set up Novu in the App

Run the following command to install the Novu node SDK:

npm install @novu/node
Enter fullscreen mode Exit fullscreen mode

Run the following command to install the Novu Notification Center package:

npm install @novu/notification-center
Enter fullscreen mode Exit fullscreen mode

The Novu Notification Center package provides a React component library that adds a fully functioning notification center to your React app. The package is also available for non-React apps.

Before using Novu in your React app, a few things need to be set up:

  1. Create a workflow for sending notifications,
  2. Create a subscriber - recipient of notifications.

Create a workflow

A workflow is a blueprint for notifications. It includes the following:

  • Workflow name and Identifier
  • Channels: - Email, SMS, Chat, In-App and Push

Follow the steps below to create a workflow:

  1. Click Workflow on the left sidebar of your Novu dashboard.
  2. Click the Add a Workflow button on the top left. You can select a Blank workflow or use one of the existing templates.
  3. The name of the new workflow is currently “Untitled”. Rename it to a more suitable title.
  4. Select In-App as the channel you want to add.

In-App Channel

  1. Click on the recently added “In-App” channel and add the following text to it. Once you’re done, click “Update” to save your configuration.

Update channel

The {{name}} and {{handle}} are custom variables. This means that we can pass them to our payload before we trigger a notification. You’ll see this when we create the API route to send notifications.

Create a subscriber

If you click “Subscriber” on the left sidebar of the Novu dashboard, you’ll see the subscriber list. As a first time Novu user, it will be an empty list.

Subscribers are recipients of notifications. In this case, subscribers are your app users.

Subscriber

You can add a subscriber to Novu by running this API endpoint. Fill in the details, add your API key and run it for the sake of this tutorial!

You can add via code. It’s documented in our subscriber docs.

Refresh the Subscribers page on your Novu dashboard. You should see the recently added subscriber now!

Set up Novu Notification Center in the App

Head over to scr/pages/index.js. We’ll modify this page to include the following:

  • Import and display the Novu Notification Center.
  • A form added to this page.
  • A function to submit the form.
    • When the form is submitted, it triggers a notification.
  • A function to trigger the notification.

Copy and past the code below to replace everything in the src/pages/index.js file.

import Image from "next/image";
import { Inter } from "next/font/google";
import {
  NovuProvider,
  PopoverNotificationCenter,
  NotificationBell,
} from "@novu/notification-center";

const inter = Inter({ subsets: ["latin"] });

export default function Home() {
  function handleSubmit(e) {
    e.preventDefault();

    const name = e.target.name.value;
    const handle = e.target.handle.value;
    const userID = process.env.NEXT_PUBLIC_SUBSCRIBER_ID;

    alert("Sending notification right now...");

    triggerNotification(userID, handle, name);
  }

  async function triggerNotification(userID, handle, name) {
    await fetch("/api/send-notification", {
      method: "POST",
      body: JSON.stringify({
        subscriberID: userID,
        userHandle: handle,
        username: name,
      }),
    });
  }

  return (
    <main
      className={`flex min-h-screen flex-col items-center justify-between p-24 ${inter.className}`}
    >
      <div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
        <p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
          APP DASHBOARD - FINANTICS
        </p>
        <div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none">
          <NovuProvider
            subscriberId={process.env.NEXT_PUBLIC_SUBSCRIBER_ID}
            applicationIdentifier={process.env.NEXT_PUBLIC_NOVU_APP_ID}
          >
            <PopoverNotificationCenter>
              {({ unseenCount }) => (
                <NotificationBell unseenCount={unseenCount} />
              )}
            </PopoverNotificationCenter>
          </NovuProvider>
        </div>
      </div>

      <div className="mt-1 flex justify-center">
        <form onSubmit={handleSubmit}>
          <div>
            <label className="p-3">Name:</label>
            <input
              className="text-black p-2"
              type="text"
              name="name"
              required
              placeholder="Enter name"
            />
          </div>
          <div className="mt-5">
            <label className="p-2">Handle:</label>
            <input
              className="text-black p-2"
              type="text"
              name="handle"
              required
              placeholder="Enter handle"
            />
          </div>

          <button
            type="submit"
            className="bg-blue-600 p-2 rounded-md mt-5 px-12 ml-3"
          >
            Submit Details
          </button>
        </form>
      </div>

      <div className="mb-32 grid text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:grid-cols-4 lg:text-left">
        <a
          href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
          className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={`mb-3 text-2xl font-semibold`}>
            Docs{" "}
            <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
              -&gt;
            </span>
          </h2>
          <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
            Find in-depth information about Next.js features and API.
          </p>
        </a>

        <a
          href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
          className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={`mb-3 text-2xl font-semibold`}>
            Learn{" "}
            <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
              -&gt;
            </span>
          </h2>
          <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
            Learn about Next.js in an interactive course with&nbsp;quizzes!
          </p>
        </a>

        <a
          href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
          className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={`mb-3 text-2xl font-semibold`}>
            Templates{" "}
            <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
              -&gt;
            </span>
          </h2>
          <p className={`m-0 max-w-[30ch] text-sm opacity-50`}>
            Discover and deploy boilerplate example Next.js&nbsp;projects.
          </p>
        </a>

        <a
          href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app"
          className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
          target="_blank"
          rel="noopener noreferrer"
        >
          <h2 className={`mb-3 text-2xl font-semibold`}>
            Deploy{" "}
            <span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
              -&gt;
            </span>
          </h2>
          <p className={`m-0 max-w-[30ch] text-sm opacity-50 text-balance`}>
            Instantly deploy your Next.js site to a shareable URL with Vercel.
          </p>
        </a>
      </div>
    </main>
  );
}

Enter fullscreen mode Exit fullscreen mode

From the code above, you can see the Notification Center component block:

import {
  NovuProvider,
  PopoverNotificationCenter,
  NotificationBell,
} from "@novu/notification-center";

...
...
...

<NovuProvider
  subscriberId={process.env.NEXT_PUBLIC_SUBSCRIBER_ID}
  applicationIdentifier={process.env.NEXT_PUBLIC_NOVU_APP_ID}
>
  <PopoverNotificationCenter>
    {({ unseenCount }) => (
      <NotificationBell unseenCount={unseenCount} />
    )}
  </PopoverNotificationCenter>
</NovuProvider>
...
...
...
Enter fullscreen mode Exit fullscreen mode

The NovuProvider root Component ships with many props that can be used to customize the Notification Center to your taste.

The floating popover component that appears when clicking on the NotificationBell button. It renders the NotificationCenter component inside its content.

Note: Please ensure NEXT_PUBLIC_SUBSCRIBER_ID, NEXT_PUBLIC_NOVU_APP_ID, and NEXT_PUBLIC_NOVU_API_KEY are present with their respective values in your .env file.

The Novu API Key and APP ID can be found in the Settings section of your Novu dashboard.

Run your app. It should look like so with the notification bell showing at the top right:

Notification bell

Submit the details with a name and handle. A notification should show up like so:

Submit details

Set Up App Notification Translation & Internationalization - i18n

Head over to the Translations section of the left sidebar on Novu Dashboard.

  • Create a Translation Group.

Create a translation group

Before creating your first Translation Group you’ll be prompted to specify the default locale for organization.

This locale serves as a fallback option for localized messages in cases where the recipient hasn’t defined a specific locale.

Fallback locale

  • Name the translation group and add the languages you want your app to support. In our app, we’ll support German and Swahili in addition to the default English language specified earlier.

Name translation group

Translation languages

  • Upload JSON translation files for each of the languages. In this case, we’ll have to upload for German, Swahili and English.

Upload JSON files

Create JSON language files with the respective content and translations, then upload them to the corresponding languages on your Novu dashboard.

English (en.json)

{
  "welcome_message": "Welcome to Notifier!",
  "learn_more": "Learn More",
  "special_offer": "Special Offer",
  "contact_us": "Contact Us"
}

Enter fullscreen mode Exit fullscreen mode

German (de.json)

{
  "welcome_message": "Willkommen bei Notifier!",
  "learn_more": "Mehr erfahren",
  "special_offer": "Sonderangebot",
  "contact_us": "Kontaktieren Sie uns"
}

Enter fullscreen mode Exit fullscreen mode

Swahili (sw.json)

{
  "welcome_message": "Karibu Notifier!",
  "learn_more": "Jifunze zaidi",
  "special_offer": "Ofa maalum",
  "contact_us": "Wasiliana nasi"
}

Enter fullscreen mode Exit fullscreen mode

JSON files

  • Now, head back to the In-App channel editor and try to type. Novu auto-suggests the translation variables in the editor. It consists of the i18n handlebar helper and path to the translated value.

Novu translation auto-suggest

  • Let’s localize our message content. Using the i18n handlebar helper , we have pointed to welcome_message and contact.us. The Subscribers locale defines in which language the message should be delivered to that recipient. When the locale is not specified, it will fallback to the default locale set for the organization.

message content localization

  • Set a locale attribute to the subscriber we created earlier. You use the Novu SDK or do it via the API. Before proceeding, let's run the app quickly. It should trigger a notification with the default language locale that we previously set for the organization.

English locale

  • Set it to German, the locale of the subscriber should be “de_DE”. Now, run your app and check the notification that comes in!

German locale

  • Set it to Swahili, the locale of the subscriber should be “sw_KE”. Now, run your app and check the notification that comes in!

Swahili locale

Hooray! We both understand how amazing and straightforward this is. Wow! I wish I Novu existed when I started coding a decade ago. Feel free to check out the complete code on GitHub.

This is all you need to render your app's notification content in as many languages as you want. You can quickly add more languages, update the locale JSON files with additional content, and expand to as many markets as possible!

Note: The translation feature is only available for Novu Cloud users with Business or Enterprise plans.

Check out the documentation for Translation management and incorporate it into your apps! We can't wait to see the incredible apps you create. Don't hesitate to ask us any questions or for support. You can find us on Discord and Twitter. Feel free to reach out.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .