From 0c4126247ecc8689e6ff8fd082563df4033778c5 Mon Sep 17 00:00:00 2001
From: kazuya kawaguchi <kawakazu80@gmail.com>
Date: Fri, 10 May 2024 17:26:58 +0900
Subject: [PATCH 1/2] feat: add `no-deprecated-tc` rule

---
 docs/rules/index.md                 |  1 +
 docs/rules/no-deprecated-tc.md      | 64 +++++++++++++++++++++++++++++
 lib/index.ts                        |  2 +
 lib/rules/no-deprecated-tc.ts       | 60 +++++++++++++++++++++++++++
 tests/lib/rules/no-deprecated-tc.ts | 38 +++++++++++++++++
 5 files changed, 165 insertions(+)
 create mode 100644 docs/rules/no-deprecated-tc.md
 create mode 100644 lib/rules/no-deprecated-tc.ts
 create mode 100644 tests/lib/rules/no-deprecated-tc.ts

diff --git a/docs/rules/index.md b/docs/rules/index.md
index 80651d6a..eb45a483 100644
--- a/docs/rules/index.md
+++ b/docs/rules/index.md
@@ -25,6 +25,7 @@
 | Rule ID | Description |    |
 |:--------|:------------|:---|
 | [@intlify/vue-i18n/<wbr>key-format-style](./key-format-style.html) | enforce specific casing for localization keys |  |
+| [@intlify/vue-i18n/<wbr>no-deprecated-tc](./no-deprecated-tc.html) | disallow using deprecated `tc` or `$tc` (Deprecated in Vue I18n 10.0.0, removed fully in Vue I18n 11.0.0) |  |
 | [@intlify/vue-i18n/<wbr>no-duplicate-keys-in-locale](./no-duplicate-keys-in-locale.html) | disallow duplicate localization keys within the same locale |  |
 | [@intlify/vue-i18n/<wbr>no-dynamic-keys](./no-dynamic-keys.html) | disallow localization dynamic keys at localization methods |  |
 | [@intlify/vue-i18n/<wbr>no-missing-keys-in-other-locales](./no-missing-keys-in-other-locales.html) | disallow missing locale message keys in other locales |  |
diff --git a/docs/rules/no-deprecated-tc.md b/docs/rules/no-deprecated-tc.md
new file mode 100644
index 00000000..a5bc232d
--- /dev/null
+++ b/docs/rules/no-deprecated-tc.md
@@ -0,0 +1,64 @@
+---
+title: '@intlify/vue-i18n/no-deprecated-tc'
+description: disallow using deprecated `tc` or `$tc` (Deprecated in Vue I18n 10.0.0, removed fully in Vue I18n 11.0.0)
+since: v3.0.0
+---
+
+# @intlify/vue-i18n/no-deprecated-tc
+
+> disallow using deprecated `tc` or `$tc` (Deprecated in Vue I18n 10.0.0, removed fully in Vue I18n 11.0.0)
+
+If you are migrating from Vue I18n v9 to v10, `tc` or `$tc` should be replaced with `t` or `$t`.
+
+## :book: Rule Details
+
+This rule reports use of deprecated `tc` or `$tc` (Deprecated in Vue I18n 10.0.0, removed fully in Vue I18n 11.0.0)
+
+:-1: Examples of **incorrect** code for this rule:
+
+<eslint-code-block>
+
+<!-- eslint-skip -->
+
+```vue
+<script>
+/* eslint @intlify/vue-i18n/no-deprecated-tc: 'error' */
+</script>
+<template>
+  <!-- ✗ BAD -->
+  <p>{{ $tc('banana') }}</p>
+</template>
+```
+
+</eslint-code-block>
+
+:+1: Examples of **correct** code for this rule:
+
+<eslint-code-block>
+
+<!-- eslint-skip -->
+
+```vue
+<script>
+/* eslint @intlify/vue-i18n/no-deprecated-tc: 'error' */
+</script>
+<template>
+  <!-- ✓ GOOD -->
+  <p>{{ $t('banana', 1) }}</p>
+</template>
+```
+
+</eslint-code-block>
+
+## :books: Further reading
+
+- [Vue I18n > Breaking Changes in v10 - Deprecate tc and $tc for Legacy API mode](https://vue-i18n.intlify.dev/guide/migration/breaking10.html#deprecate-tc-and-tc-for-legacy-api-mode)
+
+## :rocket: Version
+
+This rule was introduced in `@intlify/eslint-plugin-vue-i18n` v3.0.0
+
+## :mag: Implementation
+
+- [Rule source](https://github.com/intlify/eslint-plugin-vue-i18n/blob/master/lib/rules/no-deprecated-tc.ts)
+- [Test source](https://github.com/intlify/eslint-plugin-vue-i18n/tree/master/tests/lib/rules/no-deprecated-tc.ts)
diff --git a/lib/index.ts b/lib/index.ts
index dbd5a844..3bad70a2 100644
--- a/lib/index.ts
+++ b/lib/index.ts
@@ -11,6 +11,7 @@ import noDeprecatedI18nComponent from './rules/no-deprecated-i18n-component'
 import noDeprecatedI18nPlaceAttr from './rules/no-deprecated-i18n-place-attr'
 import noDeprecatedI18nPlacesProp from './rules/no-deprecated-i18n-places-prop'
 import noDeprecatedModuloSyntax from './rules/no-deprecated-modulo-syntax'
+import noDeprecatedTc from './rules/no-deprecated-tc'
 import noDuplicateKeysInLocale from './rules/no-duplicate-keys-in-locale'
 import noDynamicKeys from './rules/no-dynamic-keys'
 import noHtmlMessages from './rules/no-html-messages'
@@ -43,6 +44,7 @@ export = {
     'no-deprecated-i18n-place-attr': noDeprecatedI18nPlaceAttr,
     'no-deprecated-i18n-places-prop': noDeprecatedI18nPlacesProp,
     'no-deprecated-modulo-syntax': noDeprecatedModuloSyntax,
+    'no-deprecated-tc': noDeprecatedTc,
     'no-duplicate-keys-in-locale': noDuplicateKeysInLocale,
     'no-dynamic-keys': noDynamicKeys,
     'no-html-messages': noHtmlMessages,
diff --git a/lib/rules/no-deprecated-tc.ts b/lib/rules/no-deprecated-tc.ts
new file mode 100644
index 00000000..63de6d5b
--- /dev/null
+++ b/lib/rules/no-deprecated-tc.ts
@@ -0,0 +1,60 @@
+/**
+ * @author kazuya kawaguchi (a.k.a. kazupon)
+ */
+import { defineTemplateBodyVisitor } from '../utils/index'
+import { createRule } from '../utils/rule'
+
+import type { RuleContext, RuleListener } from '../types'
+import type { AST as VAST } from 'vue-eslint-parser'
+
+function checkCallExpression(
+  context: RuleContext,
+  node: VAST.ESLintCallExpression
+) {
+  const funcName =
+    (node.callee.type === 'MemberExpression' &&
+      node.callee.property.type === 'Identifier' &&
+      node.callee.property.name) ||
+    (node.callee.type === 'Identifier' && node.callee.name) ||
+    ''
+
+  if (/^(\$tc|tc)$/.test(funcName)) {
+    context.report({
+      node,
+      message: `'${funcName}' is used, but it is deprecated. Use 't' or '$t' instead.`
+    })
+    return
+  }
+}
+
+function create(context: RuleContext): RuleListener {
+  return defineTemplateBodyVisitor(
+    context,
+    {
+      CallExpression(node: VAST.ESLintCallExpression) {
+        checkCallExpression(context, node)
+      }
+    },
+    {
+      CallExpression(node: VAST.ESLintCallExpression) {
+        checkCallExpression(context, node)
+      }
+    }
+  )
+}
+
+export = createRule({
+  meta: {
+    type: 'suggestion',
+    docs: {
+      description:
+        'disallow using deprecated `tc` or `$tc` (Deprecated in Vue I18n 10.0.0, removed fully in Vue I18n 11.0.0)',
+      category: 'Best Practices',
+      url: 'https://eslint-plugin-vue-i18n.intlify.dev/rules/no-deprecated-tc.html',
+      recommended: false
+    },
+    fixable: null,
+    schema: []
+  },
+  create
+})
diff --git a/tests/lib/rules/no-deprecated-tc.ts b/tests/lib/rules/no-deprecated-tc.ts
new file mode 100644
index 00000000..22b0cb1c
--- /dev/null
+++ b/tests/lib/rules/no-deprecated-tc.ts
@@ -0,0 +1,38 @@
+/**
+ * @author kazuya kawaguchi (a.k.a. kazupon)
+ */
+import { RuleTester } from '../eslint-compat'
+import rule from '../../../lib/rules/no-deprecated-tc'
+import * as vueParser from 'vue-eslint-parser'
+
+const tester = new RuleTester({
+  languageOptions: { parser: vueParser, ecmaVersion: 2015 }
+})
+
+tester.run('no-deprecated-tc', rule as never, {
+  valid: [],
+  invalid: [
+    {
+      // tc key
+      code: `tc('banana')`,
+      errors: [`'tc' is used, but it is deprecated. Use 't' or '$t' instead.`]
+    },
+    {
+      // $tc key
+      code: `$tc('banana')`,
+      errors: [`'$tc' is used, but it is deprecated. Use 't' or '$t' instead.`]
+    },
+    {
+      // via i18n instance
+      code: `i18n.tc('banana')`,
+      errors: [`'tc' is used, but it is deprecated. Use 't' or '$t' instead.`]
+    },
+    {
+      // using mustaches in template block
+      code: `<template>
+      <p>{{ $tc('banana') }}</p>
+    </template>`,
+      errors: [`'$tc' is used, but it is deprecated. Use 't' or '$t' instead.`]
+    }
+  ]
+})

From 55041e6d91c9799e13d43902dad030301250b22c Mon Sep 17 00:00:00 2001
From: kazuya kawaguchi <kawakazu80@gmail.com>
Date: Fri, 10 May 2024 17:36:10 +0900
Subject: [PATCH 2/2] Create new-apples-ring.md

---
 .changeset/new-apples-ring.md | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 .changeset/new-apples-ring.md

diff --git a/.changeset/new-apples-ring.md b/.changeset/new-apples-ring.md
new file mode 100644
index 00000000..5e888be8
--- /dev/null
+++ b/.changeset/new-apples-ring.md
@@ -0,0 +1,5 @@
+---
+"@intlify/eslint-plugin-vue-i18n": patch
+---
+
+feat: add `no-deprecated-tc` rule