diff --git a/src/locale/helpers.ts b/src/locale/helpers.ts --- a/src/locale/helpers.ts +++ b/src/locale/helpers.ts @@ -127,8 +127,15 @@ return bcp47Match.basicFilter(lang, targetLangs).length > 0 } +// we cant hook into functions like this, so we will make other translator functions n swap between em export function getTranslatorLink(text: string, lang: string): string { return `https://translate.google.com/?sl=auto&tl=${lang}&text=${encodeURIComponent( + text, + )}` +} + +export function getTranslatorLinkKagi(text: string, lang: string): string { + return `https://translate.kagi.com/?from=auto&to=${lang}&text=${encodeURIComponent( text, )}` } diff --git a/src/lib/hooks/useTranslate.ts b/src/lib/hooks/useTranslate.ts --- a/src/lib/hooks/useTranslate.ts +++ b/src/lib/hooks/useTranslate.ts @@ -1,17 +1,29 @@ import {useCallback} from 'react' import * as IntentLauncher from 'expo-intent-launcher' -import {getTranslatorLink} from '#/locale/helpers' +import {getTranslatorLink, getTranslatorLinkKagi} from '#/locale/helpers' +import {useTranslationServicePreference} from '#/state/preferences/translation-service-preference' import {IS_ANDROID} from '#/env' import {useOpenLink} from './useOpenLink' export function useTranslate() { const openLink = useOpenLink() + const translationServicePreference = useTranslationServicePreference() + return useCallback( async (text: string, language: string) => { - const translateUrl = getTranslatorLink(text, language) - if (IS_ANDROID) { + let translateUrl + + // if ur curious why this isnt a switch case, good question, for some reason making this a switch case breaks the functionality + // it is a mystery https://www.youtube.com/watch?v=fq3abPnEEGE + if (translationServicePreference == 'kagi') { + translateUrl = getTranslatorLinkKagi(text, language) + } else { + translateUrl = getTranslatorLink(text, language) + } + + if (IS_ANDROID && translationServicePreference == 'google') { try { // use getApplicationIconAsync to determine if the translate app is installed if ( @@ -49,6 +61,6 @@ await openLink(translateUrl) } }, - [openLink], + [openLink, translationServicePreference], ) } diff --git a/src/screens/Settings/DeerSettings.tsx b/src/screens/Settings/DeerSettings.tsx --- a/src/screens/Settings/DeerSettings.tsx +++ b/src/screens/Settings/DeerSettings.tsx @@ -103,6 +103,10 @@ useSetShowLinkInHandle, useShowLinkInHandle, } from '#/state/preferences/show-link-in-handle.tsx' +import { + useSetTranslationServicePreference, + useTranslationServicePreference, +} from '#/state/preferences/translation-service-preference' import {useProfilesQuery} from '#/state/queries/profile' import * as SettingsList from '#/screens/Settings/components/SettingsList' import {atoms as a, useBreakpoints} from '#/alf' @@ -113,6 +117,7 @@ import {Atom_Stroke2_Corner0_Rounded as DeerIcon} from '#/components/icons/Atom' import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink' import {Eye_Stroke2_Corner0_Rounded as VisibilityIcon} from '#/components/icons/Eye' +import {Earth_Stroke2_Corner2_Rounded as EarthIcon} from '#/components/icons/Globe' import {Lab_Stroke2_Corner0_Rounded as _BeakerIcon} from '#/components/icons/Lab' import {PaintRoller_Stroke2_Corner2_Rounded as PaintRollerIcon} from '#/components/icons/PaintRoller' import {RaisingHand4Finger_Stroke2_Corner0_Rounded as RaisingHandIcon} from '#/components/icons/RaisingHand' @@ -326,6 +331,9 @@ const showLinkInHandle = useShowLinkInHandle() const setShowLinkInHandle = useSetShowLinkInHandle() + + const translationServicePreference = useTranslationServicePreference() + const setTranslationServicePreference = useSetTranslationServicePreference() return ( @@ -614,6 +622,41 @@ access to features locked behind email verification. + + + + + + Translation Engine + + + + Choose the engine to use when translating posts. + + + setTranslationServicePreference('google')} + style={[a.w_full]}> + + Use Google Translate + + + + + setTranslationServicePreference('kagi')} + style={[a.w_full]}> + + Use Kagi Translate + + + diff --git a/src/state/persisted/schema.ts b/src/state/persisted/schema.ts --- a/src/state/persisted/schema.ts +++ b/src/state/persisted/schema.ts @@ -171,6 +171,8 @@ showExternalShareButtons: z.boolean().optional(), + translationServicePreference: z.enum(['google', 'kagi']), + /** @deprecated */ mutedThreads: z.array(z.string()), trendingDisabled: z.boolean().optional(), @@ -279,6 +281,7 @@ highQualityImages: false, hideUnreplyablePosts: false, showExternalShareButtons: false, + translationServicePreference: 'google', } export function tryParse(rawData: string): Schema | undefined { diff --git a/src/state/preferences/index.tsx b/src/state/preferences/index.tsx --- a/src/state/preferences/index.tsx +++ b/src/state/preferences/index.tsx @@ -37,6 +37,7 @@ import {Provider as RepostCarouselProvider} from './repost-carousel-enabled' import {Provider as ShowLinkInHandleProvider} from './show-link-in-handle' import {Provider as SubtitlesProvider} from './subtitles' +import {Provider as TranslationServicePreferenceProvider} from './translation-service-preference' import {Provider as TrendingSettingsProvider} from './trending' import {Provider as UsedStarterPacksProvider} from './used-starter-packs' @@ -59,6 +60,10 @@ export {useLabelDefinitions} from './label-defs' export {useLanguagePrefs, useLanguagePrefsApi} from './languages' export {useSetSubtitlesEnabled, useSubtitlesEnabled} from './subtitles' +export { + useSetTranslationServicePreference, + useTranslationServicePreference, +} from './translation-service-preference' export function Provider({children}: React.PropsWithChildren<{}>) { return ( @@ -66,17 +71,17 @@ - + - - - - + + + + - + @@ -84,59 +89,63 @@ - - - - - - - - - - - - - - - - - - {children} - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + { + children + } + + + + + + + + + + + + + + + + + + + - + - - - - + + + + - + diff --git a/src/state/preferences/translation-service-preference.tsx b/src/state/preferences/translation-service-preference.tsx new file mode 100644 --- /dev/null +++ b/src/state/preferences/translation-service-preference.tsx @@ -0,0 +1,57 @@ +import React from 'react' + +import * as persisted from '#/state/persisted' + +type StateContext = persisted.Schema['translationServicePreference'] +type SetContext = (v: persisted.Schema['translationServicePreference']) => void + +const stateContext = React.createContext( + persisted.defaults.translationServicePreference, +) +const setContext = React.createContext( + (_: persisted.Schema['translationServicePreference']) => {}, +) + +export function Provider({children}: React.PropsWithChildren<{}>) { + const [state, setState] = React.useState( + persisted.get('translationServicePreference'), + ) + + const setStateWrapped = React.useCallback( + ( + translationServicePreference: persisted.Schema['translationServicePreference'], + ) => { + setState(translationServicePreference) + persisted.write( + 'translationServicePreference', + translationServicePreference, + ) + }, + [setState], + ) + + React.useEffect(() => { + return persisted.onUpdate( + 'translationServicePreference', + nextTranslationServicePreference => { + setState(nextTranslationServicePreference) + }, + ) + }, [setStateWrapped]) + + return ( + + + {children} + + + ) +} + +export function useTranslationServicePreference() { + return React.useContext(stateContext) +} + +export function useSetTranslationServicePreference() { + return React.useContext(setContext) +} diff --git a/src/screens/PostThread/components/ThreadItemAnchor.tsx b/src/screens/PostThread/components/ThreadItemAnchor.tsx --- a/src/screens/PostThread/components/ThreadItemAnchor.tsx +++ b/src/screens/PostThread/components/ThreadItemAnchor.tsx @@ -17,7 +17,11 @@ import {sanitizeDisplayName} from '#/lib/strings/display-names' import {sanitizeHandle} from '#/lib/strings/handles' import {niceDate} from '#/lib/strings/time' -import {getTranslatorLink, isPostInLanguage} from '#/locale/helpers' +import { + getTranslatorLink, + getTranslatorLinkKagi, + isPostInLanguage, +} from '#/locale/helpers' import { POST_TOMBSTONE, type Shadow, @@ -31,6 +35,7 @@ import {useDisableRepostsMetrics} from '#/state/preferences/disable-reposts-metrics' import {useDisableSavesMetrics} from '#/state/preferences/disable-saves-metrics' import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' +import {useTranslationServicePreference} from '#/state/preferences/translation-service-preference' import {type ThreadItem} from '#/state/queries/usePostThread/types' import {useSession} from '#/state/session' import {type OnPostSuccessData} from '#/state/shell/composer' @@ -566,6 +571,8 @@ const isRootPost = !('reply' in post.record) const langPrefs = useLanguagePrefs() + const translationServicePreference = useTranslationServicePreference() + const needsTranslation = useMemo( () => Boolean( @@ -617,10 +624,22 @@