Skip to content

Commit 2327771

Browse files
authored
feature: scope prop (#51)
* add scope prop * add unit tests * add log * update
1 parent 8f5fe2e commit 2327771

File tree

10 files changed

+446
-33
lines changed

10 files changed

+446
-33
lines changed

src/components/DatetimeFormat.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ import { useI18n } from '../i18n'
33
import { DateTimeOptions } from '../core'
44
import { Composer, ComposerInternal } from '../composer'
55
import { renderFormatter, FormattableProps } from './formatRenderer'
6+
import { baseFormatProps } from './base'
7+
8+
export type DatetimeFormatProps = FormattableProps<
9+
number | Date,
10+
Intl.DateTimeFormatOptions
11+
>
612

713
const DATETIME_FORMAT_KEYS = [
814
'dateStyle',
@@ -31,18 +37,13 @@ export const DatetimeFormat = defineComponent({
3137
/* eslint-disable */
3238
name: 'i18n-d',
3339
props: {
34-
tag: {
35-
type: String
36-
},
40+
...baseFormatProps,
3741
value: {
3842
type: [Number, Date] as PropType<number | Date>,
3943
required: true
4044
},
4145
format: {
42-
type: [String, Object]
43-
},
44-
locale: {
45-
type: String
46+
type: [String, Object] as PropType<Intl.DateTimeFormatOptions>
4647
}
4748
},
4849
/* eslint-enable */

src/components/NumberFormat.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
import { defineComponent, SetupContext } from 'vue'
1+
import { defineComponent, SetupContext, PropType } from 'vue'
22
import { useI18n } from '../i18n'
33
import { NumberOptions } from '../core'
44
import { Composer, ComposerInternal } from '../composer'
55
import { renderFormatter, FormattableProps } from './formatRenderer'
6+
import { baseFormatProps } from './base'
7+
8+
export type NumberFormatProps = FormattableProps<
9+
number,
10+
Intl.NumberFormatOptions
11+
>
612

713
const NUMBER_FORMAT_KEYS = [
814
'localeMatcher',
@@ -26,18 +32,13 @@ export const NumberFormat = defineComponent({
2632
/* eslint-disable */
2733
name: 'i18n-n',
2834
props: {
29-
tag: {
30-
type: String
31-
},
35+
...baseFormatProps,
3236
value: {
3337
type: Number,
3438
required: true
3539
},
3640
format: {
37-
type: [String, Object]
38-
},
39-
locale: {
40-
type: String
41+
type: [String, Object] as PropType<Intl.NumberFormatOptions>
4142
}
4243
},
4344
/* eslint-enable */

src/components/Translation.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,25 @@ import {
77
} from 'vue'
88
import { Composer, ComposerInternal } from '../composer'
99
import { useI18n } from '../i18n'
10-
import { TranslateOptions, Locale } from '../core'
10+
import { TranslateOptions } from '../core'
1111
import { NamedValue } from '../message/runtime'
1212
import { isNumber, isString } from '../utils'
13+
import { baseFormatProps, BaseFormatProps } from './base'
1314

14-
export type TranslationProps = {
15-
tag?: string
15+
export interface TranslationProps extends BaseFormatProps {
1616
keypath: string
17-
locale?: Locale
1817
plural?: number | string
1918
}
2019

2120
export const Translation = defineComponent({
2221
/* eslint-disable */
2322
name: 'i18n-t',
2423
props: {
25-
tag: {
26-
type: String
27-
},
24+
...baseFormatProps,
2825
keypath: {
2926
type: String,
3027
required: true
3128
},
32-
locale: {
33-
type: String
34-
},
3529
plural: {
3630
type: [Number, String],
3731
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -41,7 +35,8 @@ export const Translation = defineComponent({
4135
/* eslint-enable */
4236
setup(props: TranslationProps, context: SetupContext) {
4337
const { slots, attrs } = context
44-
const i18n = useI18n({ useScope: 'parent' }) as Composer & ComposerInternal
38+
const i18n = useI18n({ useScope: props.scope }) as Composer &
39+
ComposerInternal
4540
const keys = Object.keys(slots).filter(key => key !== '_')
4641

4742
return () => {

src/components/base.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { PropType } from 'vue'
2+
import { Locale } from '../core'
3+
import { I18nScope } from '../i18n'
4+
5+
export type ComponetI18nScope = Exclude<I18nScope, 'local'>
6+
7+
export interface BaseFormatProps {
8+
tag?: string
9+
locale?: Locale
10+
scope?: ComponetI18nScope
11+
}
12+
13+
export const baseFormatProps = {
14+
tag: {
15+
type: String
16+
},
17+
locale: {
18+
type: String
19+
},
20+
scope: {
21+
type: String as PropType<ComponetI18nScope>,
22+
validator: (val: ComponetI18nScope): boolean =>
23+
val === 'parent' || val === 'global',
24+
default: 'parent' as ComponetI18nScope
25+
}
26+
}

src/components/formatRenderer.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@ import {
55
SetupContext,
66
VNodeArrayChildren
77
} from 'vue'
8-
import { Locale, NumberOptions, DateTimeOptions } from '../core'
8+
import { NumberOptions, DateTimeOptions } from '../core'
99
import { isString, isPlainObject, isArray } from '../utils'
10+
import { BaseFormatProps } from './base'
1011

11-
export type FormattableProps<Value, Format> = {
12-
tag?: string
12+
export interface FormattableProps<Value, Format> extends BaseFormatProps {
1313
value: Value
1414
format?: string | Format
15-
locale?: Locale
1615
}
1716

1817
type FormatOptions = NumberOptions | DateTimeOptions

src/components/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
export { Translation } from './Translation'
2-
export { NumberFormat } from './NumberFormat'
3-
export { DatetimeFormat } from './DatetimeFormat'
1+
export { ComponetI18nScope, BaseFormatProps } from './base'
2+
export { FormattableProps } from './formatRenderer'
3+
export { Translation, TranslationProps } from './Translation'
4+
export { NumberFormat, NumberFormatProps } from './NumberFormat'
5+
export { DatetimeFormat, DatetimeFormatProps } from './DatetimeFormat'

src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ export {
4444
ComposerAdditionalOptions,
4545
UseI18nOptions
4646
} from './i18n'
47+
export {
48+
TranslationProps,
49+
NumberFormatProps,
50+
DatetimeFormatProps,
51+
FormattableProps,
52+
BaseFormatProps,
53+
ComponetI18nScope
54+
} from './components'
4755
export { I18nPluginOptions } from './plugin'
4856

4957
/**
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
4+
5+
import { mount } from '../helper'
6+
import { defineComponent } from 'vue'
7+
import { createI18n } from '../../src/i18n'
8+
9+
const datetimeFormats = {
10+
'en-US': {
11+
long: {
12+
year: 'numeric',
13+
month: '2-digit',
14+
day: '2-digit',
15+
hour: '2-digit',
16+
minute: '2-digit',
17+
second: '2-digit'
18+
}
19+
},
20+
'ja-JP-u-ca-japanese': {
21+
long: {
22+
era: 'long',
23+
year: 'numeric',
24+
month: 'numeric',
25+
day: 'numeric',
26+
hour: 'numeric',
27+
minute: 'numeric',
28+
second: 'numeric',
29+
weekday: 'long',
30+
hour12: true,
31+
timeZoneName: 'long'
32+
}
33+
}
34+
}
35+
36+
let org, spy
37+
beforeEach(() => {
38+
org = console.warn
39+
spy = jest.fn()
40+
console.warn = spy
41+
})
42+
afterEach(() => {
43+
console.warn = org
44+
})
45+
46+
test('basic usage', async () => {
47+
const i18n = createI18n({
48+
locale: 'en-US',
49+
datetimeFormats
50+
})
51+
52+
const App = defineComponent({
53+
template: `
54+
<i18n-d tag="p" :value="new Date()"></i18n-d>
55+
<i18n-d tag="p" :value="new Date()" format="long"></i18n-d>
56+
<i18n-d
57+
tag="p"
58+
:value="new Date()"
59+
format="long"
60+
locale="ja-JP-u-ca-japanese"
61+
></i18n-d>
62+
`
63+
})
64+
const wrapper = await mount(App, i18n)
65+
66+
expect(wrapper.html()).toMatch(
67+
/([1-9]|1[0-2])\/([1-9]|[12]\d|3[01])\/([12]\d{3})/
68+
)
69+
expect(wrapper.html()).toMatch(
70+
/(0[1-9]|1[0-2])\/(0[1-9]|[12]\d|3[01])\/([12]\d{3}), (0[0-9]|1[0-2]):([0-5][0-9]):([0-5][0-9]) (AM|PM)/
71+
)
72+
expect(wrapper.html()).toMatch(
73+
/([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]) (|)/
74+
)
75+
})
76+
77+
test('slots', async () => {
78+
const i18n = createI18n({
79+
locale: 'en-US',
80+
datetimeFormats
81+
})
82+
83+
const App = defineComponent({
84+
template: `
85+
<i18n-d
86+
:value="new Date()"
87+
locale="ja-JP-u-ca-japanese"
88+
:format="{ key: 'long', era: 'narrow' }"
89+
>
90+
<template #era="props"
91+
><span style="color: green;">{{ props.era }}</span></template
92+
>
93+
</i18n-d>
94+
`
95+
})
96+
const wrapper = await mount(App, i18n)
97+
98+
expect(wrapper.html()).toMatch(`<span style="color: green;">R</span>`)
99+
expect(wrapper.html()).toMatch(
100+
/([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]) (|)/
101+
)
102+
})
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* @jest-environment jsdom
3+
*/
4+
5+
import { mount } from '../helper'
6+
import { defineComponent } from 'vue'
7+
import { createI18n } from '../../src/i18n'
8+
9+
const numberFormats = {
10+
'en-US': {
11+
currency: {
12+
style: 'currency',
13+
currency: 'USD',
14+
currencyDisplay: 'symbol'
15+
},
16+
decimal: {
17+
style: 'decimal',
18+
useGrouping: false
19+
}
20+
},
21+
'ja-JP': {
22+
currency: {
23+
style: 'currency',
24+
currency: 'JPY',
25+
currencyDisplay: 'symbol'
26+
},
27+
numeric: {
28+
style: 'decimal',
29+
useGrouping: false
30+
},
31+
percent: {
32+
style: 'percent',
33+
useGrouping: false
34+
}
35+
}
36+
}
37+
38+
let org, spy
39+
beforeEach(() => {
40+
org = console.warn
41+
spy = jest.fn()
42+
console.warn = spy
43+
})
44+
afterEach(() => {
45+
console.warn = org
46+
})
47+
48+
test('basic usage', async () => {
49+
const i18n = createI18n({
50+
locale: 'en-US',
51+
numberFormats
52+
})
53+
54+
const App = defineComponent({
55+
template: `
56+
<i18n-n tag="p" :value="100"></i18n-n>
57+
<i18n-n tag="p" :value="100" format="currency"></i18n-n>
58+
<i18n-n tag="p" :value="100" format="currency" locale="ja-JP"></i18n-n>
59+
`
60+
})
61+
const wrapper = await mount(App, i18n)
62+
63+
expect(wrapper.html()).toEqual(`<p>100</p><p>$100.00</p><p>¥100</p>`)
64+
})
65+
66+
test('slots', async () => {
67+
const i18n = createI18n({
68+
locale: 'en-US',
69+
numberFormats
70+
})
71+
72+
const App = defineComponent({
73+
template: `
74+
<i18n-n :value="1234" :format="{ key: 'currency', currency: 'EUR' }">
75+
<template #currency="props"
76+
><span style="color: green;">{{ props.currency }}</span></template
77+
>
78+
<template #integer="props"
79+
><span style="font-weight: bold;">{{ props.integer }}</span></template
80+
>
81+
<template #group="props"
82+
><span style="font-weight: bold;">{{ props.group }}</span></template
83+
>
84+
<template #fraction="props"
85+
><span style="font-size: small;">{{ props.fraction }}</span></template
86+
>
87+
</i18n-n>
88+
`
89+
})
90+
const wrapper = await mount(App, i18n)
91+
92+
expect(wrapper.html()).toEqual(
93+
`<span style=\"color: green;\">€</span><span style=\"font-weight: bold;\">1</span><span style=\"font-weight: bold;\">,</span><span style=\"font-weight: bold;\">234</span>.<span style=\"font-size: small;\">00</span>`
94+
)
95+
})

0 commit comments

Comments
 (0)