Skip to content

Commit 74e9b2d

Browse files
authored
fix: deprecate named interpolation with modulo syntax (#1795)
1 parent f7686dd commit 74e9b2d

File tree

19 files changed

+332
-82
lines changed

19 files changed

+332
-82
lines changed

packages/core-base/src/compilation.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { warn, format, isObject, isBoolean, isString } from '@intlify/shared'
22
import {
33
baseCompile as baseCompileCore,
4+
CompileWarnCodes,
45
defaultOnError,
56
detectHtmlTag
67
} from '@intlify/message-compiler'
@@ -11,7 +12,8 @@ import type {
1112
CompileOptions,
1213
CompileError,
1314
CompilerResult,
14-
ResourceNode
15+
ResourceNode,
16+
CompileWarn
1517
} from '@intlify/message-compiler'
1618
import type { MessageFunction, MessageFunctions } from './runtime'
1719
import type { MessageCompilerContext } from './context'
@@ -27,6 +29,17 @@ function checkHtmlMessage(source: string, warnHtmlMessage?: boolean): void {
2729
const defaultOnCacheKey = (message: string): string => message
2830
let compileCache: unknown = Object.create(null)
2931

32+
function onCompileWarn(_warn: CompileWarn): void {
33+
if (_warn.code === CompileWarnCodes.USE_MODULO_SYNTAX) {
34+
warn(
35+
`The use of named interpolation with modulo syntax is deprecated. ` +
36+
`It will be removed in v10.\n` +
37+
`reference: https://vue-i18n.intlify.dev/guide/essentials/syntax#rails-i18n-format \n` +
38+
`(message compiler warning message: ${_warn.message})`
39+
)
40+
}
41+
}
42+
3043
export function clearCompileCache(): void {
3144
compileCache = Object.create(null)
3245
}
@@ -64,6 +77,11 @@ export const compileToFunction = <
6477
throw createCoreError(CoreErrorCodes.NOT_SUPPORT_NON_STRING_MESSAGE)
6578
}
6679

80+
// set onWarn
81+
if (__DEV__) {
82+
context.onWarn = onCompileWarn
83+
}
84+
6785
if (__RUNTIME__) {
6886
__DEV__ &&
6987
warn(
@@ -107,6 +125,11 @@ export function compile<
107125
message: MessageSource,
108126
context: MessageCompilerContext
109127
): MessageFunction<Message> {
128+
// set onWarn
129+
if (__DEV__) {
130+
context.onWarn = onCompileWarn
131+
}
132+
110133
if (
111134
(__ESM_BROWSER__ ||
112135
__NODE_JS__ ||

packages/core-base/src/context.ts

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@ export type LocaleMessageValue<Message = string> =
6666
export type LocaleMessageType<T, Message = string> = T extends string
6767
? string
6868
: T extends () => Promise<infer P>
69-
? LocaleMessageDictionary<P, Message>
70-
: T extends (...args: infer Arguments) => any
71-
? (...args: Arguments) => ReturnType<T>
72-
: T extends Record<string, unknown>
73-
? LocaleMessageDictionary<T, Message>
74-
: T extends Array<T>
75-
? { [K in keyof T]: T[K] }
76-
: T
69+
? LocaleMessageDictionary<P, Message>
70+
: T extends (...args: infer Arguments) => any
71+
? (...args: Arguments) => ReturnType<T>
72+
: T extends Record<string, unknown>
73+
? LocaleMessageDictionary<T, Message>
74+
: T extends Array<T>
75+
? { [K in keyof T]: T[K] }
76+
: T
7777

7878
/** @VueI18nGeneral */
7979
export type LocaleMessageDictionary<T, Message = string> = {
@@ -145,7 +145,7 @@ export type PostTranslationHandler<Message = string> = (
145145
*/
146146
export type MessageCompilerContext = Pick<
147147
CompileOptions,
148-
'onError' | 'onCacheKey'
148+
'onError' | 'onCacheKey' | 'onWarn'
149149
> & {
150150
/**
151151
* Whether to allow the use locale messages of HTML formatting.
@@ -183,37 +183,37 @@ export type MessageCompiler<
183183
export interface CoreOptions<
184184
Message = string,
185185
Schema extends
186-
{
187-
message?: unknown
188-
datetime?: unknown
189-
number?: unknown
190-
} = {
191-
message: DefaultCoreLocaleMessageSchema,
192-
datetime: DateTimeFormat,
193-
number: NumberFormat
194-
},
186+
{
187+
message?: unknown
188+
datetime?: unknown
189+
number?: unknown
190+
} = {
191+
message: DefaultCoreLocaleMessageSchema,
192+
datetime: DateTimeFormat,
193+
number: NumberFormat
194+
},
195195
Locales extends
196-
| {
197-
messages: unknown
198-
datetimeFormats: unknown
199-
numberFormats: unknown
200-
}
201-
| string = Locale,
196+
| {
197+
messages: unknown
198+
datetimeFormats: unknown
199+
numberFormats: unknown
200+
}
201+
| string = Locale,
202202
MessagesLocales = Locales extends { messages: infer M }
203-
? M
204-
: Locales extends string
205-
? Locales
206-
: Locale,
203+
? M
204+
: Locales extends string
205+
? Locales
206+
: Locale,
207207
DateTimeFormatsLocales = Locales extends { datetimeFormats: infer D }
208-
? D
209-
: Locales extends string
210-
? Locales
211-
: Locale,
208+
? D
209+
: Locales extends string
210+
? Locales
211+
: Locale,
212212
NumberFormatsLocales = Locales extends { numberFormats: infer N }
213-
? N
214-
: Locales extends string
215-
? Locales
216-
: Locale,
213+
? N
214+
: Locales extends string
215+
? Locales
216+
: Locale,
217217
MessageSchema = Schema extends { message: infer M } ? M : DefaultCoreLocaleMessageSchema,
218218
DateTimeSchema = Schema extends { datetime: infer D } ? D : DateTimeFormat,
219219
NumberSchema = Schema extends { number: infer N } ? N : NumberFormat,
@@ -300,14 +300,14 @@ export type CoreContext<
300300
NumberFormats = {},
301301
LocaleType = Locale,
302302
ResourceLocales =
303-
| PickupLocales<NonNullable<Messages>>
304-
| PickupLocales<NonNullable<DateTimeFormats>>
305-
| PickupLocales<NonNullable<NumberFormats>>,
303+
| PickupLocales<NonNullable<Messages>>
304+
| PickupLocales<NonNullable<DateTimeFormats>>
305+
| PickupLocales<NonNullable<NumberFormats>>,
306306
Locales = IsNever<ResourceLocales> extends true
307-
? LocaleType extends LocaleDetector | Locale
308-
? LocaleType
309-
: Locale
310-
: ResourceLocales
307+
? LocaleType extends LocaleDetector | Locale
308+
? LocaleType
309+
: Locale
310+
: ResourceLocales
311311
> = CoreCommonContext<Message, Locales> &
312312
CoreTranslationContext<NonNullable<Messages>, Message> &
313313
CoreDateTimeContext<NonNullable<DateTimeFormats>> &
@@ -355,7 +355,7 @@ function getDefaultLinkedModifiers<
355355
return type === 'text' && isString(val)
356356
? val.toUpperCase()
357357
: type === 'vnode' && isObject(val) && '__v_isVNode' in val
358-
? (val as any).children.toUpperCase()
358+
? (val as any).children.toUpperCase()
359359
: val
360360
},
361361
lower: (val: Message, type: string): MessageType<Message> => {
@@ -371,7 +371,7 @@ function getDefaultLinkedModifiers<
371371
return (type === 'text' && isString(val)
372372
? capitalize(val)
373373
: type === 'vnode' && isObject(val) && '__v_isVNode' in val
374-
? capitalize( (val as any).children)
374+
? capitalize((val as any).children)
375375
: val) as MessageType<Message>
376376
}
377377
}

packages/core-base/src/errors.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ const code = CompileErrorCodes.__EXTEND_POINT__
1212
const inc = incrementer(code)
1313

1414
export const CoreErrorCodes = {
15-
INVALID_ARGUMENT: code, // 18
16-
INVALID_DATE_ARGUMENT: inc(), // 19
17-
INVALID_ISO_DATE_ARGUMENT: inc(), // 20
18-
NOT_SUPPORT_NON_STRING_MESSAGE: inc(), // 21
19-
NOT_SUPPORT_LOCALE_PROMISE_VALUE: inc(), // 22
20-
NOT_SUPPORT_LOCALE_ASYNC_FUNCTION: inc(), // 23
21-
NOT_SUPPORT_LOCALE_TYPE: inc(), // 24
22-
__EXTEND_POINT__: inc() // 25
15+
INVALID_ARGUMENT: code, // 17
16+
INVALID_DATE_ARGUMENT: inc(), // 18
17+
INVALID_ISO_DATE_ARGUMENT: inc(), // 19
18+
NOT_SUPPORT_NON_STRING_MESSAGE: inc(), // 20
19+
NOT_SUPPORT_LOCALE_PROMISE_VALUE: inc(), // 21
20+
NOT_SUPPORT_LOCALE_ASYNC_FUNCTION: inc(), // 22
21+
NOT_SUPPORT_LOCALE_TYPE: inc(), // 23
22+
__EXTEND_POINT__: inc() // 24
2323
} as const
2424

2525
export type CoreErrorCodes =

packages/core-base/src/warnings.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
import { format } from '@intlify/shared'
1+
import { format, incrementer } from '@intlify/shared'
2+
import { CompileWarnCodes } from '@intlify/message-compiler'
3+
4+
const code = CompileWarnCodes.__EXTEND_POINT__
5+
const inc = incrementer(code)
26

37
export const CoreWarnCodes = {
4-
NOT_FOUND_KEY: 1,
5-
FALLBACK_TO_TRANSLATE: 2,
6-
CANNOT_FORMAT_NUMBER: 3,
7-
FALLBACK_TO_NUMBER_FORMAT: 4,
8-
CANNOT_FORMAT_DATE: 5,
9-
FALLBACK_TO_DATE_FORMAT: 6,
10-
EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER: 7,
11-
__EXTEND_POINT__: 8
8+
NOT_FOUND_KEY: code, // 2
9+
FALLBACK_TO_TRANSLATE: inc(), // 3
10+
CANNOT_FORMAT_NUMBER: inc(), // 4
11+
FALLBACK_TO_NUMBER_FORMAT: inc(), // 5
12+
CANNOT_FORMAT_DATE: inc(), // 6
13+
FALLBACK_TO_DATE_FORMAT: inc(), // 7
14+
EXPERIMENTAL_CUSTOM_MESSAGE_COMPILER: inc(), // 8
15+
__EXTEND_POINT__: inc() // 9
1216
} as const
1317

1418
export type CoreWarnCodes = (typeof CoreWarnCodes)[keyof typeof CoreWarnCodes]

packages/core-base/test/compilation.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
// utils
2+
import * as shared from '@intlify/shared'
3+
vi.mock('@intlify/shared', async () => {
4+
const actual = await vi.importActual<object>('@intlify/shared')
5+
return {
6+
...actual,
7+
warn: vi.fn()
8+
}
9+
})
10+
111
import { baseCompile } from '@intlify/message-compiler'
212
import {
313
compileToFunction,
@@ -9,7 +19,7 @@ import { createMessageContext as context } from '../src/runtime'
919

1020
const DEFAULT_CONTEXT = { locale: 'en', key: 'key' }
1121

12-
beforeAll(() => {
22+
beforeEach(() => {
1323
clearCompileCache()
1424
})
1525

@@ -50,6 +60,19 @@ describe('compileToFunction', () => {
5060
})
5161
expect(occured).toBe(true)
5262
})
63+
64+
test('modulo syntax warning', () => {
65+
const mockWarn = vi.spyOn(shared, 'warn')
66+
mockWarn.mockImplementation(() => {})
67+
68+
compileToFunction('hello %{name}!', {
69+
...DEFAULT_CONTEXT
70+
})
71+
expect(mockWarn).toHaveBeenCalledTimes(1)
72+
expect(mockWarn.mock.calls[0][0]).includes(
73+
`The use of named interpolation with modulo syntax is deprecated. It will be removed in v10.`
74+
)
75+
})
5376
})
5477

5578
describe('compile', () => {
@@ -109,4 +132,15 @@ describe('compile', () => {
109132
})
110133
expect(occured).toBe(true)
111134
})
135+
136+
test('modulo syntax warning', () => {
137+
const mockWarn = vi.spyOn(shared, 'warn')
138+
mockWarn.mockImplementation(() => {})
139+
140+
compile('%{msg} world!', DEFAULT_CONTEXT)
141+
expect(mockWarn).toHaveBeenCalledTimes(1)
142+
expect(mockWarn.mock.calls[0][0]).includes(
143+
`The use of named interpolation with modulo syntax is deprecated. It will be removed in v10.`
144+
)
145+
})
112146
})
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { CoreErrorCodes } from '../src/errors'
2+
3+
test('CoreErrorCodes', () => {
4+
expect(CoreErrorCodes.INVALID_ARGUMENT).toBe(17)
5+
})
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { CoreWarnCodes } from '../src/warnings'
2+
3+
test('CoreWarnCodes', () => {
4+
expect(CoreWarnCodes.NOT_FOUND_KEY).toBe(2)
5+
})

packages/message-compiler/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './location'
22
export * from './nodes'
33
export * from './options'
4+
export * from './warnings'
45
export * from './errors'
56
export * from './helpers'
67
export * from './parser'

packages/message-compiler/src/nodes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export interface TextNode extends Node {
8080
export interface NamedNode extends Node {
8181
type: NodeTypes.Named
8282
key: Identifier
83+
modulo?: boolean
8384
/**
8485
* @internal `key` alias
8586
*/

packages/message-compiler/src/options.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type { CompileError } from './errors'
2+
import type { CompileWarn } from './warnings'
23

4+
export type CompileWarnHandler = (warn: CompileWarn) => void
35
export type CompileErrorHandler = (error: CompileError) => void
46
export type CacheKeyHandler = (source: string) => string
57

@@ -11,10 +13,12 @@ export interface TokenizeOptions {
1113
export interface ParserOptions {
1214
location?: boolean // default true
1315
onCacheKey?: (source: string) => string
16+
onWarn?: CompileWarnHandler
1417
onError?: CompileErrorHandler
1518
}
1619

1720
export interface TransformOptions {
21+
onWarn?: CompileWarnHandler
1822
onError?: CompileErrorHandler
1923
}
2024

@@ -23,6 +27,7 @@ export interface CodeGenOptions {
2327
mode?: 'normal' | 'arrow' // default normal
2428
breakLineCode?: '\n' | ';' // default newline
2529
needIndent?: boolean // default true
30+
onWarn?: CompileWarnHandler
2631
onError?: CompileErrorHandler
2732
// Generate source map?
2833
// - Default: false

0 commit comments

Comments
 (0)