mirror of
https://github.com/lfnovo/open-notebook.git
synced 2026-04-28 03:19:59 +00:00
fix: use getApiErrorMessage in use-settings and update CLAUDE.md docs
- Replace getApiErrorKey with getApiErrorMessage in use-settings.ts
so error toasts show translated messages instead of raw i18n keys
- Update CLAUDE.md files to reflect the new t('section.key') pattern
and remove outdated Proxy-related gotchas
This commit is contained in:
parent
98a528158a
commit
e2cf35060b
4 changed files with 31 additions and 23 deletions
|
|
@ -64,8 +64,8 @@ User interactions trigger mutations/queries via hooks, which communicate with th
|
|||
#### `lib/locales/` — Internationalization (i18n)
|
||||
- **Locale files** (`en-US/`, `pt-BR/`, `zh-CN/`, `zh-TW/`, `ja-JP/`): Translation strings organized by feature
|
||||
- **`i18n.ts`**: i18next configuration with language detection
|
||||
- **`use-translation.ts`**: Custom hook with Proxy-based `t.section.key` access pattern
|
||||
- **Pattern**: Components call `useTranslation()` hook; access strings via `t.common.save`, `t.notebooks.title`
|
||||
- **`use-translation.ts`**: Thin wrapper around react-i18next's `useTranslation` with language change events
|
||||
- **Pattern**: Components call `useTranslation()` hook; access strings via `t('common.save')`, `t('notebooks.title')`
|
||||
|
||||
## Data & Control Flow Walkthrough
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ React hooks for API data fetching, state management, and complex workflows (chat
|
|||
- **Streaming hooks** (`useAsk`): SSE parsing for multi-stage Ask workflows (strategy → answers → final answer)
|
||||
- **Model/config hooks** (`useModels`, `useSettings`, `useTransformations`): Application-level settings and model management
|
||||
- **Utility hooks** (`useMediaQuery`, `useToast`, `useNavigation`, `useAuth`): UI state and auth checking
|
||||
- **i18n hook** (`useTranslation`): Proxy-based translation access with `t.section.key` pattern and language switching
|
||||
- **i18n hook** (`useTranslation`): Thin wrapper around react-i18next with `t('section.key')` pattern and language switching
|
||||
|
||||
## Important Patterns
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ React hooks for API data fetching, state management, and complex workflows (chat
|
|||
- **SSE streaming pattern**: `useAsk` manually parses newline-delimited JSON from `/api/search/ask`; handles incomplete buffers
|
||||
- **Status polling**: `useSourceStatus` auto-refetches every 2s while `status === 'running' | 'queued' | 'new'`
|
||||
- **Context building**: `useNotebookChat.buildContext()` assembles selected sources + notes with token/char counts
|
||||
- **i18n Proxy pattern**: `useTranslation` returns `t` object with Proxy; access `t.section.key` instead of `t('section.key')`
|
||||
- **i18n pattern**: `useTranslation` returns standard react-i18next `t` function; access translations via `t('section.key')`
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
|
|
@ -49,8 +49,7 @@ React hooks for API data fetching, state management, and complex workflows (chat
|
|||
- **Status polling race**: `useSourceStatus` may refetch stale data before server catches up; retry logic has 3-attempt limit
|
||||
- **Keyboard trap in dialogs**: Some hooks manage modal state; ensure Dialog/Modal components handle escape key properly
|
||||
- **Form data handling**: `useFileUpload` and source creation convert JSON fields to strings in FormData
|
||||
- **useTranslation depth limit**: Proxy limits nesting to 4 levels; deeper access returns path string as fallback
|
||||
- **useTranslation loop detection**: >1000 accesses to same key in 1s triggers error and breaks recursion
|
||||
- **useTranslation**: Thin wrapper preserving `setLanguage` with language change events for `LanguageLoadingOverlay`
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
|
|
@ -187,7 +186,7 @@ function CredentialSettings() {
|
|||
### Important Notes
|
||||
|
||||
- **Toast notifications**: All mutations show success/error toasts automatically
|
||||
- **i18n integration**: Toast messages use translation keys from `t.apiKeys.*` and `t.common.*`
|
||||
- **i18n integration**: Toast messages use translation keys from `t('apiKeys.*')` and `t('common.*')`
|
||||
- **Error handling**: Uses `getApiErrorKey()` utility to extract error messages from API responses
|
||||
- **Local test results**: `useTestCredential` stores results in local state (not cached in TanStack Query)
|
||||
- **Migration feedback**: Migration hooks show different toasts based on migrated/skipped/error counts
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { settingsApi } from '@/lib/api/settings'
|
|||
import { QUERY_KEYS } from '@/lib/api/query-client'
|
||||
import { useToast } from '@/lib/hooks/use-toast'
|
||||
import { useTranslation } from '@/lib/hooks/use-translation'
|
||||
import { getApiErrorKey } from '@/lib/utils/error-handler'
|
||||
import { getApiErrorMessage } from '@/lib/utils/error-handler'
|
||||
import { SettingsResponse } from '@/lib/types/api'
|
||||
|
||||
export function useSettings() {
|
||||
|
|
@ -30,7 +30,7 @@ export function useUpdateSettings() {
|
|||
onError: (error: unknown) => {
|
||||
toast({
|
||||
title: t('common.error'),
|
||||
description: getApiErrorKey(error, t('common.error')),
|
||||
description: getApiErrorMessage(error, (key) => t(key), 'common.error'),
|
||||
variant: 'destructive',
|
||||
})
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Locales Module (i18n)
|
||||
|
||||
Internationalization system providing multi-language UI support using i18next with type-safe translation access.
|
||||
Internationalization system providing multi-language UI support using i18next with standard `t()` function calls.
|
||||
|
||||
## Architecture
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ lib/
|
|||
├── i18n.ts # i18next initialization and configuration
|
||||
├── i18n-events.ts # Language change event emitters
|
||||
├── hooks/
|
||||
│ └── use-translation.ts # Custom hook with Proxy-based API
|
||||
│ └── use-translation.ts # Thin wrapper around react-i18next with language change events
|
||||
├── utils/
|
||||
│ └── date-locale.ts # date-fns locale mapping
|
||||
└── locales/
|
||||
|
|
@ -28,7 +28,7 @@ lib/
|
|||
- **`i18n.ts`**: i18next initialization with language detection (localStorage → browser)
|
||||
- **`i18n-events.ts`**: Event emitters for language change start/end (used by loading overlay)
|
||||
- **`locales/index.ts`**: Central registry exporting all locales and `LanguageCode` type
|
||||
- **`use-translation.ts`**: Custom hook providing `t` object with nested property access
|
||||
- **`use-translation.ts`**: Thin wrapper around react-i18next returning `{ t, i18n, language, setLanguage }`
|
||||
|
||||
## Translation Structure
|
||||
|
||||
|
|
@ -67,24 +67,36 @@ import { useTranslation } from '@/lib/hooks/use-translation'
|
|||
function MyComponent() {
|
||||
const { t, language, setLanguage } = useTranslation()
|
||||
|
||||
// Nested property access (Proxy-based)
|
||||
return <h1>{t.notebooks.title}</h1>
|
||||
// Standard t() function call
|
||||
return <h1>{t('notebooks.title')}</h1>
|
||||
|
||||
// With interpolation
|
||||
return <p>{t.common.updated.replace('{time}', timeAgo)}</p>
|
||||
// With string interpolation
|
||||
return <p>{t('common.updated').replace('{time}', timeAgo)}</p>
|
||||
|
||||
// Change language
|
||||
await setLanguage('zh-CN')
|
||||
}
|
||||
```
|
||||
|
||||
### Functions that accept t as a parameter
|
||||
|
||||
Use `TFunction` from i18next:
|
||||
|
||||
```typescript
|
||||
import type { TFunction } from 'i18next'
|
||||
|
||||
const getNavigation = (t: TFunction) => [
|
||||
{ name: t('navigation.sources'), href: '/sources' },
|
||||
]
|
||||
```
|
||||
|
||||
## Important Patterns
|
||||
|
||||
- **Proxy-based access**: `t.section.key` instead of `t('section.key')` for better DX
|
||||
- **Type safety**: `TranslationKeys` type derived from `enUS` locale
|
||||
- **Standard t() calls**: `t('section.key')` — standard react-i18next pattern
|
||||
- **Language persistence**: Saved to localStorage, auto-detected on load
|
||||
- **Fallback**: Falls back to `en-US` if key missing in current locale
|
||||
- **Date localization**: Use `getDateLocale(language)` from `utils/date-locale.ts`
|
||||
- **Language change events**: `setLanguage` emits start/end events for `LanguageLoadingOverlay`
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
|
|
@ -117,13 +129,10 @@ function MyComponent() {
|
|||
|
||||
## Important Quirks & Gotchas
|
||||
|
||||
- **Proxy depth limit**: `useTranslation` limits nesting to 4 levels to prevent infinite loops
|
||||
- **Blocked properties**: React internals (`__proto__`, `$$typeof`, etc.) are blocked from Proxy traversal
|
||||
- **Loop detection**: Access counts reset every 1s; >1000 accesses triggers error and breaks recursion
|
||||
- **String methods**: `.replace()`, `.split()` work on translated strings via Proxy magic
|
||||
- **Language change events**: `emitLanguageChangeStart/End` used by `LanguageLoadingOverlay` for UX
|
||||
- **No SSR**: `useSuspense: false` disables React Suspense for i18next (avoids hydration issues)
|
||||
- **All keys required**: Missing keys in non-English locales fall back to English; keep locales in sync
|
||||
- **ErrorBoundary**: Uses raw `enUS` locale object directly (class component, can't use hooks)
|
||||
|
||||
## Testing Patterns
|
||||
|
||||
|
|
@ -131,7 +140,7 @@ function MyComponent() {
|
|||
// Mock useTranslation in tests (see test/setup.ts)
|
||||
vi.mock('@/lib/hooks/use-translation', () => ({
|
||||
useTranslation: () => ({
|
||||
t: enUS, // Use English locale directly
|
||||
t: (key: string) => key, // Identity function returns the key
|
||||
language: 'en-US',
|
||||
setLanguage: vi.fn(),
|
||||
}),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue