From 00c66eca472748c2ad1e8bfa745d4cad1f9a0f12 Mon Sep 17 00:00:00 2001
From: DS Boyce <260543580+ds-boyce@users.noreply.github.com>
Date: Tue, 3 Mar 2026 12:36:22 -0800
Subject: [PATCH] Pass key as argument to useTranslate hook
---
src/components/Post/Translated/index.tsx | 17 +++----
.../PostControls/PostMenu/PostMenuItems.tsx | 13 +++---
src/lib/translation/context.ts | 9 ++++
src/lib/translation/index.tsx | 44 ++++++++++++-------
src/lib/translation/index.web.tsx | 42 ++++++++++++++++--
5 files changed, 90 insertions(+), 35 deletions(-)
diff --git a/src/components/Post/Translated/index.tsx b/src/components/Post/Translated/index.tsx
index 66489f47db..2b6c199a41 100644
--- a/src/components/Post/Translated/index.tsx
+++ b/src/components/Post/Translated/index.tsx
@@ -3,7 +3,7 @@ import {Platform, View} from 'react-native'
import {Trans, useLingui} from '@lingui/react/macro'
import {HITSLOP_30} from '#/lib/constants'
-import {useTranslate, useTranslationKey} from '#/lib/translation'
+import {useTranslate} from '#/lib/translation'
import {codeToLanguageName, languageName} from '#/locale/helpers'
import {LANGUAGES} from '#/locale/languages'
import {useLanguagePrefs} from '#/state/preferences'
@@ -21,21 +21,19 @@ export function TranslatedPost({
translationKey: string
postText: string
}) {
- const {translationState} = useTranslate()
- // Register this component as using this translation key with focus-based cleanup
- useTranslationKey(translationKey)
+ const {translationState} = useTranslate({key: translationKey})
- if (translationState[translationKey]?.status === 'loading') {
+ if (translationState.status === 'loading') {
return
}
- if (translationState[translationKey]?.status === 'success') {
+ if (translationState.status === 'success') {
return (
)
}
@@ -119,7 +117,7 @@ function TranslationLanguageSelect({
const ax = useAnalytics()
const {t: l} = useLingui()
const langPrefs = useLanguagePrefs()
- const {translate} = useTranslate()
+ const {translate} = useTranslate({key: translationKey})
const items = useMemo(
() =>
@@ -149,7 +147,6 @@ function TranslationLanguageSelect({
targetLanguage: langPrefs.primaryLanguage,
})
void translate({
- key: translationKey,
text: postText,
targetLangCode: langPrefs.primaryLanguage,
sourceLangCode,
diff --git a/src/components/PostControls/PostMenu/PostMenuItems.tsx b/src/components/PostControls/PostMenu/PostMenuItems.tsx
index 58b51bfa4c..ece506feba 100644
--- a/src/components/PostControls/PostMenu/PostMenuItems.tsx
+++ b/src/components/PostControls/PostMenu/PostMenuItems.tsx
@@ -135,7 +135,9 @@ let PostMenuItems = ({
const {hidePost} = useHiddenPostsApi()
const feedFeedback = useFeedFeedbackContext()
const openLink = useOpenLink()
- const {clearTranslation, translate, translationState} = useTranslate()
+ const {clearTranslation, translate, translationState} = useTranslate({
+ key: post.uri,
+ })
const navigation = useNavigation()
const {mutedWordsDialogControl} = useGlobalDialogsControlContext()
const blockPromptControl = useDialogControl()
@@ -190,8 +192,6 @@ let PostMenuItems = ({
return makeProfileLink(postAuthor, 'post', urip.rkey)
}, [postUri, postAuthor])
- const translationKey = post.uri
-
const onDeletePost = () => {
deletePostMutate({uri: postUri}).then(
() => {
@@ -259,7 +259,6 @@ let PostMenuItems = ({
const onPressTranslate = () => {
void translate({
- key: translationKey,
text: record.text,
targetLangCode: langPrefs.primaryLanguage,
forceGoogleTranslate,
@@ -465,7 +464,7 @@ let PostMenuItems = ({
const onSignIn = () => requireSignIn(() => {})
- const onPressHideTranslation = () => clearTranslation(translationKey)
+ const onPressHideTranslation = () => clearTranslation()
const isDiscoverDebugUser =
IS_INTERNAL ||
@@ -501,7 +500,7 @@ let PostMenuItems = ({
{!hideInPWI || hasSession ? (
<>
- {translationState[translationKey]?.status === 'loading' ? (
+ {translationState.status === 'loading' ? (
{l`Translating…`}
- ) : translationState[translationKey]?.status === 'success' ? (
+ ) : translationState.status === 'success' ? (
translate: (parameters: {
key: string
+ /**
+ * The text to be translated.
+ */
text: string
+ /**
+ * The language to translate the text into.
+ */
targetLangCode: string
+ /**
+ * The source language of the text. Will auto-detect if not provided.
+ */
sourceLangCode?: string
/**
* Whether to force the use of Google Translate. Default is false.
diff --git a/src/lib/translation/index.tsx b/src/lib/translation/index.tsx
index 9ebee4d49e..7a16aea04e 100644
--- a/src/lib/translation/index.tsx
+++ b/src/lib/translation/index.tsx
@@ -77,31 +77,46 @@ async function attemptTranslation(
*
* Web uses index.web.ts which always opens Google Translate.
*/
-export function useTranslate() {
+export function useTranslate({key}: {key: string}) {
const context = useContext(Context)
if (!context) {
throw new Error(
'useTranslate must be used within a TranslateOnDeviceProvider',
)
}
- return context
-}
-
-/**
- * Hook to register a component as using a translation key.
- * Automatically handles ref counting with screen focus management
- * via useFocusEffect and cleans up the translation when the component
- * loses focus.
- */
-export function useTranslationKey(key: string) {
- const {acquireTranslation} = useTranslate()
useFocusEffect(
useCallback(() => {
- const cleanup = acquireTranslation(key)
+ if (!key) return
+ const cleanup = context.acquireTranslation(key)
return cleanup
- }, [key, acquireTranslation]),
+ }, [key, context]),
)
+
+ const translate = useCallback(
+ async (params: {
+ text: string
+ targetLangCode: string
+ sourceLangCode?: string
+ forceGoogleTranslate?: boolean
+ }) => {
+ return context.translate({...params, key})
+ },
+ [key, context],
+ )
+
+ const clearTranslation = useCallback(
+ () => context.clearTranslation(key),
+ [key, context],
+ )
+
+ return {
+ translationState: context.translationState[key] ?? {
+ status: 'idle',
+ },
+ translate,
+ clearTranslation,
+ }
}
export function Provider({children}: React.PropsWithChildren) {
@@ -184,7 +199,6 @@ export function Provider({children}: React.PropsWithChildren) {
await googleTranslate(text, targetLangCode, sourceLangCode)
return
}
- LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
setTranslationState(prev => ({
...prev,
[key]: {status: 'loading'},
diff --git a/src/lib/translation/index.web.tsx b/src/lib/translation/index.web.tsx
index 3ed4811392..fbcb4e9f55 100644
--- a/src/lib/translation/index.web.tsx
+++ b/src/lib/translation/index.web.tsx
@@ -11,18 +11,54 @@ const acquireTranslation = (_key: string) => {
}
const clearTranslation = (_key: string) => {}
-export function useTranslationKey(_key: string) {}
-
/**
* Web always opens Google Translate.
*/
-export function useTranslate() {
+export function useTranslate(key: string) {
const context = useContext(Context)
if (!context) {
throw new Error(
'useTranslate must be used within a TranslateOnDeviceProvider',
)
}
+
+ // Always call hooks in consistent order
+ const translate = useCallback(
+ async (params: {
+ text: string
+ targetLangCode: string
+ sourceLangCode?: string
+ }) => {
+ if (!key) {
+ throw new Error(
+ 'translate requires a key. Either pass key to useTranslate() or use context.translate() with key parameter',
+ )
+ }
+ return context.translate({...params, key})
+ },
+ [key, context],
+ )
+
+ const clearTranslation = useCallback(() => {
+ if (!key) {
+ throw new Error(
+ 'clearTranslation requires a key. Either pass key to useTranslate() or use context.clearTranslation() with key parameter',
+ )
+ }
+ return context.clearTranslation(key)
+ }, [key, context])
+
+ // If a key is provided, return wrapped versions that automatically use the key
+ if (key) {
+ return {
+ translationState: context.translationState[key] ?? {
+ status: 'idle' as const,
+ },
+ translate,
+ clearTranslation,
+ }
+ }
+
return context
}