Files
bsky-social-app/src/state/preferences/languages.tsx
T
Ansh c5b6f88e9a Hindi Internationalization (#1914)
* get basic hindi support to work

* get web app language switcher in

* Refactor i18n implementation and remove unused
code

* add missing strings

* add dropdowns and modals missing strings

* complete all hindi translations

* fix merge conflicts

* fix legeacy persisted state

* fix data in RecommendedFeeds

* fix lint
2023-11-20 13:29:27 -08:00

143 lines
4.2 KiB
TypeScript

import React from 'react'
import * as persisted from '#/state/persisted'
type SetStateCb = (
s: persisted.Schema['languagePrefs'],
) => persisted.Schema['languagePrefs']
type StateContext = persisted.Schema['languagePrefs']
type ApiContext = {
setPrimaryLanguage: (code2: string) => void
setPostLanguage: (commaSeparatedLangCodes: string) => void
toggleContentLanguage: (code2: string) => void
togglePostLanguage: (code2: string) => void
savePostLanguageToHistory: () => void
setAppLanguage: (code2: string) => void
}
const stateContext = React.createContext<StateContext>(
persisted.defaults.languagePrefs,
)
const apiContext = React.createContext<ApiContext>({
setPrimaryLanguage: (_: string) => {},
setPostLanguage: (_: string) => {},
toggleContentLanguage: (_: string) => {},
togglePostLanguage: (_: string) => {},
savePostLanguageToHistory: () => {},
setAppLanguage: (_: string) => {},
})
export function Provider({children}: React.PropsWithChildren<{}>) {
const [state, setState] = React.useState(persisted.get('languagePrefs'))
const setStateWrapped = React.useCallback(
(fn: SetStateCb) => {
const s = fn(persisted.get('languagePrefs'))
setState(s)
persisted.write('languagePrefs', s)
},
[setState],
)
React.useEffect(() => {
return persisted.onUpdate(() => {
setState(persisted.get('languagePrefs'))
})
}, [setStateWrapped])
const api = React.useMemo(
() => ({
setPrimaryLanguage(code2: string) {
setStateWrapped(s => ({...s, primaryLanguage: code2}))
},
setPostLanguage(commaSeparatedLangCodes: string) {
setStateWrapped(s => ({...s, postLanguage: commaSeparatedLangCodes}))
},
toggleContentLanguage(code2: string) {
setStateWrapped(s => {
const exists = s.contentLanguages.includes(code2)
const next = exists
? s.contentLanguages.filter(lang => lang !== code2)
: s.contentLanguages.concat(code2)
return {
...s,
contentLanguages: next,
}
})
},
togglePostLanguage(code2: string) {
setStateWrapped(s => {
const exists = hasPostLanguage(state.postLanguage, code2)
let next = s.postLanguage
if (exists) {
next = toPostLanguages(s.postLanguage)
.filter(lang => lang !== code2)
.join(',')
} else {
// sort alphabetically for deterministic comparison in context menu
next = toPostLanguages(s.postLanguage)
.concat([code2])
.sort((a, b) => a.localeCompare(b))
.join(',')
}
return {
...s,
postLanguage: next,
}
})
},
/**
* Saves whatever language codes are currently selected into a history array,
* which is then used to populate the language selector menu.
*/
savePostLanguageToHistory() {
// filter out duplicate `this.postLanguage` if exists, and prepend
// value to start of array
setStateWrapped(s => ({
...s,
postLanguageHistory: [s.postLanguage]
.concat(
s.postLanguageHistory.filter(
commaSeparatedLangCodes =>
commaSeparatedLangCodes !== s.postLanguage,
),
)
.slice(0, 6),
}))
},
setAppLanguage(code2: string) {
setStateWrapped(s => ({...s, appLanguage: code2}))
},
}),
[state, setStateWrapped],
)
return (
<stateContext.Provider value={state}>
<apiContext.Provider value={api}>{children}</apiContext.Provider>
</stateContext.Provider>
)
}
export function useLanguagePrefs() {
return React.useContext(stateContext)
}
export function useLanguagePrefsApi() {
return React.useContext(apiContext)
}
export function getContentLanguages() {
return persisted.get('languagePrefs').contentLanguages
}
export function toPostLanguages(postLanguage: string): string[] {
// filter out empty strings if exist
return postLanguage.split(',').filter(Boolean)
}
export function hasPostLanguage(postLanguage: string, code2: string): boolean {
return toPostLanguages(postLanguage).includes(code2)
}