ryOS ryOS / Docs
GitHub Launch

i18n & Hooks

Internationalization and 43+ custom hooks.

Supported Languages

CodeLanguageNative Name
enEnglish (default)English
zh-TWChinese Traditional中文
jaJapanese日本語
koKorean한국어
frFrenchFrançais
deGermanDeutsch
esSpanishEspañol
ptPortuguesePortuguês
itItalianItaliano
ruRussianРусский

i18n Configuration

The i18n system is configured in src/lib/i18n.ts and src/lib/languageConfig.ts:

  • Library: i18next with react-i18next bindings
  • Detection: Custom locale resolution via resolveInitialLanguage() (uses navigator.languages with fuzzy locale matching)
  • Storage: Language preference saved to localStorage (ryos:language, ryos:language-initialized) with legacy key migration from ryos_language
  • Fallback: English (en) as default fallback language
  • Runtime switching: applyLanguage() ensures resources are loaded before i18n.changeLanguage()

Locale Bundle Loading

  • Eager default locale: English (en) translation bundle is imported at startup
  • Lazy non-default locales: Other locale JSON files are dynamically imported on demand (localeLoaders)
  • Deduplicated loading: Concurrent requests for the same locale share a single promise (loadingLanguages)
  • Bootstrap flow: initializeI18n() initializes i18n, resolves initial language, lazy-loads if needed, then applies language

Translation File Structure

Translations are stored in src/lib/locales/{lang}/translation.json:

src/lib/locales/
├── en/translation.json
├── zh-TW/translation.json
├── ja/translation.json
├── ko/translation.json
├── fr/translation.json
├── de/translation.json
├── es/translation.json
├── pt/translation.json
├── it/translation.json
└── ru/translation.json

Translation Key Patterns

PatternDescriptionExample
common.Shared UI elements (menus, dialogs, buttons)common.menu.file
apps.{appId}.App-specific translationsapps.finder.name
apps.{appId}.menu.App menu itemsapps.ipod.menu.addSong
apps.{appId}.help.App help contentapps.chats.help.chatWithRyo
apps.{appId}.dialogs.App dialog textapps.videos.dialogs.clearPlaylistTitle
apps.{appId}.widgets.Widget translations (Dashboard)apps.dashboard.widgets.clock
apps.calendar.Calendar views, events, sidebarapps.calendar.views.month
apps.contacts.Address book, groups, fieldsapps.contacts.groups.all
apps.dashboard.Dashboard widgets, dictionary, stocksapps.dashboard.stocks.loading
settings.Settings panel translationssettings.language.title
components.Shared component translationscomponents.linkPreview.openIpod
spotlight.Spotlight Search UI and sectionsspotlight.placeholder, spotlight.sections.apps

Key Hooks

Window & App Management:
HookPurpose
useLaunchAppLaunch apps with multi-window support
useWindowManagerDrag, resize, snap-to-edge
useWindowInsetsTheme-dependent constraints
Audio System:
HookPurpose
useSoundWeb Audio API playback
useChatSynthChat typing sounds
useTerminalSoundsTerminal feedback
useTtsQueueText-to-speech queue
useAudioRecorderAudio recording
Device Detection:
HookPurpose
useIsMobileMobile detection (<768px)
useIsPhonePhone detection (<640px)
useMediaQueryCSS media query hook

Using i18n

import { useTranslation } from "react-i18next";

const { t } = useTranslation();

// Simple key lookup
t("common.menu.file")              // "File"
t("apps.finder.name")              // "Finder"

// With interpolation
t("common.dialog.aboutApp", { appName: "Finder" })  // "About Finder"
t("apps.ipod.dialogs.addedSongsToTop", { count: 5, plural: "s" })

// Pluralization
t("apps.admin.statusBar.usersCount", { count: 1 })  // "1 user"
t("apps.admin.statusBar.usersCount_plural", { count: 5 })  // "5 users"

Helper Functions (src/utils/i18n.ts)

FunctionPurpose
getTranslatedAppName(appId)Get localized app name (theme-aware)
getTranslatedAppDescription(appId)Get localized app description
getTranslatedFolderName(path)Get localized folder name from path
getTranslatedHelpItems(appId)Get localized help items for an app