Split useTranslation into state + actions via useBetween singleton

Same pattern as the wired-tools split: 600-line useTranslation backs
6 consumers with a wide state + action surface. Split along the
read/write seam:

- useTranslationStore (internal, was the inner useTranslationState) —
  the previous singleton body, untouched except for the rename and a
  doc-comment.
- useTranslationState (public, read-only) — useBetween filter exposing
  settings, the supported-languages list, the loading/loaded flags,
  the detected-language tags, lastError, and the pure getLanguageName
  helper.
- useTranslationActions (public, imperative) — same singleton filter
  exposing updateSettings, ensureSupportedLanguagesLoaded, the four
  translate/queue helpers. Also re-exposes 'settings' because most
  call sites need 'if(settings.enabled)' before dispatching.
- useTranslation (deprecated shim) — composes the singleton via
  useBetween, preserving the historical full-shape return.

applyTextTranslationLocale stays exported from the same module path
so LoginView's import keeps working.

Updates docs/ARCHITECTURE.md proposal #4 section to list the three
new splits (chat-input + wired-tools + translation) alongside the
previous five.
This commit is contained in:
simoleo89
2026-05-11 22:05:51 +02:00
parent e1f5df6b1c
commit eeb9cc66a5
2 changed files with 105 additions and 2 deletions
+87 -2
View File
@@ -228,7 +228,17 @@ const resolveSupportedLanguage = (value: string, languages: ITranslationLanguage
return languages[0].code;
};
const useTranslationState = () =>
/**
* Internal singleton state + actions hook. Public consumers should
* call useTranslationState (read-only) or useTranslationActions
* (imperative) instead. useTranslation is the deprecated shim that
* composes both.
*
* Wrapped in useBetween at each public-hook layer so every consumer
* in the tree sees the same instance (preserves the previous
* useBetween(useTranslationState) behavior).
*/
const useTranslationStore = () =>
{
const defaultTargetLanguage = getBrowserLanguageCode();
const [ settings, setSettings ] = useLocalStorage<ITranslationSettings>(LocalStorageKeys.CHAT_TRANSLATION_SETTINGS, {
@@ -597,4 +607,79 @@ const useTranslationState = () =>
};
};
export const useTranslation = () => useBetween(useTranslationState);
/**
* Read-only slice of the translation store: persisted settings, the
* supported languages list, the loading/loaded flags, the last
* incoming/outgoing detected language tags, and the last error message
* surfaced to the UI.
*
* Components that only render translation state subscribe here.
*/
export const useTranslationState = () =>
{
const {
settings,
supportedLanguages,
availableTextLocales,
languagesLoading,
languagesLoaded,
localizationTextsLoading,
lastIncomingLanguage,
lastOutgoingLanguage,
lastError,
getLanguageName
} = useBetween(useTranslationStore);
return {
settings,
supportedLanguages,
availableTextLocales,
languagesLoading,
languagesLoaded,
localizationTextsLoading,
lastIncomingLanguage,
lastOutgoingLanguage,
lastError,
getLanguageName
};
};
/**
* Imperative slice of the translation store: settings mutation,
* supported-languages refresh, the translate* helpers, and the
* outgoing-queue write/read pair. Stays separate so components that
* only invoke actions (e.g. ChatInputActions) don't pull the full
* state shape.
*/
export const useTranslationActions = () =>
{
const {
settings,
updateSettings,
ensureSupportedLanguagesLoaded,
translateIncoming,
translateOutgoing,
enqueueOutgoingTranslation,
consumeOutgoingTranslation
} = useBetween(useTranslationStore);
return {
// settings is exposed here too because most action call sites
// need `if(settings.enabled)` checks before dispatching.
settings,
updateSettings,
ensureSupportedLanguagesLoaded,
translateIncoming,
translateOutgoing,
enqueueOutgoingTranslation,
consumeOutgoingTranslation
};
};
/**
* @deprecated Prefer `useTranslationState` (read-only) and
* `useTranslationActions` (imperative) directly. This shim composes
* both into the historical `useTranslation()` shape so the six
* existing consumers keep working unchanged.
*/
export const useTranslation = () => useBetween(useTranslationStore);