diff --git a/src/components/Translation.ts b/src/components/Translation.ts index 24ccf4e2f..927292808 100644 --- a/src/components/Translation.ts +++ b/src/components/Translation.ts @@ -1,9 +1,17 @@ -import { h, Fragment, defineComponent, SetupContext, VNodeChild } from 'vue' +import { + h, + Component, + ComponentOptions, + Fragment, + defineComponent, + SetupContext, + VNodeChild +} from 'vue' import { Composer, ComposerInternal } from '../composer' import { useI18n } from '../i18n' import { TranslateOptions } from '../core' import { NamedValue } from '../message/runtime' -import { isNumber, isString } from '../utils' +import { isNumber, isString, isObject } from '../utils' import { baseFormatProps, BaseFormatProps } from './base' export interface TranslationProps extends BaseFormatProps { @@ -43,9 +51,12 @@ export const Translation = defineComponent({ } const arg = getInterpolateArg(context, keys) const children = i18n.__transrateVNode(props.keypath, arg, options) - return props.tag + // prettier-ignore + return isString(props.tag) ? h(props.tag, { ...attrs }, children) - : h(Fragment, { ...attrs }, children) + : isObject(props.tag) + ? h(props.tag as Component | ComponentOptions, { ...attrs }, children) + : h(Fragment, { ...attrs }, children) } } }) diff --git a/src/components/base.ts b/src/components/base.ts index 74938ab83..0e30962a7 100644 --- a/src/components/base.ts +++ b/src/components/base.ts @@ -5,14 +5,14 @@ import { I18nScope } from '../i18n' export type ComponetI18nScope = Exclude export interface BaseFormatProps { - tag?: string + tag?: string | object locale?: Locale scope?: ComponetI18nScope } export const baseFormatProps = { tag: { - type: String + type: [String, Object] }, locale: { type: String diff --git a/src/components/formatRenderer.ts b/src/components/formatRenderer.ts index 8ab120bbe..a3feae170 100644 --- a/src/components/formatRenderer.ts +++ b/src/components/formatRenderer.ts @@ -1,5 +1,7 @@ import { h, + Component, + ComponentOptions, RenderFunction, Fragment, SetupContext, @@ -7,7 +9,7 @@ import { VNodeArrayChildren } from 'vue' import { NumberOptions, DateTimeOptions } from '../core' -import { isString, isPlainObject, isArray } from '../utils' +import { isString, isObject, isArray } from '../utils' import { BaseFormatProps } from './base' export interface FormattableProps extends BaseFormatProps { @@ -45,7 +47,7 @@ export function renderFormatter< if (isString(props.format)) { options.key = props.format - } else if (isPlainObject(props.format)) { + } else if (isObject(props.format)) { // eslint-disable-next-line @typescript-eslint/no-explicit-any if (isString((props.format as any).key)) { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -72,8 +74,11 @@ export function renderFormatter< children = [parts] } - return props.tag + // prettier-ignore + return isString(props.tag) ? h(props.tag, { ...attrs }, children) - : h(Fragment, { ...attrs }, children) + : isObject(props.tag) + ? h(props.tag as Component | ComponentOptions, { ...attrs }, children) + : h(Fragment, { ...attrs }, children) } } diff --git a/test/components/DatetimeFormat.test.ts b/test/components/DatetimeFormat.test.ts index bc22a778b..671a1cad4 100644 --- a/test/components/DatetimeFormat.test.ts +++ b/test/components/DatetimeFormat.test.ts @@ -3,7 +3,7 @@ */ import { mount } from '../helper' -import { defineComponent } from 'vue' +import { defineComponent, SetupContext, VNodeChild, h } from 'vue' import { createI18n } from '../../src/i18n' const datetimeFormats = { @@ -100,3 +100,33 @@ test('slots', async () => { /([1-9]|1[0-2])年([1-9]|1[0-2])月([1-9]|[1-3][0-9])日(月|火|水|木|金|土|日)曜日 (午前|午後)([0-9]|1[0-2]):([0-5][0-9]):([0-5][0-9]) (協定世界時|グリニッジ標準時)/ ) }) + +test('component', async () => { + const i18n = createI18n({ + locale: 'en-US', + datetimeFormats + }) + + const MyComponent = defineComponent({ + setup(props, context: SetupContext) { + return (): VNodeChild => h('span', context.slots.default()) + } + }) + + const App = defineComponent({ + data: () => ({ MyComponent }), + template: ` + + + +` + }) + const wrapper = await mount(App, i18n) + + expect(wrapper.html().includes('span')).toBeTruthy() +}) diff --git a/test/components/NumberFormat.test.ts b/test/components/NumberFormat.test.ts index 2c6930372..cfef3a40d 100644 --- a/test/components/NumberFormat.test.ts +++ b/test/components/NumberFormat.test.ts @@ -3,7 +3,7 @@ */ import { mount } from '../helper' -import { defineComponent } from 'vue' +import { defineComponent, SetupContext, VNodeChild, h } from 'vue' import { createI18n } from '../../src/i18n' const numberFormats = { @@ -93,3 +93,30 @@ test('slots', async () => { `1,234.00` ) }) + +test('component', async () => { + const i18n = createI18n({ + locale: 'en-US', + numberFormats + }) + + const MyComponent = defineComponent({ + setup(props, context: SetupContext) { + return (): VNodeChild => h('span', context.slots.default()) + } + }) + + const App = defineComponent({ + data: () => ({ MyComponent }), + template: ` + + + +` + }) + const wrapper = await mount(App, i18n) + + expect(wrapper.html()).toEqual( + `100$100.00¥100` + ) +}) diff --git a/test/components/Translation.test.ts b/test/components/Translation.test.ts index f08db8b09..187dd6d70 100644 --- a/test/components/Translation.test.ts +++ b/test/components/Translation.test.ts @@ -3,7 +3,7 @@ */ import { mount } from '../helper' -import { defineComponent, ref } from 'vue' +import { h, defineComponent, SetupContext, VNodeChild, ref } from 'vue' import { createI18n, useI18n } from '../../src/i18n' const messages = { @@ -182,3 +182,32 @@ test('scope', async () => { expect(wrapper.html()).toEqual(`this is rootthis is global`) }) + +test('component', async () => { + const i18n = createI18n({ + locale: 'en', + messages + }) + + const MyComponent = defineComponent({ + setup(props, context: SetupContext) { + return (): VNodeChild => h('p', context.slots.default()) + } + }) + + const App = defineComponent({ + data: () => ({ MyComponent }), + template: ` + + + +` + }) + const wrapper = await mount(App, i18n) + + expect(wrapper.html()).toEqual( + `

hello, kazupon!

` + ) +})