Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client

add translation engine choice into tweaks, add kagi as option #51

merged opened by daniela.lol targeting main from daniela.lol/witchsky.app: main

this is HEAVILY based on code thats inside the wip post-text-option branch, i saw what was going on in there and instantly thought about other ways that could be used and decided to write this quickly

this currently only adds kagi as a separate engine, i was thinking on adding deepl but im not sure if it supports translating through url...? if it does i mustve missed it, but absolutely use this as a base to add whatever translation engines u want to

this could 100% be done better but im not smart so this was my quick n dirty solution, feel free to improve this

works on both desktop and android, lemme know if theres any issues

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:x3vdwseoxbzbp2dfv7q3brqn/sh.tangled.repo.pull/3md7rdpcm5c22
+205 -57
Diff #0
+14 -4
src/lib/hooks/useTranslate.ts
··· 1 1 import {useCallback} from 'react' 2 2 import * as IntentLauncher from 'expo-intent-launcher' 3 3 4 - import {getTranslatorLink} from '#/locale/helpers' 4 + import {getTranslatorLink, getTranslatorLinkKagi} from '#/locale/helpers' 5 + import {useTranslationServicePreference} from '#/state/preferences/translation-service-preference' 5 6 import {IS_ANDROID} from '#/env' 6 7 import {useOpenLink} from './useOpenLink' 7 8 8 9 export function useTranslate() { 9 10 const openLink = useOpenLink() 10 11 12 + const translationServicePreference = useTranslationServicePreference() 13 + 11 14 return useCallback( 12 15 async (text: string, language: string) => { 13 - const translateUrl = getTranslatorLink(text, language) 14 - if (IS_ANDROID) { 16 + let translateUrl 17 + 18 + // if ur curious why this isnt a switch case, good question, for some reason making this a switch case breaks the functionality 19 + // it is a mystery https://www.youtube.com/watch?v=fq3abPnEEGE 20 + if (translationServicePreference == 'kagi') 21 + translateUrl = getTranslatorLinkKagi(text, language) 22 + else translateUrl = getTranslatorLink(text, language) 23 + 24 + if (IS_ANDROID && translationServicePreference == 'google') { 15 25 try { 16 26 // use getApplicationIconAsync to determine if the translate app is installed 17 27 if ( ··· 49 59 await openLink(translateUrl) 50 60 } 51 61 }, 52 - [openLink], 62 + [openLink, translationServicePreference], 53 63 ) 54 64 }
+7
src/locale/helpers.ts
··· 127 127 return bcp47Match.basicFilter(lang, targetLangs).length > 0 128 128 } 129 129 130 + // we cant hook into functions like this, so we will make other translator functions n swap between em 130 131 export function getTranslatorLink(text: string, lang: string): string { 131 132 return `https://translate.google.com/?sl=auto&tl=${lang}&text=${encodeURIComponent( 132 133 text, 133 134 )}` 134 135 } 135 136 137 + export function getTranslatorLinkKagi(text: string, lang: string): string { 138 + return `https://translate.kagi.com/?from=auto&to=${lang}&text=${encodeURIComponent( 139 + text, 140 + )}` 141 + } 142 + 136 143 /** 137 144 * Returns a valid `appLanguage` value from an arbitrary string. 138 145 *
+24 -5
src/screens/PostThread/components/ThreadItemAnchor.tsx
··· 17 17 import {sanitizeDisplayName} from '#/lib/strings/display-names' 18 18 import {sanitizeHandle} from '#/lib/strings/handles' 19 19 import {niceDate} from '#/lib/strings/time' 20 - import {getTranslatorLink, isPostInLanguage} from '#/locale/helpers' 20 + import { 21 + getTranslatorLink, 22 + getTranslatorLinkKagi, 23 + isPostInLanguage, 24 + } from '#/locale/helpers' 21 25 import {logger} from '#/logger' 22 26 import { 23 27 POST_TOMBSTONE, ··· 32 36 import {useDisableRepostsMetrics} from '#/state/preferences/disable-reposts-metrics' 33 37 import {useDisableSavesMetrics} from '#/state/preferences/disable-saves-metrics' 34 38 import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' 39 + import {useTranslationServicePreference} from '#/state/preferences/translation-service-preference' 35 40 import {type ThreadItem} from '#/state/queries/usePostThread/types' 36 41 import {useSession} from '#/state/session' 37 42 import {type OnPostSuccessData} from '#/state/shell/composer' ··· 563 568 const isRootPost = !('reply' in post.record) 564 569 const langPrefs = useLanguagePrefs() 565 570 571 + const translationServicePreference = useTranslationServicePreference() 572 + 566 573 const needsTranslation = useMemo( 567 574 () => 568 575 Boolean( ··· 614 621 <InlineLinkText 615 622 // overridden to open an intent on android, but keep 616 623 // as anchor tag for accessibility 617 - to={getTranslatorLink( 618 - post.record.text, 619 - langPrefs.primaryLanguage, 620 - )} 624 + to={ 625 + translationServicePreference === 'kagi' 626 + ? getTranslatorLinkKagi( 627 + post.record.text, 628 + langPrefs.primaryLanguage, 629 + ) 630 + : // in case u want to expand this to allow other services to be u would do this 631 + // : translationServicePreference === "insert service name" 632 + // ? getTranslatorLink(post.record.text, langPrefs.primaryLanguage, "insert service name") 633 + // atm i cant really think of another service that lets u easily do this so its only kagi for now 634 + // the default itll use if its not any of the checks is google!! 635 + getTranslatorLink( 636 + post.record.text, 637 + langPrefs.primaryLanguage, 638 + ) 639 + } 621 640 label={_(msg`Translate`)} 622 641 style={[a.text_sm]} 623 642 onPress={onTranslatePress}>
+43
src/screens/Settings/DeerSettings.tsx
··· 109 109 useSetShowLinkInHandle, 110 110 useShowLinkInHandle, 111 111 } from '#/state/preferences/show-link-in-handle.tsx' 112 + import { 113 + useSetTranslationServicePreference, 114 + useTranslationServicePreference, 115 + } from '#/state/preferences/translation-service-preference' 112 116 import {useProfilesQuery} from '#/state/queries/profile' 113 117 import * as SettingsList from '#/screens/Settings/components/SettingsList' 114 118 import {atoms as a, useBreakpoints} from '#/alf' ··· 119 123 import {Atom_Stroke2_Corner0_Rounded as DeerIcon} from '#/components/icons/Atom' 120 124 import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink' 121 125 import {Eye_Stroke2_Corner0_Rounded as VisibilityIcon} from '#/components/icons/Eye' 126 + import {Earth_Stroke2_Corner2_Rounded as EarthIcon} from '#/components/icons/Globe' 122 127 import {Lab_Stroke2_Corner0_Rounded as BeakerIcon} from '#/components/icons/Lab' 123 128 import {PaintRoller_Stroke2_Corner2_Rounded as PaintRollerIcon} from '#/components/icons/PaintRoller' 124 129 import {RaisingHand4Finger_Stroke2_Corner0_Rounded as RaisingHandIcon} from '#/components/icons/RaisingHand' ··· 353 358 const showLinkInHandle = useShowLinkInHandle() 354 359 const setShowLinkInHandle = useSetShowLinkInHandle() 355 360 361 + const translationServicePreference = useTranslationServicePreference() 362 + const setTranslationServicePreference = useSetTranslationServicePreference() 363 + 356 364 const [gates, setGatesView] = useState( 357 365 Object.assign(defaultGateValues, Object.fromEntries(useGatesCache())), 358 366 ) ··· 654 662 </Admonition> 655 663 </SettingsList.Group> 656 664 665 + <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 666 + <SettingsList.ItemIcon icon={EarthIcon} /> 667 + <SettingsList.ItemText> 668 + <Trans>Translation Engine</Trans> 669 + </SettingsList.ItemText> 670 + 671 + <Admonition type="info" style={[a.flex_1]}> 672 + <Trans>Choose the engine to use when translating posts.</Trans> 673 + </Admonition> 674 + 675 + <Toggle.Item 676 + name="service_google" 677 + label={_(msg`Use Google Translate`)} 678 + value={translationServicePreference === 'google'} 679 + onChange={() => setTranslationServicePreference('google')} 680 + style={[a.w_full]}> 681 + <Toggle.LabelText style={[a.flex_1]}> 682 + <Trans>Use Google Translate</Trans> 683 + </Toggle.LabelText> 684 + <Toggle.Radio /> 685 + </Toggle.Item> 686 + 687 + <Toggle.Item 688 + name="service_kagi" 689 + label={_(msg`Use Kagi Translate`)} 690 + value={translationServicePreference === 'kagi'} 691 + onChange={() => setTranslationServicePreference('kagi')} 692 + style={[a.w_full]}> 693 + <Toggle.LabelText style={[a.flex_1]}> 694 + <Trans>Use Kagi Translate</Trans> 695 + </Toggle.LabelText> 696 + <Toggle.Radio /> 697 + </Toggle.Item> 698 + </SettingsList.Group> 699 + 657 700 <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 658 701 <SettingsList.ItemIcon icon={VisibilityIcon} /> 659 702 <SettingsList.ItemText>
+3
src/state/persisted/schema.ts
··· 171 171 172 172 showExternalShareButtons: z.boolean().optional(), 173 173 174 + translationServicePreference: z.enum(['google', 'kagi']), 175 + 174 176 /** @deprecated */ 175 177 mutedThreads: z.array(z.string()), 176 178 trendingDisabled: z.boolean().optional(), ··· 279 281 highQualityImages: false, 280 282 hideUnreplyablePosts: false, 281 283 showExternalShareButtons: false, 284 + translationServicePreference: 'google', 282 285 } 283 286 284 287 export function tryParse(rawData: string): Schema | undefined {
+57 -48
src/state/preferences/index.tsx
··· 37 37 import {Provider as RepostCarouselProvider} from './repost-carousel-enabled' 38 38 import {Provider as ShowLinkInHandleProvider} from './show-link-in-handle' 39 39 import {Provider as SubtitlesProvider} from './subtitles' 40 + import {Provider as TranslationServicePreferenceProvider} from './translation-service-preference' 40 41 import {Provider as TrendingSettingsProvider} from './trending' 41 42 import {Provider as UsedStarterPacksProvider} from './used-starter-packs' 42 43 ··· 59 60 export {useLabelDefinitions} from './label-defs' 60 61 export {useLanguagePrefs, useLanguagePrefsApi} from './languages' 61 62 export {useSetSubtitlesEnabled, useSubtitlesEnabled} from './subtitles' 63 + export { 64 + useSetTranslationServicePreference, 65 + useTranslationServicePreference, 66 + } from './translation-service-preference' 62 67 63 68 export function Provider({children}: React.PropsWithChildren<{}>) { 64 69 return ( ··· 66 71 <AltTextRequiredProvider> 67 72 <ExternalShareButtonsProvider> 68 73 <GoLinksProvider> 69 - <NoAppLabelersProvider> 74 + <NoAppLabelersProvider> 70 75 <DirectFetchRecordsProvider> 71 76 <ConstellationProvider> 72 - <ConstellationInstanceProvider> 73 - <DeerVerificationProvider> 74 - <NoDiscoverProvider> 75 - <ShowLinkInHandleProvider> 77 + <ConstellationInstanceProvider> 78 + <DeerVerificationProvider> 79 + <NoDiscoverProvider> 80 + <ShowLinkInHandleProvider> 76 81 <LargeAltBadgeProvider> 77 82 <ExternalEmbedsProvider> 78 83 <HiddenPostsProvider> 79 - <HighQualityImagesProvider> 84 + <HighQualityImagesProvider> 80 85 <InAppBrowserProvider> 81 86 <DisableHapticsProvider> 82 87 <AutoplayProvider> ··· 84 89 <SubtitlesProvider> 85 90 <TrendingSettingsProvider> 86 91 <RepostCarouselProvider> 87 - <KawaiiProvider> 88 - <HideFeedsPromoTabProvider> 89 - <DisableViaRepostNotificationProvider> 90 - <DisableLikesMetricsProvider> 91 - <DisableRepostsMetricsProvider> 92 - <DisableQuotesMetricsProvider> 93 - <DisableSavesMetricsProvider> 94 - <DisableReplyMetricsProvider> 95 - <DisableFollowersMetricsProvider> 96 - <DisableFollowingMetricsProvider> 97 - <DisableFollowedByMetricsProvider> 98 - <DisablePostsMetricsProvider> 99 - <HideSimilarAccountsRecommProvider> 100 - <HideUnreplyablePostsProvider> 101 - <EnableSquareAvatarsProvider> 102 - <EnableSquareButtonsProvider> 103 - <DisableVerifyEmailReminderProvider> 104 - {children} 105 - </DisableVerifyEmailReminderProvider> 106 - </EnableSquareButtonsProvider> 107 - </EnableSquareAvatarsProvider> 108 - </HideUnreplyablePostsProvider> 109 - </HideSimilarAccountsRecommProvider> 110 - </DisablePostsMetricsProvider> 111 - </DisableFollowedByMetricsProvider> 112 - </DisableFollowingMetricsProvider> 113 - </DisableFollowersMetricsProvider> 114 - </DisableReplyMetricsProvider> 115 - </DisableSavesMetricsProvider> 116 - </DisableQuotesMetricsProvider> 117 - </DisableRepostsMetricsProvider> 118 - </DisableLikesMetricsProvider> 119 - </DisableViaRepostNotificationProvider> 120 - </HideFeedsPromoTabProvider> 121 - </KawaiiProvider> 122 - </RepostCarouselProvider> 92 + <KawaiiProvider> 93 + <HideFeedsPromoTabProvider> 94 + <DisableViaRepostNotificationProvider> 95 + <DisableLikesMetricsProvider> 96 + <DisableRepostsMetricsProvider> 97 + <DisableQuotesMetricsProvider> 98 + <DisableSavesMetricsProvider> 99 + <DisableReplyMetricsProvider> 100 + <DisableFollowersMetricsProvider> 101 + <DisableFollowingMetricsProvider> 102 + <DisableFollowedByMetricsProvider> 103 + <DisablePostsMetricsProvider> 104 + <HideSimilarAccountsRecommProvider> 105 + <HideUnreplyablePostsProvider> 106 + <EnableSquareAvatarsProvider> 107 + <EnableSquareButtonsProvider> 108 + <DisableVerifyEmailReminderProvider> 109 + <TranslationServicePreferenceProvider> 110 + { 111 + children 112 + } 113 + </TranslationServicePreferenceProvider> 114 + </DisableVerifyEmailReminderProvider> 115 + </EnableSquareButtonsProvider> 116 + </EnableSquareAvatarsProvider> 117 + </HideUnreplyablePostsProvider> 118 + </HideSimilarAccountsRecommProvider> 119 + </DisablePostsMetricsProvider> 120 + </DisableFollowedByMetricsProvider> 121 + </DisableFollowingMetricsProvider> 122 + </DisableFollowersMetricsProvider> 123 + </DisableReplyMetricsProvider> 124 + </DisableSavesMetricsProvider> 125 + </DisableQuotesMetricsProvider> 126 + </DisableRepostsMetricsProvider> 127 + </DisableLikesMetricsProvider> 128 + </DisableViaRepostNotificationProvider> 129 + </HideFeedsPromoTabProvider> 130 + </KawaiiProvider> 131 + </RepostCarouselProvider> 123 132 </TrendingSettingsProvider> 124 133 </SubtitlesProvider> 125 134 </UsedStarterPacksProvider> 126 135 </AutoplayProvider> 127 136 </DisableHapticsProvider> 128 137 </InAppBrowserProvider> 129 - </HighQualityImagesProvider> 138 + </HighQualityImagesProvider> 130 139 </HiddenPostsProvider> 131 140 </ExternalEmbedsProvider> 132 141 </LargeAltBadgeProvider> 133 - </ShowLinkInHandleProvider> 134 - </NoDiscoverProvider> 135 - </DeerVerificationProvider> 136 - </ConstellationInstanceProvider> 142 + </ShowLinkInHandleProvider> 143 + </NoDiscoverProvider> 144 + </DeerVerificationProvider> 145 + </ConstellationInstanceProvider> 137 146 </ConstellationProvider> 138 147 </DirectFetchRecordsProvider> 139 - </NoAppLabelersProvider> 148 + </NoAppLabelersProvider> 140 149 </GoLinksProvider> 141 150 </ExternalShareButtonsProvider> 142 151 </AltTextRequiredProvider>
+57
src/state/preferences/translation-service-preference.tsx
··· 1 + import React from 'react' 2 + 3 + import * as persisted from '#/state/persisted' 4 + 5 + type StateContext = persisted.Schema['translationServicePreference'] 6 + type SetContext = (v: persisted.Schema['translationServicePreference']) => void 7 + 8 + const stateContext = React.createContext<StateContext>( 9 + persisted.defaults.translationServicePreference, 10 + ) 11 + const setContext = React.createContext<SetContext>( 12 + (_: persisted.Schema['translationServicePreference']) => {}, 13 + ) 14 + 15 + export function Provider({children}: React.PropsWithChildren<{}>) { 16 + const [state, setState] = React.useState( 17 + persisted.get('translationServicePreference'), 18 + ) 19 + 20 + const setStateWrapped = React.useCallback( 21 + ( 22 + translationServicePreference: persisted.Schema['translationServicePreference'], 23 + ) => { 24 + setState(translationServicePreference) 25 + persisted.write( 26 + 'translationServicePreference', 27 + translationServicePreference, 28 + ) 29 + }, 30 + [setState], 31 + ) 32 + 33 + React.useEffect(() => { 34 + return persisted.onUpdate( 35 + 'translationServicePreference', 36 + nextTranslationServicePreference => { 37 + setState(nextTranslationServicePreference) 38 + }, 39 + ) 40 + }, [setStateWrapped]) 41 + 42 + return ( 43 + <stateContext.Provider value={state}> 44 + <setContext.Provider value={setStateWrapped}> 45 + {children} 46 + </setContext.Provider> 47 + </stateContext.Provider> 48 + ) 49 + } 50 + 51 + export function useTranslationServicePreference() { 52 + return React.useContext(stateContext) 53 + } 54 + 55 + export function useSetTranslationServicePreference() { 56 + return React.useContext(setContext) 57 + }

History

3 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
change translation engine tweak
expand 0 comments
pull request successfully merged
2 commits
expand
change translation engine tweak
vs code did some weird auto formatting n that sketches me out
expand 0 comments
daniela.lol submitted #0
1 commit
expand
change translation engine tweak
expand 0 comments