From 0999dd8f14af481f02fef2a6e5cfff8016f60f04 Mon Sep 17 00:00:00 2001 From: Paritosh Maurya Date: Sat, 26 Apr 2025 22:16:30 +0530 Subject: [PATCH 1/4] Added support for part option in Added test cases for the same --- packages/vue-i18n-core/src/composer.ts | 12 +-- .../vue-i18n-core/test/composer.test-d.ts | 14 ++-- packages/vue-i18n-core/test/composer.test.ts | 77 +++++++++++++++++++ 3 files changed, 90 insertions(+), 13 deletions(-) diff --git a/packages/vue-i18n-core/src/composer.ts b/packages/vue-i18n-core/src/composer.ts index f696e53aa..8256be2bc 100644 --- a/packages/vue-i18n-core/src/composer.ts +++ b/packages/vue-i18n-core/src/composer.ts @@ -1207,7 +1207,7 @@ export interface ComposerNumberFormatting< | Key | ResourceKeys | NumberOptions - ): string + ): string | Intl.NumberFormatPart[] /** * Number Formatting * @@ -1229,7 +1229,7 @@ export interface ComposerNumberFormatting< | ResourceKeys | NumberOptions, locale: Locales - ): string + ): string | Intl.NumberFormatPart[] } /** @@ -2290,14 +2290,14 @@ export function createComposer(options: any = {}): any { } // n - function n(...args: unknown[]): string { - return wrapWithDeps<{}, string>( - context => Reflect.apply(number, null, [context, ...args]) as string, + function n(...args: unknown[]): string | Intl.NumberFormatPart[] { + return wrapWithDeps<{}, string | Intl.NumberFormatPart[]>( + context => Reflect.apply(number, null, [context, ...args]), () => parseNumberArgs(...args), 'number format', root => Reflect.apply(root.n, root, [...args]), () => MISSING_RESOLVE_VALUE, - val => isString(val) + val => isString(val) || isArray(val) ) } diff --git a/packages/vue-i18n-core/test/composer.test-d.ts b/packages/vue-i18n-core/test/composer.test-d.ts index 1e05f5461..97d0f156a 100644 --- a/packages/vue-i18n-core/test/composer.test-d.ts +++ b/packages/vue-i18n-core/test/composer.test-d.ts @@ -353,15 +353,15 @@ test('strict composer with direct options', () => { strictDirectComposer.d(new Date(), 'custom' as any) ).toEqualTypeOf() expectTypeOf(strictDirectComposer.n(1)).toEqualTypeOf() - expectTypeOf( - strictDirectComposer.n(1, 'currency', 'zh') - ).toEqualTypeOf() + expectTypeOf(strictDirectComposer.n(1, 'currency', 'zh')).toEqualTypeOf< + string | Intl.NumberFormatPart[] + >() expectTypeOf( strictDirectComposer.n(1, { key: 'currency', locale: 'en' }) - ).toEqualTypeOf() - expectTypeOf( - strictDirectComposer.n(1, 'custom' as any) - ).toEqualTypeOf() + ).toEqualTypeOf() + expectTypeOf(strictDirectComposer.n(1, 'custom' as any)).toEqualTypeOf< + string | Intl.NumberFormatPart[] + >() // const noOptionsComposer = createComposer({ missingWarn: true }) const noOptionsComposer = createComposer({ locale: 'en' }) diff --git a/packages/vue-i18n-core/test/composer.test.ts b/packages/vue-i18n-core/test/composer.test.ts index 59ff15c30..74f541098 100644 --- a/packages/vue-i18n-core/test/composer.test.ts +++ b/packages/vue-i18n-core/test/composer.test.ts @@ -1213,6 +1213,83 @@ describe('n', () => { }) expect(n(0.99, { key: 'percent' })).toEqual('') }) + + test('part formating with n', () => { + const { n } = createComposer({ + locale: 'en-US', + fallbackLocale: ['ja-JP'], + numberFormats: { + 'en-US': { + currency: { + style: 'currency', + currency: 'USD', + currencyDisplay: 'symbol' + }, + decimal: { + style: 'decimal', + useGrouping: true + } + }, + 'ja-JP': { + currency: { + style: 'currency', + currency: 'JPY' /*, currencyDisplay: 'symbol'*/ + }, + numeric: { + style: 'decimal', + useGrouping: false + }, + percent: { + style: 'percent', + useGrouping: true + } + } + } + }) + expect(n(0.99, { key: 'currency', part: true })).toEqual([ + { type: 'currency', value: '$' }, + { type: 'integer', value: '0' }, + { type: 'decimal', value: '.' }, + { type: 'fraction', value: '99' } + ]) + expect( + n(10100, { + key: 'currency', + locale: 'ja-JP', + currency: 'EUR', + part: true + }) + ).toEqual([ + { type: 'currency', value: '€' }, + { type: 'integer', value: '10' }, + { type: 'group', value: ',' }, + { type: 'integer', value: '100' }, + { type: 'decimal', value: '.' }, + { type: 'fraction', value: '00' } + ]) + // expect(n(12145281111, 'decimal', 'ja-JP')).toEqual([]) + expect(n(12145281000, { key: 'percent', part: true })).toEqual([ + { type: 'integer', value: '1' }, + { type: 'group', value: ',' }, + { type: 'integer', value: '214' }, + { type: 'group', value: ',' }, + { type: 'integer', value: '528' }, + { type: 'group', value: ',' }, + { type: 'integer', value: '100' }, + { type: 'group', value: ',' }, + { type: 'integer', value: '000' }, + { type: 'percentSign', value: '%' } + ]) + expect(n(12145281111, { key: 'decimal', part: true })).toEqual([ + { type: 'integer', value: '12' }, + { type: 'group', value: ',' }, + { type: 'integer', value: '145' }, + { type: 'group', value: ',' }, + { type: 'integer', value: '281' }, + { type: 'group', value: ',' }, + { type: 'integer', value: '111' } + ]) + }) }) describe('tm', () => { From feb8aab1c7a16b626ce7c77260a7021e6161ac57 Mon Sep 17 00:00:00 2001 From: Paritosh Maurya Date: Sun, 27 Apr 2025 19:23:44 +0530 Subject: [PATCH 2/4] Updated function defination for --- packages/vue-i18n/src/vue.d.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/vue-i18n/src/vue.d.ts b/packages/vue-i18n/src/vue.d.ts index c3a53f405..2fe1a8722 100644 --- a/packages/vue-i18n/src/vue.d.ts +++ b/packages/vue-i18n/src/vue.d.ts @@ -846,7 +846,10 @@ declare module 'vue' { * * @returns formatted value */ - $n(value: number, options: NumberOptions): string + $n( + value: number, + options: OptionsType + ): OptionsType['part'] extends true ? Intl.NumberFormatPart[] : string /** * Locale messages getter * From 7106971660e3a20b9ff4c20e9b2c2292ab5b4d68 Mon Sep 17 00:00:00 2001 From: Paritosh Maurya Date: Wed, 30 Apr 2025 20:57:53 +0530 Subject: [PATCH 3/4] Added support for part option in dates formatting --- packages/vue-i18n-core/src/composer.ts | 31 ++++++-- .../vue-i18n-core/test/composer.test-d.ts | 16 +++- packages/vue-i18n-core/test/composer.test.ts | 77 +++++++++++++++++++ packages/vue-i18n/src/vue.d.ts | 19 ++++- 4 files changed, 131 insertions(+), 12 deletions(-) diff --git a/packages/vue-i18n-core/src/composer.ts b/packages/vue-i18n-core/src/composer.ts index 93e00dae2..19005b320 100644 --- a/packages/vue-i18n-core/src/composer.ts +++ b/packages/vue-i18n-core/src/composer.ts @@ -1111,13 +1111,19 @@ export interface ComposerDateTimeFormatting< * * @returns Formatted value */ - ( + < + Value extends number | Date | string = number, + Key extends string = string, + Return extends string | Intl.DateTimeFormatPart[] = + | string + | Intl.DateTimeFormatPart[] + >( value: Value, keyOrOptions: | Key | ResourceKeys | DateTimeOptions - ): string + ): Return /** * Datetime formatting * @@ -1132,14 +1138,20 @@ export interface ComposerDateTimeFormatting< * * @returns Formatted value */ - ( + < + Value extends number | Date | string = number, + Key extends string = string, + Return extends string | Intl.DateTimeFormatPart[] = + | string + | Intl.DateTimeFormatPart[] + >( value: Value, keyOrOptions: | Key | ResourceKeys | DateTimeOptions, locale: Locales - ): string + ): Return } /** @@ -2288,14 +2300,17 @@ export function createComposer(options: any = {}): any { } // d - function d(...args: unknown[]): string { - return wrapWithDeps<{}, string>( - context => Reflect.apply(datetime, null, [context, ...args]) as string, + function d(...args: unknown[]): string | Intl.DateTimeFormatPart[] { + return wrapWithDeps<{}, string | Intl.DateTimeFormatPart[]>( + context => + Reflect.apply(datetime, null, [context, ...args]) as + | string + | Intl.DateTimeFormatPart[], () => parseDateTimeArgs(...args), 'datetime format', root => Reflect.apply(root.d, root, [...args]), () => MISSING_RESOLVE_VALUE, - val => isString(val) + val => isString(val) || isArray(val) ) } diff --git a/packages/vue-i18n-core/test/composer.test-d.ts b/packages/vue-i18n-core/test/composer.test-d.ts index bc4056424..581395542 100644 --- a/packages/vue-i18n-core/test/composer.test-d.ts +++ b/packages/vue-i18n-core/test/composer.test-d.ts @@ -344,13 +344,23 @@ test('strict composer with direct options', () => { }>() expectTypeOf(strictDirectComposer.d(new Date())).toEqualTypeOf() expectTypeOf( - strictDirectComposer.d(new Date(), 'short', 'ja-JP') + strictDirectComposer.d(new Date(), 'short', 'ja-JP') ).toEqualTypeOf() expectTypeOf( strictDirectComposer.d(new Date(), { key: 'short', locale: 'zh' }) - ).toEqualTypeOf() + ).toEqualTypeOf() + expectTypeOf( + strictDirectComposer.d( + new Date(), + { + key: 'short', + locale: 'zh', + part: true + } + ) + ).toEqualTypeOf() expectTypeOf( - strictDirectComposer.d(new Date(), 'custom' as any) + strictDirectComposer.d(new Date(), 'custom' as any) ).toEqualTypeOf() expectTypeOf(strictDirectComposer.n(1)).toEqualTypeOf() expectTypeOf(strictDirectComposer.n(1, 'currency', 'zh')).toEqualTypeOf< diff --git a/packages/vue-i18n-core/test/composer.test.ts b/packages/vue-i18n-core/test/composer.test.ts index b7a712bf8..53587db76 100644 --- a/packages/vue-i18n-core/test/composer.test.ts +++ b/packages/vue-i18n-core/test/composer.test.ts @@ -1058,6 +1058,83 @@ describe('d', () => { '12/20/2012, 07:00 AM' ) }) + + test.only('parts formatting', () => { + const { d } = createComposer({ + locale: 'en-US', + datetimeFormats: { + 'en-US': { + short: { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + timeZone: 'America/New_York' + } + }, + 'ja-JP': { + long: { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + timeZone: 'America/New_York' + }, + short: { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + timeZone: 'Asia/Tokyo' + } + } + } + }) + const dt = new Date(2015, 12) + expect(d(dt, { key: 'short', part: true, year: '2-digit' })).toEqual([ + { type: 'month', value: '12' }, + { type: 'literal', value: '/' }, + { type: 'day', value: '31' }, + { type: 'literal', value: '/' }, + { type: 'year', value: '15' }, + { type: 'literal', value: ', ' }, + { type: 'hour', value: '07' }, + { type: 'literal', value: ':' }, + { type: 'minute', value: '00' }, + { type: 'literal', value: ' ' }, + { type: 'dayPeriod', value: 'PM' } + ]) + expect(d(dt, { key: 'short', locale: 'en-US', part: true })).toEqual([ + { type: 'month', value: '12' }, + { type: 'literal', value: '/' }, + { type: 'day', value: '31' }, + { type: 'literal', value: '/' }, + { type: 'year', value: '2015' }, + { type: 'literal', value: ', ' }, + { type: 'hour', value: '07' }, + { type: 'literal', value: ':' }, + { type: 'minute', value: '00' }, + { type: 'literal', value: ' ' }, + { type: 'dayPeriod', value: 'PM' } + ]) + expect(d(dt, { key: 'long', locale: 'ja-JP', part: true })).toEqual([ + { type: 'year', value: '2015' }, + { type: 'literal', value: '/' }, + { type: 'month', value: '12' }, + { type: 'literal', value: '/' }, + { type: 'day', value: '31' }, + { type: 'literal', value: ' ' }, + { type: 'hour', value: '19' }, + { type: 'literal', value: ':' }, + { type: 'minute', value: '00' }, + { type: 'literal', value: ':' }, + { type: 'second', value: '00' } + ]) + }) }) describe('n', () => { diff --git a/packages/vue-i18n/src/vue.d.ts b/packages/vue-i18n/src/vue.d.ts index 5b9eb26fb..45b05c56a 100644 --- a/packages/vue-i18n/src/vue.d.ts +++ b/packages/vue-i18n/src/vue.d.ts @@ -653,7 +653,24 @@ declare module 'vue' { * * @returns formatted value */ - $d(value: number | Date, options: DateTimeOptions): string + $d< + Value extends number | Date = number, + Key extends string = string, + Return extends string | Intl.DateTimeFormatPart[] = + | string + | Intl.DateTimeFormatPart[], + DefinedDateTimeFormat extends + RemovedIndexResources = RemovedIndexResources, + Keys = IsEmptyObject extends false + ? PickupFormatPathKeys<{ + [K in keyof DefinedDateTimeFormat]: DefinedDateTimeFormat[K] + }> + : never, + ResourceKeys extends Keys = IsNever extends false ? Keys : never + >( + value: Value, + options: DateTimeOptions + ): Return /** * Number formatting * From d0143e7048e81be23d71e62818c44f16dbbdbb9a Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Thu, 1 May 2025 11:16:55 +0900 Subject: [PATCH 4/4] Update packages/vue-i18n-core/test/composer.test.ts --- packages/vue-i18n-core/test/composer.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vue-i18n-core/test/composer.test.ts b/packages/vue-i18n-core/test/composer.test.ts index 53587db76..5c5636aae 100644 --- a/packages/vue-i18n-core/test/composer.test.ts +++ b/packages/vue-i18n-core/test/composer.test.ts @@ -1059,7 +1059,7 @@ describe('d', () => { ) }) - test.only('parts formatting', () => { + test('parts formatting', () => { const { d } = createComposer({ locale: 'en-US', datetimeFormats: {