基于React Hook的国际化(i18n)解决方案
2020-11-12 本文已影响0人
请叫我Pro大叔
设计思路
- 持久化locale信息到
localeStorage
- 创建
useLocale
用于获取locale以及设置并持久化locale信息 - 创建
I18nConfigProvider
提供locale配置加载方法 - 创建
useLocaleConfig
用于根据locale的变化加载locale配置 - 创建
LocaleConfigProvider.tsx
将LocaleConfig配置应用于组件中
代码
/* types.ts */
import { Locale as AntdLocale } from 'antd/lib/locale-provider'
import { IntlConfig } from 'react-intl'
export type SupportedLocale = 'en-US' | 'zh-CN'
export interface LocaleConfig extends Pick<IntlConfig, 'messages'> {
locale: SupportedLocale
/** 支持antd配置 */
antdLocale: AntdLocale
}
export interface I18nConfigProvider {
defaultConfig: LocaleConfig
getConfig: (locale?: SupportedLocale) => Promise<LocaleConfig>
}
/** locale.ts */
/** 默认i18nProvider */
const defaultI18nProvider: I18nConfigProvider = {
defaultConfig: enConfig,
// defaultConfig: zhConfig,
async getConfig(locale?: SupportedLocale) {
if (locale === 'zh-CN') {
return (await import('./zh_CN')).default
}
if (locale === 'en-US') {
return enConfig
}
return this.defaultConfig
}
}
const KEY_LOCALE = '__locale__'
const DEFAULT_LOCALE = 'en-US'
function persistLocale(lang: SupportedLocale) {
window.localStorage.setItem(KEY_LOCALE, lang || DEFAULT_LOCALE)
// 重新刷新整个页面
// window.location.reload()
}
export function getLocale(): SupportedLocale {
return (
(window.localStorage.getItem(KEY_LOCALE) as SupportedLocale) ??
DEFAULT_LOCALE
)
}
const createLocaleHook = (defaultLocale: SupportedLocale = getLocale()) => {
let localeCache = defaultLocale
let reactions = []
const setStoreFn = (newLocale: SupportedLocale) => {
localeCache = newLocale
persistLocale(localeCache)
// Call reactions
reactions.forEach(fn => fn())
}
return (): [SupportedLocale, (newLocale: SupportedLocale) => void] => {
const [locale, setLocale] = useState<SupportedLocale>(localeCache)
const reaction = useCallback(() => {
setLocale(localeCache)
}, [])
useEffect(() => {
reactions.push(reaction)
return () => {
reactions = reactions.filter(f => reaction !== f)
}
}, [])
return [locale, setStoreFn]
}
}
export const useLocale = createLocaleHook()
export const useLocaleConfig = (
defaultI18nConfigProvider: I18nConfigProvider = defaultI18nProvider
) => {
const { getConfig, defaultConfig } = defaultI18nConfigProvider
const [locale] = useLocale()
const [localeConfig, setLocaleConfig] = useState<LocaleConfig>(defaultConfig)
useEffect(() => {
// DO Load New Locale Config
const load = async () => {
const newLocaleConfig = await getConfig(locale)
console.log('newLocaleConfig ---> ', newLocaleConfig)
setLocaleConfig(newLocaleConfig)
}
if (locale && locale !== defaultConfig.locale) {
load()
} else {
setLocaleConfig(defaultConfig)
}
}, [locale, localeConfigProvider])
return localeConfig
}
/** LocaleConfigProvider.tsx */
import { useLocaleConfig } from '@/locales'
import { ConfigProvider } from 'antd'
import React from 'react'
import { IntlProvider } from 'react-intl'
const LocaleConfigProvider: React.FC = ({ children }) => {
const currentConfig = useLocaleConfig()
return (
<IntlProvider
messages={currentConfig.messages}
locale={currentConfig.locale}
defaultLocale={currentConfig.locale}
>
<ConfigProvider locale={currentConfig.antdLocale}>
{children}
</ConfigProvider>
</IntlProvider>
)
}
export default LocaleConfigProvider