From 81e6c377ba9ad5e5d334c39d8998542ad3e91fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90?= Date: Sat, 8 Jan 2022 19:36:13 +0800 Subject: [PATCH] fix(hooks): provide root locale (#5218) * fix: provide root locale * refactor(locale): refactor locale * fix: tests * revert: play --- .../__tests__/config-provider.spec.ts | 6 +- .../config-provider/src/config-provider.ts | 12 ++-- packages/hooks/__tests__/use-locale.spec.ts | 18 +++--- packages/hooks/use-global-config/index.ts | 8 ++- packages/hooks/use-locale/index.ts | 61 ++++--------------- 5 files changed, 33 insertions(+), 72 deletions(-) diff --git a/packages/components/config-provider/__tests__/config-provider.spec.ts b/packages/components/config-provider/__tests__/config-provider.spec.ts index 997361c262..012802bcfb 100644 --- a/packages/components/config-provider/__tests__/config-provider.spec.ts +++ b/packages/components/config-provider/__tests__/config-provider.spec.ts @@ -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')) } diff --git a/packages/components/config-provider/src/config-provider.ts b/packages/components/config-provider/src/config-provider.ts index 47218e576b..1348edb52c 100644 --- a/packages/components/config-provider/src/config-provider.ts +++ b/packages/components/config-provider/src/config-provider.ts @@ -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(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 }) }, diff --git a/packages/hooks/__tests__/use-locale.spec.ts b/packages/hooks/__tests__/use-locale.spec.ts index d37318aa43..26eace5831 100644 --- a/packages/hooks/__tests__/use-locale.spec.ts +++ b/packages/hooks/__tests__/use-locale.spec.ts @@ -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() }, }, diff --git a/packages/hooks/use-global-config/index.ts b/packages/hooks/use-global-config/index.ts index 4228bd054b..c497af6e67 100644 --- a/packages/hooks/use-global-config/index.ts +++ b/packages/hooks/use-global-config/index.ts @@ -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({}) +// 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({}) export function useGlobalConfig( key: K ): Ref export function useGlobalConfig(): Ref 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 } diff --git a/packages/hooks/use-locale/index.ts b/packages/hooks/use-locale/index.ts index dc1bbbebc5..d1af5311c6 100644 --- a/packages/hooks/use-locale/index.ts +++ b/packages/hooks/use-locale/index.ts @@ -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 @@ -14,48 +14,6 @@ export type LocaleContext = { t: Translator } -export const useLocaleProps = buildProps({ - locale: { - type: definePropType(Object), - }, -}) - -export const localeContextKey: InjectionKey = - 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: - - /** - * - * - * Something calls modal component. - * - * - */ - cache = provides - provide(localeContextKey, provides) -} - export const buildTranslator = (locale: MaybeRef): 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 +): 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)) }