Each wrapper built a fresh closure every render, so `t` in a dependency array recomputed forever. That is the documented useClient()/Jellyfin trap in platform/CLAUDE.md, and phase 2 needs t in useMemo deps immediately. useTranslation in officer-web now also returns tUi, typed against the shared components' catalog. 'Cancel' and 'Close' recur across the shell and already exist in ui.* — the alternative was importing two hooks in a hundred files.
17 lines
880 B
TypeScript
17 lines
880 B
TypeScript
import type { InterpolationParams, TranslationKeys } from 'i18n';
|
|
import { useCallback } from 'react';
|
|
import { useTranslation as useTranslationBase } from 'i18n';
|
|
import type en from './en-GB.json';
|
|
|
|
type UiTranslationKey = TranslationKeys<typeof en>;
|
|
|
|
// The `ui.` prefix is namespace wiring for the merged catalog (D6) — callers never spell it, they
|
|
// pass the same bare key the JSON file uses. `ui.*` may not name a feature (see the naming convention).
|
|
export const useTranslation = () => {
|
|
const { t: baseT, locale } = useTranslationBase();
|
|
// Memoized: an unstable `t` is unusable in a dependency array. See platform/CLAUDE.md on the
|
|
// `useClient()` trap that silently disabled the Jellyfin player's playback reports.
|
|
const t = useCallback((key: UiTranslationKey, params?: InterpolationParams) => baseT(`ui.${key}`, params), [baseT]);
|
|
return { t, locale };
|
|
};
|