electron-shadcn

Internationalization (i18n)

How to implement and manage multiple languages in your electron-shadcn application using i18next

GitHubEdit on GitHub

electron-shadcn comes with built-in internationalization support using i18next and react-i18next. This allows you to easily translate your application into multiple languages.

Directory Structure

Internationalization files are organized as follows:

src/
├── localization/
│   ├── i18n.ts           # i18next configuration and translations
│   ├── langs.ts          # Available languages list
│   └── language.ts       # Language type definition
├── actions/
│   └── language.ts       # Language actions (set/update)
├── components/
│   └── lang-toggle.tsx   # Language switcher component
└── constants/
    └── index.ts          # Contains localStorage keys

Using Translations

Basic Usage

Use the useTranslation hook to access translations in your components:

import { useTranslation } from "react-i18next";

export default function MyComponent() {
  const { t } = useTranslation();

  return (
    <div>
      <h1>{t("appName")}</h1>
      <p>{t("titleHomePage")}</p>
    </div>
  );
}

Available Translation Keys

The template comes with these default translation keys:

KeyEnglishPortuguese (Brazil)
appNameelectron-shadcnelectron-shadcn
titleHomePageHome PagePágina Inicial
titleSecondPageSecond PageSegunda Página
documentationDocumentationDocumentação
versionVersionVersão
madeByMade by LuanRogerFeito por LuanRoger

These keys can be deleted or modified as needed, since they are just examples.

Adding a New Language

Add Translations to i18n.ts

Open src/localization/i18n.ts and add a new language resource:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";

i18n.use(initReactI18next).init({
  fallbackLng: "en",
  resources: {
    en: {
      translation: {
        appName: "electron-shadcn",
        titleHomePage: "Home Page",
        titleSecondPage: "Second Page",
        documentation: "Documentation",
        version: "Version",
        madeBy: "Made by LuanRoger",
      },
    },
    "pt-BR": {
      translation: {
        appName: "electron-shadcn",
        titleHomePage: "Página Inicial",
        titleSecondPage: "Segunda Página",
        documentation: "Documentação",
        version: "Versão",
        madeBy: "Feito por LuanRoger",
      },
    },
    // Add your new language here
    es: {
      translation: {
        appName: "electron-shadcn",
        titleHomePage: "Página de Inicio",
        titleSecondPage: "Segunda Página",
        documentation: "Documentación",
        version: "Versión",
        madeBy: "Hecho por LuanRoger",
      },
    },
  },
});

Register the Language in langs.ts

Add the new language to the src/localization/langs.ts file:

import { Language } from "./language";

export default [
  {
    key: "en",
    nativeName: "English",
    prefix: "EN-US",
  },
  {
    key: "pt-BR",
    nativeName: "Português (Brasil)",
    prefix: "PT-BR",
  },
  // Add your new language
  {
    key: "es",
    nativeName: "Español",
    prefix: "ES",
  },
] as const satisfies Language[];

Test the New Language

Run your application and use the language toggle to switch to the new language:

npm run start

The language toggle component will automatically display the new language option.

Adding New Translation Keys

When you need new translatable text in your application:

Add Keys to All Languages

Update src/localization/i18n.ts to include the new key in all language resources:

i18n.use(initReactI18next).init({
  fallbackLng: "en",
  resources: {
    en: {
      translation: {
        // ... existing keys
        welcomeMessage: "Welcome to the app!",
        settings: "Settings",
        save: "Save",
        cancel: "Cancel",
      },
    },
    "pt-BR": {
      translation: {
        // ... existing keys
        welcomeMessage: "Bem-vindo ao aplicativo!",
        settings: "Configurações",
        save: "Salvar",
        cancel: "Cancelar",
      },
    },
  },
});

Use the New Keys

Access the new translations in your components:

import { useTranslation } from "react-i18next";
import { Button } from "@/components/ui/button";

export default function SettingsPage() {
  const { t } = useTranslation();

  return (
    <div>
      <h1>{t("settings")}</h1>
      <p>{t("welcomeMessage")}</p>
      <div className="flex gap-2">
        <Button variant="outline">{t("cancel")}</Button>
        <Button>{t("save")}</Button>
      </div>
    </div>
  );
}

Always add new translation keys to all language resources. If a key is missing, i18next will fall back to the fallbackLng (English by default).

Language Actions

setAppLanguage

Changes the current language and persists the selection:

import { setAppLanguage } from "@/actions/language";
import { useTranslation } from "react-i18next";

function MyComponent() {
  const { i18n } = useTranslation();

  const changeToSpanish = () => {
    setAppLanguage("es", i18n);
  };

  return <button onClick={changeToSpanish}>Switch to Spanish</button>;
}

This function:

  1. Saves the language to localStorage
  2. Changes the i18next language
  3. Updates the lang attribute on the HTML document

updateAppLanguage

Restores the language from localStorage on app startup. This is called automatically in App.tsx:

import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { updateAppLanguage } from "./actions/language";

export default function App() {
  const { i18n } = useTranslation();

  useEffect(() => {
    updateAppLanguage(i18n);
  }, [i18n]);

  // ...
}

Language Toggle Component

electron-shadcn includes a LangToggle component that displays all available languages:

import LangToggle from "@/components/lang-toggle";

export default function Header() {
  return (
    <header>
      <LangToggle />
    </header>
  );
}

The component automatically:

  • Reads available languages from langs.ts
  • Highlights the currently selected language
  • Persists language changes to localStorage

Customizing the Toggle

You can create a custom language selector using the same building blocks:

import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { useTranslation } from "react-i18next";
import langs from "@/localization/langs";
import { setAppLanguage } from "@/actions/language";

export default function CustomLangSelector() {
  const { i18n } = useTranslation();

  return (
    <Select
      value={i18n.language}
      onValueChange={(value) => setAppLanguage(value, i18n)}
    >
      <SelectTrigger className="w-[180px]">
        <SelectValue placeholder="Select language" />
      </SelectTrigger>
      <SelectContent>
        {langs.map((lang) => (
          <SelectItem key={lang.key} value={lang.key}>
            {lang.nativeName}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  );
}

Advanced Usage

Interpolation

Insert dynamic values into translations:

// In i18n.ts
en: {
  translation: {
    greeting: "Hello, {{name}}!",
    itemCount: "You have {{count}} items",
  },
}
// In your component
const { t } = useTranslation();

t("greeting", { name: "John" }); // "Hello, John!"
t("itemCount", { count: 5 });    // "You have 5 items"

Pluralization

Handle singular and plural forms:

// In i18n.ts
en: {
  translation: {
    item_one: "{{count}} item",
    item_other: "{{count}} items",
  },
},
"pt-BR": {
  translation: {
    item_one: "{{count}} item",
    item_other: "{{count}} itens",
  },
}
t("item", { count: 1 }); // "1 item"
t("item", { count: 5 }); // "5 items"

Nested Translations

Organize translations with nested objects:

// In i18n.ts
en: {
  translation: {
    nav: {
      home: "Home",
      settings: "Settings",
      about: "About",
    },
    errors: {
      notFound: "Page not found",
      serverError: "Server error occurred",
    },
  },
}
t("nav.home");         // "Home"
t("errors.notFound");  // "Page not found"

Toggle language component

electron-shadcn already includes a language toggle component that displays all available languages and allows users to switch between them easily.

LangToggle Component

Check out the documentation for the built-in language toggle component

Best Practices

  • Use descriptive keys: Prefer nav.homeButton over btn1
  • Keep translations organized: Group related keys together using nested objects
  • Don't hardcode text: Always use the t() function for user-facing text
  • Test all languages: Verify translations don't break layouts (some languages are longer)
  • Document context: Add comments for translators if a key's context isn't obvious
  • Handle missing translations: Set up a fallback language and monitor for missing keys

Removing a Language

To remove a language from your application:

  1. Remove the language object from src/localization/i18n.ts resources
  2. Remove the language entry from src/localization/langs.ts

The language toggle will automatically update to show only the remaining languages.

On this page