基于React Hook的国际化(i18n)解决方案

2020-11-12  本文已影响0人  请叫我Pro大叔

设计思路

代码

/* 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
上一篇下一篇

猜你喜欢

热点阅读