diff --git a/docs/api/injection.md b/docs/api/injection.md index 4f629b59f..ab635b537 100644 --- a/docs/api/injection.md +++ b/docs/api/injection.md @@ -599,6 +599,10 @@ Translation message ### $tc(key) +:::danger NOTICE +**`$tc` has been deprecated in v10. Use `$t` instead. `$tc` is going to remove in v11.** +::: + Locale message pluralization **Signature:** @@ -632,6 +636,10 @@ Translation message that is pluraled ### $tc(key, locale) +:::danger NOTICE +**`$tc` has been deprecated in v10. Use `$t` instead. `$tc` is going to remove in v11.** +::: + Locale message pluralization **Signature:** @@ -659,6 +667,10 @@ Translation message that is pluraled ### $tc(key, list) +:::danger NOTICE +**`$tc` has been deprecated in v10. Use `$t` instead. `$tc` is going to remove in v11.** +::: + Locale message pluralization **Signature:** @@ -686,6 +698,10 @@ Translation message that is pluraled ### $tc(key, named) +:::danger NOTICE +**`$tc` has been deprecated in v10. Use `$t` instead. `$tc` is going to remove in v11.** +::: + Locale message pluralization **Signature:** @@ -713,6 +729,10 @@ Translation message that is pluraled ### $tc(key, choice) +:::danger NOTICE +**`$tc` has been deprecated in v10. Use `$t` instead. `$tc` is going to remove in v11.** +::: + Locale message pluralization **Signature:** @@ -740,6 +760,10 @@ Translation message that is pluraled ### $tc(key, choice, locale) +:::danger NOTICE +**`$tc` has been deprecated in v10. Use `$t` instead. `$tc` is going to remove in v11.** +::: + Locale message pluralization **Signature:** @@ -768,6 +792,10 @@ Translation message that is pluraled ### $tc(key, choice, list) +:::danger NOTICE +**`$tc` has been deprecated in v10. Use `$t` instead. `$tc` is going to remove in v11.** +::: + Locale message pluralization **Signature:** @@ -796,6 +824,10 @@ Translation message that is pluraled ### $tc(key, choice, named) +:::danger NOTICE +**`$tc` has been deprecated in v10. Use `$t` instead. `$tc` is going to remove in v11.** +::: + Locale message pluralization **Signature:** diff --git a/docs/guide/migration/breaking10.md b/docs/guide/migration/breaking10.md index 45429ba3f..235695a07 100644 --- a/docs/guide/migration/breaking10.md +++ b/docs/guide/migration/breaking10.md @@ -197,3 +197,382 @@ const i18n = createI18n({ console.log(i18n.global.t('message.hello', { name: 'dio' }, { locale: 'ja' })) ``` + +### Deprecate `tc` and `$tc` for Legacy API mode + +The following APIs are deprecated in v10: + +- `tc(key: Key | ResourceKeys): TranslateResult;` +- `tc(key: Key | ResourceKeys, locale: Locales | Locale): TranslateResult;` +- `tc(key: Key | ResourceKeys, list: unknown[]): TranslateResult;` +- `tc(key: Key | ResourceKeys, named: Record): TranslateResult;` +- `tc(key: Key | ResourceKeys, choice: number): TranslateResult;` +- `tc(key: Key | ResourceKeys, choice: number, locale: Locales | Locale): TranslateResult;` +- `tc(key: Key | ResourceKeys, choice: number, list: unknown[]): TranslateResult;` +- `tc(key: Key | ResourceKeys, choice: number, named: Record): TranslateResult;` +- `$tc(key: Key): TranslateResult;` +- `$tc(key: Key, locale: Locale): TranslateResult;` +- `$tc(key: Key, list: unknown[]): TranslateResult;` +- `$tc(key: Key, named: Record): TranslateResult;` +- `$tc(key: Key, choice: number): TranslateResult;` +- `$tc(key: Key, choice: number, locale: Locale): TranslateResult;` +- `$tc(key: Key, choice: number, list: unknown[]): TranslateResult;` +- `$tc(key: Key, choice: number, named: Record): TranslateResult;` + +**Reason**: Legacy API mode has `t` and `$t` support plural interfaces, so they can be replaced. + +In v10, `tc` and `$tc` still exist to give benefit migration. These will be dropped completely in v11. + +If you will use them, Vue I18n will output the console warning in your application. + + +#### `tc(key: Key | ResourceKeys): TranslateResult;` + +Vue I18n v9.x: + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.tc('banana')) +``` + +Vue I18n v10 or later: + +use `t(key: Key | ResourceKeys, plural: number): TranslateResult;` + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.t('banana', 1)) +``` + +#### `tc(key: Key | ResourceKeys, locale: Locales | Locale): TranslateResult;` + +Vue I18n v9.x: + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.tc('banana', 'ja')) +``` + +Vue I18n v10 or later: + +use `t(key: Key | ResourceKeys, plural: number, options: TranslateOptions): TranslateResult;` + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.t('banana', 1, { locale: 'ja' })) +``` + +#### `tc(key: Key | ResourceKeys, list: unknown[]): TranslateResult;` + + +Vue I18n v9.x: + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.tc('banana', ['dio'])) +``` + +Vue I18n v10 or later: + +use `t(key: Key | ResourceKeys, list: unknown[], plural: number): TranslateResult;` + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.t('banana', ['dio'], 1)) +``` + +#### `tc(key: Key | ResourceKeys, named: Record): TranslateResult;` + +Vue I18n v9.x: + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.tc('banana', { name: 'dio' })) +``` + +Vue I18n v10 or later: + +use `t(key: Key | ResourceKeys, named: NamedValue, plural: number): TranslateResult;` + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.t('banana', { name: 'dio' }, 1)) +``` + +#### `tc(key: Key | ResourceKeys, choice: number): TranslateResult;` + +Vue I18n v9.x: + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.tc('banana', 2)) +``` + +Vue I18n v10 or later: + +use `t(key: Key | ResourceKeys, plural: number): TranslateResult;` + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.t('banana', 2)) +``` + +#### `tc(key: Key | ResourceKeys, choice: number, locale: Locales | Locale): TranslateResult;` + +Vue I18n v9.x: + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.tc('banana', 2, 'ja')) +``` + +Vue I18n v10 or later: + +use `t(key: Key | ResourceKeys, plural: number, options: TranslateOptions): TranslateResult;` + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.t('banana', 2, { locale: 'ja' })) +``` + +#### `tc(key: Key | ResourceKeys, choice: number, list: unknown[]): TranslateResult;` + +Vue I18n v9.x: + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.t('banana', 2, ['dio'])) +``` + +Vue I18n v10 or later: + +use `t(key: Key | ResourceKeys, list: unknown[], plural: number): TranslateResult;` + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.t('banana', ['dio'], 2)) +``` + +#### `tc(key: Key | ResourceKeys, choice: number, named: Record): TranslateResult;` + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.tc('banana', 2, { name: 'dio' })) +``` + +Vue I18n v10 or later: + +use `t(key: Key | ResourceKeys, named: NamedValue, plural: number): TranslateResult;` + +```ts +const i18n = createI18n({ + legacy: true, + // something options ... +}) +console.log(i18n.global.tc('banana', { name: 'dio' }, 2)) +``` + +#### `$tc(key: Key): TranslateResult;` + +Vue I18n v9.x: + +```vue + +``` + +Vue I18n v10 or later: + +use `$t(key: Key, plural: number): TranslateResult;` + +```vue + +``` + +#### `$tc(key: Key, locale: Locale): TranslateResult;` + +Vue I18n v9.x: + +```vue + +``` + +Vue I18n v10 or later: + +use `$t(key: Key, plural: number, options: TranslateOptions): TranslateResult;` + +```vue + +``` + +#### `$tc(key: Key, list: unknown[]): TranslateResult;` + +Vue I18n v9.x: + +```vue + +``` + +Vue I18n v10 or later: + +use `$t(key: Key, list: unknown[], plural: number): TranslateResult;` + +```vue + +``` + +#### `$tc(key: Key, named: Record): TranslateResult;` + +Vue I18n v9.x: + +```vue + +``` + +Vue I18n v10 or later: + +use `$t(key: Key, named: NamedValue, plural: number): TranslateResult;` + +```vue + +``` + +#### `$tc(key: Key, choice: number): TranslateResult;` + +Vue I18n v9.x: + +```vue + +``` + +Vue I18n v10 or later: + +use `$t(key: Key, plural: number): TranslateResult;` + +```vue + +``` + +#### `$tc(key: Key, choice: number, locale: Locale): TranslateResult;` + +Vue I18n v9.x: + +```vue + +``` + +Vue I18n v10 or later: + +use `$t(key: Key, plural: number, options: TranslateOptions): TranslateResult;` + +```vue + +``` + +#### `$tc(key: Key, choice: number, list: unknown[]): TranslateResult;` + +Vue I18n v9.x: + +```vue + +``` + +Vue I18n v10 or later: + +use `$t(key: Key, list: unknown[], plural: number): TranslateResult;` + +```vue + +``` + +#### `$tc(key: Key, choice: number, named: Record): TranslateResult;` + +Vue I18n v9.x: + +```vue + +``` + +Vue I18n v10 or later: + +use `$t(key: Key, named: NamedValue, plural: number): TranslateResult;` + +```vue + +``` diff --git a/packages/vue-i18n-core/src/legacy.ts b/packages/vue-i18n-core/src/legacy.ts index 6edbf38b6..4fe4f6f88 100644 --- a/packages/vue-i18n-core/src/legacy.ts +++ b/packages/vue-i18n-core/src/legacy.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { createComposer, DefineLocaleMessage } from './composer' import { createI18nError, I18nErrorCodes } from './errors' +import { I18nWarnCodes, getWarnMessage } from './warnings' import { EnableEmitter, DisableEmitter } from './symbols' import { DEFAULT_LOCALE } from '@intlify/core-base' import { @@ -11,7 +12,8 @@ import { isBoolean, isFunction, isRegExp, - assign + assign, + warnOnce } from '@intlify/shared' import type { @@ -1720,6 +1722,10 @@ export function createVueI18n(options: any = {}): any { let list: unknown[] | null = null let named: NamedValue | null = null + if (__DEV__) { + warnOnce(getWarnMessage(I18nWarnCodes.DEPRECATE_TC)) + } + if (!isString(arg1)) { throw createI18nError(I18nErrorCodes.INVALID_ARGUMENT) } diff --git a/packages/vue-i18n-core/src/warnings.ts b/packages/vue-i18n-core/src/warnings.ts index b429dbf12..85964a43e 100644 --- a/packages/vue-i18n-core/src/warnings.ts +++ b/packages/vue-i18n-core/src/warnings.ts @@ -9,7 +9,9 @@ export const I18nWarnCodes = { COMPONENT_NAME_LEGACY_COMPATIBLE: inc(), // 9 NOT_FOUND_PARENT_SCOPE: inc(), // 10 IGNORE_OBJ_FLATTEN: inc(), // 11 - NOTICE_DROP_TRANSLATE_EXIST_COMPATIBLE_FLAG: inc() // 12 + NOTICE_DROP_TRANSLATE_EXIST_COMPATIBLE_FLAG: inc(), // 12 + DEPRECATE_TC: inc(), // 13 + __EXTEND_POINT__: inc() // 14 } as const type I18nWarnCodes = (typeof I18nWarnCodes)[keyof typeof I18nWarnCodes] @@ -19,7 +21,8 @@ export const warnMessages: { [code: number]: string } = { [I18nWarnCodes.COMPONENT_NAME_LEGACY_COMPATIBLE]: `Component name legacy compatible: '{name}' -> 'i18n'`, [I18nWarnCodes.NOT_FOUND_PARENT_SCOPE]: `Not found parent scope. use the global scope.`, [I18nWarnCodes.IGNORE_OBJ_FLATTEN]: `Ignore object flatten: '{key}' key has an string value`, - [I18nWarnCodes.NOTICE_DROP_TRANSLATE_EXIST_COMPATIBLE_FLAG]: `'translateExistCompatible' option will be dropped in the next major version.` + [I18nWarnCodes.NOTICE_DROP_TRANSLATE_EXIST_COMPATIBLE_FLAG]: `'translateExistCompatible' option will be dropped in the next major version.`, + [I18nWarnCodes.DEPRECATE_TC]: `'tc' and '$tc' has been deprecated in v10. Use 't' or '$t' instead. 'tc' and '$tc’ are going to remove in v11.` } export function getWarnMessage( diff --git a/packages/vue-i18n-core/test/i18n.test.ts b/packages/vue-i18n-core/test/i18n.test.ts index e40dfcbc3..5dc87e8f3 100644 --- a/packages/vue-i18n-core/test/i18n.test.ts +++ b/packages/vue-i18n-core/test/i18n.test.ts @@ -8,7 +8,8 @@ vi.mock('@intlify/shared', async () => { const actual = await vi.importActual('@intlify/shared') return { ...actual, - warn: vi.fn() + warn: vi.fn(), + warnOnce: vi.fn() } }) @@ -1036,6 +1037,10 @@ describe('merge i18n custom blocks to global scope', () => { }) describe('custom pluralization', () => { + const mockWarn = vi.spyOn(shared, 'warnOnce') + // eslint-disable-next-line @typescript-eslint/no-empty-function + mockWarn.mockImplementation(() => {}) + test('legacy', async () => { const i18n = createI18n({ locale: 'ru', diff --git a/packages/vue-i18n-core/test/legacy.test.ts b/packages/vue-i18n-core/test/legacy.test.ts index 1c1436238..7236446c6 100644 --- a/packages/vue-i18n-core/test/legacy.test.ts +++ b/packages/vue-i18n-core/test/legacy.test.ts @@ -6,7 +6,8 @@ vi.mock('@intlify/shared', async () => { const actual = await vi.importActual('@intlify/shared') return { ...actual, - warn: vi.fn() + warn: vi.fn(), + warnOnce: vi.fn() } }) @@ -117,6 +118,10 @@ test('postTranslation', () => { }) test('pluralizationRules', () => { + const mockWarn = vi.spyOn(shared, 'warnOnce') + // eslint-disable-next-line @typescript-eslint/no-empty-function + mockWarn.mockImplementation(() => {}) + const i18n = createVueI18n({ locale: 'ru', pluralizationRules: _pluralRules, @@ -133,6 +138,8 @@ test('pluralizationRules', () => { expect(i18n.tc('car', 4)).toEqual('4 машины') expect(i18n.tc('car', 12)).toEqual('12 машин') expect(i18n.tc('car', 21)).toEqual('21 машина') + + expect(mockWarn).toHaveBeenCalled() }) test('messages', () => { diff --git a/packages/vue-i18n-core/test/mixin.test.ts b/packages/vue-i18n-core/test/mixin.test.ts index c5160ad43..04876588c 100644 --- a/packages/vue-i18n-core/test/mixin.test.ts +++ b/packages/vue-i18n-core/test/mixin.test.ts @@ -2,6 +2,16 @@ * @vitest-environment jsdom */ +// utils +import * as shared from '@intlify/shared' +vi.mock('@intlify/shared', async () => { + const actual = await vi.importActual('@intlify/shared') + return { + ...actual, + warnOnce: vi.fn() + } +}) + import { mount } from './helper' import { defineComponent, nextTick } from 'vue' import { @@ -126,6 +136,10 @@ test('$rt', async () => { }) test('$tc', async () => { + const mockWarn = vi.spyOn(shared, 'warnOnce') + // eslint-disable-next-line @typescript-eslint/no-empty-function + mockWarn.mockImplementation(() => {}) + const i18n = createI18n({ legacy: true, locale: 'en', @@ -140,6 +154,7 @@ test('$tc', async () => { const { vm } = await mount(App, i18n) expect(vm.$tc!('banana', 2)).toEqual('2 bananas') + expect(mockWarn).toHaveBeenCalled() }) test('$te', async () => { diff --git a/packages/vue-i18n-core/test/warnings.test.ts b/packages/vue-i18n-core/test/warnings.test.ts index 4a0a243d2..dc7835e55 100644 --- a/packages/vue-i18n-core/test/warnings.test.ts +++ b/packages/vue-i18n-core/test/warnings.test.ts @@ -1,5 +1,5 @@ import { I18nWarnCodes } from '../src/warnings' test('I18nWarnCodes', () => { - expect(I18nWarnCodes.NOTICE_DROP_TRANSLATE_EXIST_COMPATIBLE_FLAG).toBe(12) + expect(I18nWarnCodes.__EXTEND_POINT__).toBe(14) })