fix(hooks): provide root locale (#5218)

* fix: provide root locale

* refactor(locale): refactor locale

* fix: tests

* revert: play
This commit is contained in:
三咲智子 2022-01-08 19:36:13 +08:00 committed by GitHub
parent f5b7e3accc
commit 81e6c377ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 33 additions and 72 deletions

View File

@ -1,6 +1,6 @@
import { h, ref, inject, reactive, nextTick } from 'vue'
import { h, ref, reactive, nextTick } from 'vue'
import { mount } from '@vue/test-utils'
import { localeContextKey } from '@element-plus/hooks'
import { useLocale } from '@element-plus/hooks'
import Chinese from '@element-plus/locale/lang/zh-cn'
import English from '@element-plus/locale/lang/en'
import { ElButton } from '@element-plus/components'
@ -9,7 +9,7 @@ import type { Language } from '@element-plus/locale'
const TestComp = {
setup() {
const { t } = inject(localeContextKey)
const { t } = useLocale()
return () => {
return h('div', t('el.popconfirm.confirmButtonText'))
}

View File

@ -1,14 +1,13 @@
import { defineComponent, renderSlot } from 'vue'
import { buildProps, definePropType } from '@element-plus/utils/props'
import {
useLocaleProps,
provideLocale,
provideGlobalConfig,
} from '@element-plus/hooks'
import { provideGlobalConfig } from '@element-plus/hooks'
import type { Language } from '@element-plus/locale'
import type { ButtonConfigContext } from '@element-plus/components/button'
export const configProviderProps = buildProps({
...useLocaleProps,
locale: {
type: definePropType<Language>(Object),
},
size: {
type: String,
@ -29,7 +28,6 @@ export default defineComponent({
props: configProviderProps,
setup(props, { slots }) {
provideLocale()
const config = provideGlobalConfig(props)
return () => renderSlot(slots, 'default', { config: config?.value })
},

View File

@ -1,13 +1,9 @@
import { h, nextTick } from 'vue'
import { h, nextTick, computed } from 'vue'
import { mount } from '@vue/test-utils'
import Chinese from '@element-plus/locale/lang/zh-cn'
import English from '@element-plus/locale/lang/en'
import {
provideLocale,
useLocaleProps,
useLocale,
buildTranslator,
} from '../use-locale'
import { useLocale, buildTranslator } from '../use-locale'
import { provideGlobalConfig } from '..'
const TestComp = {
setup() {
@ -27,12 +23,14 @@ describe('use-locale', () => {
beforeEach(() => {
wrapper = mount(
{
props: useLocaleProps,
props: {
locale: Object,
},
components: {
'el-test': TestComp,
},
setup(_, { slots }) {
provideLocale()
setup(props, { slots }) {
provideGlobalConfig(computed(() => ({ locale: props.locale })))
return () => slots.default()
},
},

View File

@ -6,14 +6,17 @@ import type { MaybeRef } from '@vueuse/core'
import type { Ref, App } from 'vue'
import type { ConfigProviderContext } from '@element-plus/tokens'
const fallback = ref<ConfigProviderContext>({})
// this is meant to fix global methods like `ElMessage(opts)`, this way we can inject current locale
// into the component as default injection value.
// refer to: https://github.com/element-plus/element-plus/issues/2610#issuecomment-887965266
const cache = ref<ConfigProviderContext>({})
export function useGlobalConfig<K extends keyof ConfigProviderContext>(
key: K
): Ref<ConfigProviderContext[K]>
export function useGlobalConfig(): Ref<ConfigProviderContext>
export function useGlobalConfig(key?: keyof ConfigProviderContext) {
const config = inject(configProviderContextKey, fallback)
const config = inject(configProviderContextKey, cache)
if (key) {
return isObject(config.value) && hasOwn(config.value, key)
? computed(() => config.value[key])
@ -45,5 +48,6 @@ export const provideGlobalConfig = (
return merge(oldConfig.value, cfg)
})
provideFn(configProviderContextKey, context)
cache.value = context.value
return context
}

View File

@ -1,9 +1,9 @@
import { computed, getCurrentInstance, inject, provide, ref, unref } from 'vue'
import { computed, ref, unref, isRef } from 'vue'
import get from 'lodash/get'
import English from '@element-plus/locale/lang/en'
import { buildProps, definePropType } from '@element-plus/utils/props'
import { useGlobalConfig } from '../use-global-config'
import type { MaybeRef } from '@vueuse/core'
import type { InjectionKey, Ref } from 'vue'
import type { Ref } from 'vue'
import type { Language } from '@element-plus/locale'
export type TranslatorOption = Record<string, string | number>
@ -14,48 +14,6 @@ export type LocaleContext = {
t: Translator
}
export const useLocaleProps = buildProps({
locale: {
type: definePropType<Language>(Object),
},
})
export const localeContextKey: InjectionKey<LocaleContext> =
Symbol('localeContextKey')
// this is meant to fix global methods like `ElMessage(opts)`, this way we can inject current locale
// into the component as default injection value.
// refer to: https://github.com/element-plus/element-plus/issues/2610#issuecomment-887965266
let cache: LocaleContext
export const provideLocale = () => {
const vm = getCurrentInstance()!
const props = vm.props as {
locale: Language
}
const locale = computed(() => props.locale || English)
const lang = computed(() => locale.value.name)
const t = buildTranslator(locale)
const provides: LocaleContext = {
locale,
lang,
t,
}
// this could be broken if someone tries to do following:
/**
* <config-provider :locale="lang1">
* <config-provider :locale="lang2">
* Something calls modal component.
* </config-provider>
* </config-provider>
*/
cache = provides
provide(localeContextKey, provides)
}
export const buildTranslator =
(locale: MaybeRef<Language>): Translator =>
(path, option) =>
@ -71,16 +29,19 @@ export const translate = (
(_, key) => `${option?.[key] ?? `{${key}}`}`
)
export const localeProviderMaker = (locale = English) => {
const lang = ref(locale.name)
const localeRef = ref(locale)
export const buildLocaleContext = (
locale: MaybeRef<Language>
): LocaleContext => {
const lang = computed(() => unref(locale).name)
const localeRef = isRef(locale) ? locale : ref(locale)
return {
lang,
locale: localeRef,
t: buildTranslator(localeRef),
t: buildTranslator(locale),
}
}
export const useLocale = () => {
return inject(localeContextKey, cache || localeProviderMaker(English))
const locale = useGlobalConfig('locale')
return buildLocaleContext(computed(() => locale.value || English))
}