From 5441f70cd556bdec5263ed42f4c59b3cdcaa19f9 Mon Sep 17 00:00:00 2001 From: Alexander Brown Date: Fri, 6 Feb 2026 14:03:00 -0800 Subject: [PATCH] feat: enforce i18n import conventions via ESLint (#8701) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Enforce i18n import conventions via ESLint: Vue components must use `useI18n()`, non-composable `.ts` files must use the global `t` from `@/i18n`. ## Changes - **What**: Two new `no-restricted-imports` rules in `eslint.config.ts` (both `warn` severity for incremental migration). Removed the disabled `@/i18n--to-enable` placeholder from `.oxlintrc.json`. - `.vue` files: disallow importing `t`/`d`/`st`/`te` from `@/i18n` (37 existing violations to migrate) - Non-composable `.ts` files: disallow importing `useI18n` from `vue-i18n` (2 existing violations) - Composable `use*.ts`, test files, and `src/i18n.ts` are excluded from Rule 2 ## Review Focus - The rules are placed after `oxlint.buildFromOxlintConfigFile()` to re-enable ESLint's `no-restricted-imports` for these specific file scopes without conflicting with oxlint's base rule (which handles PrimeVue deprecations). - `warn` severity chosen so CI is not blocked while existing violations are migrated. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-8701-feat-enforce-i18n-import-conventions-via-ESLint-2ff6d73d36508123b6f9edf2693fb5e0) by [Unito](https://www.unito.io) --------- Co-authored-by: Amp Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .oxlintrc.json | 5 ----- eslint.config.ts | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index fb5d00dab..36eef37a9 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -60,11 +60,6 @@ { "name": "primevue/sidebar", "message": "Sidebar is deprecated in PrimeVue 4+. Use Drawer instead: import Drawer from 'primevue/drawer'" - }, - { - "name": "@/i18n--to-enable", - "importNames": ["st", "t", "te", "d"], - "message": "Don't import `@/i18n` directly, prefer `useI18n()`" } ] } diff --git a/eslint.config.ts b/eslint.config.ts index 4974637fa..8dc7da5a5 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -279,5 +279,46 @@ export default defineConfig([ 'import-x/no-duplicates': 'off', 'import-x/consistent-type-specifier-style': 'off' } + }, + + // i18n import enforcement + // Vue components must use the useI18n() composable, not the global t/d/st/te + { + files: ['**/*.vue'], + rules: { + 'no-restricted-imports': [ + 'warn', + { + paths: [ + { + name: '@/i18n', + importNames: ['t', 'd', 'st', 'te'], + message: + "In Vue components, use `const { t } = useI18n()` instead of importing from '@/i18n'." + } + ] + } + ] + } + }, + // Non-composable .ts files must use the global t/d/st/te, not useI18n() + { + files: ['**/*.ts'], + ignores: ['**/use[A-Z]*.ts', '**/*.test.ts', 'src/i18n.ts'], + rules: { + 'no-restricted-imports': [ + 'warn', + { + paths: [ + { + name: 'vue-i18n', + importNames: ['useI18n'], + message: + "useI18n() requires Vue setup context. Use `import { t } from '@/i18n'` instead." + } + ] + } + ] + } } ])