Skip to content
Huang Xin edited this page Feb 22, 2025 · 2 revisions

Overview

Readest uses the key-as-content approach for internationalization (i18n), where English strings are used as translation keys. This ensures straightforward management of translations across the app. Strings for translations are marked with the _ shorthand token of useTranslation hook in React components and stub translation function in non-React modules.

Setting Up Translations in React Components

To translate strings within React components, import and use the useTranslation hook. This provides a shorthand function for translating strings.

--- a/apps/readest-app/src/app/reader/hooks/useProgressSync.ts
+++ b/apps/readest-app/src/app/reader/hooks/useProgressSync.ts
@@ -5,6 +5,7 @@ import { BookConfig } from '@/types/book';
 import { useBookDataStore } from '@/store/bookDataStore';
 import { useReaderStore } from '@/store/readerStore';
 import { useSettingsStore } from '@/store/settingsStore';
+import { useTranslation } from '@/hooks/useTranslation';
 import { deserializeConfig, serializeConfig } from '@/utils/serializer';
 import { DEFAULT_BOOK_SEARCH_CONFIG, SYNC_PROGRESS_INTERVAL_SEC } from '@/services/constants';

@@ -12,6 +13,7 @@ export const useProgressSync = (
   bookKey: string,
   setToastMessage?: React.Dispatch<React.SetStateAction<string>>,
 ) => {
+  const _ = useTranslation();
   const { getConfig, setConfig } = useBookDataStore();
   const { getView } = useReaderStore();
   const { settings } = useSettingsStore();
@@ -81,7 +83,7 @@ export const useProgressSync = (
           const configFraction = config!.progress![0] / config!.progress![1];
           if (syncedFraction > configFraction) {
             view?.goToFraction(syncedFraction);
-            setToastMessage?.('Progress synced');
+            setToastMessage?.(_('Progress synced'));
           }
         }
       }

Translating Strings in Non-React Modules

For non-React modules, use a stub translation function (stubTranslation) during string declaration. Then, use the real translation function provided by the useTranslation hook in the React component where the string is utilized.

Declaring Localized Strings

Define strings with stubTranslation shorthand _ in a utility module:

import { stubTranslation as _ } from '@/utils/misc';

export const FILE_REVEAL_LABELS = {
  macos: _('Reveal in Finder'),
  windows: _('Reveal in File Explorer'),
  linux: _('Reveal in Folder'),
  default: _('Reveal in Folder'),
};

Using Localized Strings in Components

Use the real translation function from the useTranslation hook in the relevant React component:

    const showBookInFinderMenuItem = await MenuItem.new({
      text: _(fileRevealLabel),
      action: async () => {
        const folder = `${settings.localBooksDir}/${getFilename(book)}`;
        revealItemInDir(folder);
      },
    });

Managing Translations

Extracting New Strings

Run the following command to automatically extract new strings for translation:

pnpm i18n:extract

This command scans the codebase for untranslated strings and adds them to the public/locales directory. Newly extracted strings are assigned the value __STRING_NOT_TRANSLATED__.

Translation Files

  • The English locale has an empty translation file because the key-as-content approach is used. In this case, the key itself serves as the translation.

  • Other locales include translations in their respective files.

Notes

  1. Stub Translation: Use stubTranslation only for non-React modules.

  2. Default to English: When no translation is found, the key (English string) will be used as the fallback.

  3. Local Directory: Translation files are stored in public/locales. Ensure this directory is kept in sync when adding or modifying translations.

  4. Only translation keys in the format _('KEY', options?={}) are recognized. For more details, refer to the config file for i18next-scanner.