BLOG

Efficiently Caching Translations in React with Zustand and Google Translate API

Efficiently Caching Translations in React with Zustand and Google Translate API

Mauro Davoli

Frontend Engineer

Dec 20, 2024

3 min read

React

Introduction

Integrating a translation feature in your React app can enhance accessibility and expand your audience. However, repeatedly fetching translations from APIs like Google Translate can increase latency and costs. We’ll combine React, Zustand, and the Google Translate API to build a performant, cache-enabled translation solution to tackle those issues.

In this article, you’ll learn how to do the following:

  1. Set up a Zustand store to cache translations and avoid redundant API calls.
  2. Use Google Translate API for dynamic translations.
  3. Implement the solution with React hooks for seamless integration.

Why Zustand?

Zustand is a lightweight state management library that simplifies state handling in React. Its simplicity and scalability make it ideal for managing and caching translations in memory.

Step 1: Creating the Zustand Store

Our Zustand store will manage:

  • A cache for storing translations.
  • yoA registry to track ongoing API requests and avoid duplicates.
  • A method to fetch translations while leveraging the cache.
import { create } from "zustand";

interface TranslationStore {
  translations: Record<string, string>;
  ongoingRequests: Record<string, Promise<string>>;
  getTranslation: (
    text: string,
    targetLang: string,
    fetchTranslation: (text: string, targetLang: string) => Promise<string>,
  ) => Promise<string>;
}

export const useTranslationStore = create<TranslationStore>((set, get) => ({
  translations: {},
  ongoingRequests: {},
  getTranslation: async (text, targetLang, fetchTranslation) => {
    const cacheKey = `${text}_${targetLang}`;
    const { translations, ongoingRequests } = get();

    if (translations[cacheKey]) {
      return translations[cacheKey];
    }

    if (await ongoingRequests[cacheKey]) {
      return ongoingRequests[cacheKey];
    }

    const requestPromise = fetchTranslation(text, targetLang);
    set((state) => ({
      ongoingRequests: { ...state.ongoingRequests, [cacheKey]: requestPromise },
    }));

    try {
      const translatedText = await requestPromise;

      set((state) => ({
        translations: { ...state.translations, [cacheKey]: translatedText },
      }));

      return translatedText;
    } finally {
      set((state) => {
        const updatedRequests = { ...state.ongoingRequests };
        delete updatedRequests[cacheKey];
        return { ongoingRequests: updatedRequests };
      });
    }
  },
}));

export default useTranslationStore;

Step 2: Integrating Google Translate API

Create a function to interact with the Google Translate API:

import { useCallback, useState } from "react";
import axios from "axios";

const API_KEY = `${process.env.GOOGLE_TRANSLATION_API_KEY}`!;
const API_URL = `${process.env.GOOGLE_TRANSLATION_API_URL}`!;

const useTranslateText = () => {

  const translateText = useCallback(
    async (text: string, targetLanguage: string) => {
      try {
        const response = await axios.post(`${API_URL}?key=${API_KEY}`, {
          q: text,
          target: targetLanguage,
        });
        const translated = response.data.data.translations[0].translatedText;

        setTranslatedText(translated);
        return translated;
      } catch (err: any) {
        return null;
      }
    },
    [],
  );

  return { translateText };
};

export default useTranslateText;

Step 3: Using the Store in a React Component

Now, let’s build a component that uses the store to fetch and display translations:

import React, { useEffect, useState } from 'react';
import useTranslationStore from './useTranslationStore';
import { useTranslation } from "react-i18next";

const TranslationComponent = ({ text }: { text: string }) => {
  const { i18n } = useTranslation();
  const [translatedText, setTranslatedText] = useState('');
 
  const { translateText } = useTranslateText();
  const getTranslation = useTranslationStore((state) => state.getTranslation);

   useEffect(() => {
    const translateActions = async () => {
      if (i18n.language === "en") return;

        const result = await getTranslation(
          "example text",
          i18n.language,
          translateText,
        );

        setTranslatedText(result);
      }
      
    fetchTranslation();
  }, [text, getTranslation]);

  return (
    <div>
      <h1>Original: {text}</h1>
      <h2>Translated: {translatedText}</h2>
    </div>
  );
};

export default TranslationComponent;

Benefits of this Approach

  1. Performance Optimization: Translations are cached, reducing redundant API calls.
  2. Scalability: The approach is flexible and can handle additional languages or features.
  3. Ease of Maintenance: Zustand’s simple API makes state management clean and intuitive.

Conclusion

By integrating Zustand and the Google Translate API, we’ve built a highly efficient and scalable translation system. This solution minimizes API usage, improves user experience, and keeps your codebase organized. Start implementing this in your projects and elevate the global reach of your applications! 🚀

Mauro Davoli

Frontend Engineer

Dec 20, 2024

3 min read

React

BLOG

Unlock forbidden knowledge

Explore more

Arrow Icon
Fast API Development with Hono and Cloudflare Workers

Backend

Technology

Guides

Fast API Development with Hono and Cloudflare Workers

Pablo shares his journey of finding tools to build REST APIs quickly and efficiently. He highlights his experience with Hono, a lightweight framework reminiscent of Express, designed specifically for Cloudflare Workers.

Pablo Haller

A step-by-step infographic showing the process to set up authentication with Next.js using the next-auth library.

Guides

Technology

Basic GitHub OAuth Authentication with Next.js and next-auth

This blog explains how to set up GitHub OAuth in Next.js using next-auth. It covers creating a GitHub OAuth app, configuring environment variables, integrating authentication with a custom route, protecting routes, and managing sessions.

Pablo Haller

Building cool search UIs with Algolia and InstantSearch

Frontend

Technology

Guides

Building cool search UIs with Algolia and InstantSearch

In this article, we’re going to pick some characters from a video game (Genshin Impact) and display them in a fancy way using the aforementioned programs.

Pablo Haller