memoize t in the typed wrappers, and give app code typed access to ui.*

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.
This commit is contained in:
2026-08-23 04:30:07 +00:00
parent 99a78f3c7f
commit 0ddcdcbdbf
3 changed files with 33 additions and 6 deletions
@@ -1,4 +1,5 @@
import type { InterpolationParams, TranslationKeys } from 'i18n';
import { useCallback } from 'react';
import { useTranslation as useTranslationBase } from 'i18n';
import type en from './en-GB.json';
@@ -8,6 +9,8 @@ type UiTranslationKey = TranslationKeys<typeof en>;
// 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();
const t = (key: UiTranslationKey, params?: InterpolationParams) => baseT(`ui.${key}`, params);
// 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 };
};
@@ -1,4 +1,5 @@
import type { InterpolationParams, TranslationKeys } from 'i18n';
import { useCallback } from 'react';
import { useTranslation as useTranslationBase } from 'i18n';
import type en from './en-GB.json';
@@ -8,6 +9,11 @@ type PanelTranslationKey = TranslationKeys<typeof en>;
// pass the same bare key the JSON file uses.
export const useTranslation = () => {
const { t: baseT, locale } = useTranslationBase();
const t = (key: PanelTranslationKey, params?: InterpolationParams) => baseT(`panel.${key}`, params);
// 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: PanelTranslationKey, params?: InterpolationParams) => baseT(`panel.${key}`, params),
[baseT],
);
return { t, locale };
};