Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: intlify/eslint-plugin-vue-i18n
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.11.0
Choose a base ref
...
head repository: intlify/eslint-plugin-vue-i18n
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref

Commits on Feb 23, 2021

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    041e6a9 View commit details

Commits on Feb 26, 2021

  1. Update dependency jsonc-eslint-parser to v1 (#170)

    Co-authored-by: Renovate Bot <bot@renovateapp.com>
    renovate[bot] and renovate-bot authored Feb 26, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    032f4b0 View commit details

Commits on Mar 5, 2021

  1. Copy the full SHA
    ff2ebc2 View commit details

Commits on Mar 9, 2021

  1. build(deps): bump elliptic from 6.5.3 to 6.5.4 (#172)

    Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4.
    - [Release notes](https://github.com/indutny/elliptic/releases)
    - [Commits](indutny/elliptic@v6.5.3...v6.5.4)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Mar 9, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    fb57f5f View commit details

Commits on Mar 18, 2021

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6c80a22 View commit details
  2. Update yarn lock (#174)

    ota-meshi authored Mar 18, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    95bd608 View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8a444c4 View commit details
  4. Fix changelog (#176)

    ota-meshi authored Mar 18, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ba54b54 View commit details

Commits on Mar 25, 2021

  1. chore(docs): update sponsors

    kazupon committed Mar 25, 2021
    Copy the full SHA
    6d00b49 View commit details

Commits on Apr 10, 2021

  1. Format (#181)

    ota-meshi authored Apr 10, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    fc089e9 View commit details

Commits on May 8, 2021

  1. build(deps): bump lodash from 4.17.19 to 4.17.21 (#193)

    Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21.
    - [Release notes](https://github.com/lodash/lodash/releases)
    - [Commits](lodash/lodash@4.17.19...4.17.21)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored May 8, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    e4c8e5e View commit details
  2. build(deps): bump lodash in /tests-integrations/config-recommended (#194

    )
    
    Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21.
    - [Release notes](https://github.com/lodash/lodash/releases)
    - [Commits](lodash/lodash@4.17.19...4.17.21)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored May 8, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    2f4a930 View commit details
  3. build(deps): bump ssri from 6.0.1 to 6.0.2 (#184)

    Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
    - [Release notes](https://github.com/npm/ssri/releases)
    - [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
    - [Commits](npm/ssri@v6.0.1...v6.0.2)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored May 8, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    70aefb0 View commit details
  4. build(deps): bump handlebars from 4.7.2 to 4.7.7 (#191)

    Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.7.2 to 4.7.7.
    - [Release notes](https://github.com/wycats/handlebars.js/releases)
    - [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
    - [Commits](handlebars-lang/handlebars.js@v4.7.2...v4.7.7)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored May 8, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    165197d View commit details
  5. build(deps): bump url-parse from 1.4.7 to 1.5.1 (#192)

    Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.1.
    - [Release notes](https://github.com/unshiftio/url-parse/releases)
    - [Commits](unshiftio/url-parse@1.4.7...1.5.1)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored May 8, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6e5bdd4 View commit details

Commits on May 12, 2021

  1. Copy the full SHA
    ea6c9c0 View commit details

Commits on Jun 11, 2021

  1. chore(deps): update dependency monaco-editor to ^0.25.0 (#203)

    Co-authored-by: Renovate Bot <bot@renovateapp.com>
    renovate[bot] and renovate-bot authored Jun 11, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    d5e6fac View commit details
  2. build(deps): bump hosted-git-info from 2.8.5 to 2.8.9 (#195)

    Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.5 to 2.8.9.
    - [Release notes](https://github.com/npm/hosted-git-info/releases)
    - [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
    - [Commits](npm/hosted-git-info@v2.8.5...v2.8.9)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jun 11, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c4c08b1 View commit details
  3. build(deps): bump browserslist from 4.16.0 to 4.16.6 (#198)

    Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.16.0 to 4.16.6.
    - [Release notes](https://github.com/browserslist/browserslist/releases)
    - [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
    - [Commits](browserslist/browserslist@4.16.0...4.16.6)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jun 11, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    5aa04cc View commit details
  4. build(deps): bump dns-packet from 1.3.1 to 1.3.4 (#199)

    Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 1.3.1 to 1.3.4.
    - [Release notes](https://github.com/mafintosh/dns-packet/releases)
    - [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md)
    - [Commits](mafintosh/dns-packet@v1.3.1...v1.3.4)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jun 11, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    655f7d3 View commit details
  5. build(deps): bump ws from 6.2.1 to 6.2.2 (#200)

    Bumps [ws](https://github.com/websockets/ws) from 6.2.1 to 6.2.2.
    - [Release notes](https://github.com/websockets/ws/releases)
    - [Commits](https://github.com/websockets/ws/commits)
    
    ---
    updated-dependencies:
    - dependency-name: ws
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jun 11, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    30890fa View commit details

Commits on Jun 12, 2021

  1. build(deps): bump glob-parent in /tests-integrations/config-recommend…

    …ed (#204)
    
    Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
    - [Release notes](https://github.com/gulpjs/glob-parent/releases)
    - [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
    - [Commits](gulpjs/glob-parent@v5.1.1...v5.1.2)
    
    ---
    updated-dependencies:
    - dependency-name: glob-parent
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jun 12, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    cf51d0f View commit details
  2. Upgrade @intlify/message-compiler and @intlify/message-resolver (#…

    …205)
    
    * Upgrade `@intlify/message-compiler` and `@intlify/message-resolver`
    
    * Upgrade typescript
    ota-meshi authored Jun 12, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    67b26ed View commit details

Commits on Jul 15, 2021

  1. Copy the full SHA
    cb1c30c View commit details

Commits on Jul 27, 2021

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    be06c3b View commit details
  2. build(deps): bump postcss from 7.0.35 to 7.0.36 (#207)

    Bumps [postcss](https://github.com/postcss/postcss) from 7.0.35 to 7.0.36.
    - [Release notes](https://github.com/postcss/postcss/releases)
    - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
    - [Commits](postcss/postcss@7.0.35...7.0.36)
    
    ---
    updated-dependencies:
    - dependency-name: postcss
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jul 27, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c4aafd8 View commit details
  3. build(deps): bump prismjs from 1.23.0 to 1.24.0 (#208)

    Bumps [prismjs](https://github.com/PrismJS/prism) from 1.23.0 to 1.24.0.
    - [Release notes](https://github.com/PrismJS/prism/releases)
    - [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md)
    - [Commits](PrismJS/prism@v1.23.0...v1.24.0)
    
    ---
    updated-dependencies:
    - dependency-name: prismjs
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jul 27, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    0e7f733 View commit details
  4. fix(deps): update dependency yaml-eslint-parser to ^0.4.0 (#211)

    Co-authored-by: Renovate Bot <bot@renovateapp.com>
    renovate[bot] and renovate-bot authored Jul 27, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    cea26ae View commit details
  5. build(deps): bump color-string from 1.5.4 to 1.6.0 (#212)

    Bumps [color-string](https://github.com/Qix-/color-string) from 1.5.4 to 1.6.0.
    - [Release notes](https://github.com/Qix-/color-string/releases)
    - [Changelog](https://github.com/Qix-/color-string/blob/master/CHANGELOG.md)
    - [Commits](Qix-/color-string@1.5.4...1.6.0)
    
    ---
    updated-dependencies:
    - dependency-name: color-string
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jul 27, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    2141d1e View commit details
  6. minor release v0.12.0 (#213)

    * minor release v0.12.0
    
    * update
    ota-meshi authored Jul 27, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    b6ac57c View commit details

Commits on Aug 4, 2021

  1. update sponsors

    kazupon committed Aug 4, 2021
    Copy the full SHA
    c16156b View commit details

Commits on Aug 14, 2021

  1. Chores: Replace CLIEngine with ESLint class. (#218)

    * Chores: Replace CLIEngine with ESLint class.
    
    * fix
    
    * fix
    ota-meshi authored Aug 14, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    4a70f1e View commit details

Commits on Aug 16, 2021

  1. Copy the full SHA
    0ced952 View commit details

Commits on Aug 20, 2021

  1. Add support for eslint v8(beta) (#221)

    * Add test for eslint v8
    
    * fix test for eslint 6
    
    * update ci
    
    * fix
    
    * fix test for eslint 5
    
    * fix test for eslint 5
    
    * revert peer dep
    
    * add eslint 8.0.0-0 to peer deps
    ota-meshi authored Aug 20, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8d094f9 View commit details

Commits on Aug 22, 2021

  1. Refactor test cases (#222)

    * Refactor test cases
    
    * sort messages
    
    * combine the messages that are output twice.
    
    * refactor for no-html-messages test
    
    * update
    
    * refactor test
    
    * remove unused source code
    ota-meshi authored Aug 22, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    df4fffd View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ccbcff0 View commit details

Commits on Aug 25, 2021

  1. build(deps): bump tar from 6.1.0 to 6.1.3 (#214)

    Bumps [tar](https://github.com/npm/node-tar) from 6.1.0 to 6.1.3.
    - [Release notes](https://github.com/npm/node-tar/releases)
    - [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
    - [Commits](isaacs/node-tar@v6.1.0...v6.1.3)
    
    ---
    updated-dependencies:
    - dependency-name: tar
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Aug 25, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    dadf448 View commit details
  2. build(deps): bump url-parse from 1.5.1 to 1.5.3 (#219)

    Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.1 to 1.5.3.
    - [Release notes](https://github.com/unshiftio/url-parse/releases)
    - [Commits](unshiftio/url-parse@1.5.1...1.5.3)
    
    ---
    updated-dependencies:
    - dependency-name: url-parse
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Aug 25, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6e752c1 View commit details

Commits on Sep 1, 2021

  1. chore(deps): update dependency eslint-plugin-prettier to v4 (#226)

    Co-authored-by: Renovate Bot <bot@renovateapp.com>
    renovate[bot] and renovate-bot authored Sep 1, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    417f563 View commit details
  2. build(deps): bump tar from 6.1.3 to 6.1.11 (#227)

    Bumps [tar](https://github.com/npm/node-tar) from 6.1.3 to 6.1.11.
    - [Release notes](https://github.com/npm/node-tar/releases)
    - [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md)
    - [Commits](isaacs/node-tar@v6.1.3...v6.1.11)
    
    ---
    updated-dependencies:
    - dependency-name: tar
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Sep 1, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    1f992e9 View commit details
  3. build(deps): bump path-parse from 1.0.6 to 1.0.7 (#217)

    Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
    - [Release notes](https://github.com/jbgutierrez/path-parse/releases)
    - [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)
    
    ---
    updated-dependencies:
    - dependency-name: path-parse
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Sep 1, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    e031cc2 View commit details

Commits on Sep 8, 2021

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    41b8294 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    38bd3b6 View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7cd9ab3 View commit details
  4. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    b7b5f16 View commit details
  5. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    e8966b5 View commit details
  6. update funding

    kazupon committed Sep 8, 2021
    Copy the full SHA
    587f411 View commit details

Commits on Sep 20, 2021

  1. build(deps): bump prismjs from 1.24.0 to 1.25.0 (#234)

    Bumps [prismjs](https://github.com/PrismJS/prism) from 1.24.0 to 1.25.0.
    - [Release notes](https://github.com/PrismJS/prism/releases)
    - [Changelog](https://github.com/PrismJS/prism/blob/master/CHANGELOG.md)
    - [Commits](PrismJS/prism@v1.24.0...v1.25.0)
    
    ---
    updated-dependencies:
    - dependency-name: prismjs
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Sep 20, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    5bd30b3 View commit details

Commits on Sep 21, 2021

  1. Copy the full SHA
    a452577 View commit details

Commits on Sep 22, 2021

  1. Copy the full SHA
    997b50d View commit details
Showing with 17,820 additions and 17,133 deletions.
  1. +8 −0 .changeset/README.md
  2. +15 −0 .changeset/config.json
  3. +5 −0 .env-cmdrc
  4. +0 −11 .eslintignore
  5. +0 −43 .eslintrc.js
  6. +2 −0 .githooks/pre-commit
  7. +9 −9 .github/CONTRIBUTING.md
  8. +4 −2 .github/FUNDING.yml
  9. +6 −1 .github/ISSUE_TEMPLATE/bug_report.md
  10. +4 −4 .github/ISSUE_TEMPLATE/rule-proposal.md
  11. +43 −0 .github/workflows/Release.yml
  12. +0 −29 .github/workflows/release.yml
  13. +42 −12 .github/workflows/test.yml
  14. +2 −1 .gitignore
  15. +1 −0 .node-version
  16. +2 −0 .npmrc
  17. +5 −2 .prettierignore
  18. +3 −0 .prettierrc.mjs
  19. +2 −2 .vscode/settings.json
  20. +472 −93 CHANGELOG.md
  21. +1 −0 LICENSE
  22. +30 −6 README.md
  23. +43 −50 docs/{.vuepress → .vitepress}/components/eslint-code-block.vue
  24. +1 −2 docs/{.vuepress → .vitepress}/components/resource-group.vue
  25. 0 docs/{.vuepress → .vitepress}/components/sponsor-button.vue
  26. +73 −0 docs/.vitepress/config.mts
  27. +0 −7 docs/.vuepress/components/setup.js
  28. +0 −79 docs/.vuepress/config.js
  29. 0 docs/.vuepress/public/.nojekyll
  30. +0 −6 docs/.vuepress/shim/eslint-plugin-vue-i18n/utils/glob-utils.js
  31. +0 −13 docs/.vuepress/shim/eslint/index.js
  32. +0 −22 docs/.vuepress/shim/fs/fake-fs.js
  33. +0 −4 docs/.vuepress/shim/fs/index.js
  34. +0 −5 docs/.vuepress/shim/glob.js
  35. +0 −3 docs/.vuepress/shim/module.js
  36. +0 −23 docs/README.md
  37. +29 −0 docs/index.md
  38. +15 −0 docs/intro.md
  39. +2 −0 docs/public/_headers
  40. BIN docs/{.vuepress → }/public/eslint-plugin-vue-i18n.png
  41. 0 docs/{.vuepress → }/public/eslint-plugin-vue-i18n.svg
  42. +12 −6 docs/rules/{README.md → index.md}
  43. +34 −3 docs/rules/key-format-style.md
  44. +1 −1 docs/rules/keys-order.md
  45. +1 −0 docs/rules/no-deprecated-i18n-component.md
  46. +2 −0 docs/rules/no-deprecated-i18n-place-attr.md
  47. +2 −0 docs/rules/no-deprecated-i18n-places-prop.md
  48. +57 −0 docs/rules/no-deprecated-modulo-syntax.md
  49. +66 −0 docs/rules/no-deprecated-tc.md
  50. +66 −0 docs/rules/no-deprecated-v-t.md
  51. +6 −2 docs/rules/no-dynamic-keys.md
  52. +8 −4 docs/rules/no-html-messages.md
  53. +1 −0 docs/rules/no-i18n-t-path-prop.md
  54. +1 −1 docs/rules/no-missing-keys-in-other-locales.md
  55. +7 −3 docs/rules/no-missing-keys.md
  56. +61 −9 docs/rules/no-raw-text.md
  57. +71 −0 docs/rules/no-unknown-locale.md
  58. +8 −2 docs/rules/no-unused-keys.md
  59. +1 −1 docs/rules/no-v-html.md
  60. +70 −0 docs/rules/prefer-sfc-lang-attr.md
  61. +94 −0 docs/rules/sfc-locale-attr.md
  62. +2 −0 docs/rules/valid-message-syntax.md
  63. +151 −53 docs/started.md
  64. +93 −0 eslint.config.mjs
  65. +0 −8 lib/configs.ts
  66. +1 −6 lib/configs/base.ts
  67. +35 −0 lib/configs/flat/base.ts
  68. +36 −0 lib/configs/flat/recommended.ts
  69. +9 −1 lib/configs/recommended.ts
  70. +65 −8 lib/index.ts
  71. +0 −34 lib/rules.ts
  72. +114 −28 lib/rules/key-format-style.ts
  73. +8 −4 lib/rules/no-deprecated-i18n-component.ts
  74. +5 −3 lib/rules/no-deprecated-i18n-place-attr.ts
  75. +5 −3 lib/rules/no-deprecated-i18n-places-prop.ts
  76. +130 −0 lib/rules/no-deprecated-modulo-syntax.ts
  77. +60 −0 lib/rules/no-deprecated-tc.ts
  78. +43 −0 lib/rules/no-deprecated-v-t.ts
  79. +49 −33 lib/rules/no-duplicate-keys-in-locale.ts
  80. +18 −30 lib/rules/no-dynamic-keys.ts
  81. +18 −13 lib/rules/no-html-messages.ts
  82. +5 −3 lib/rules/no-i18n-t-path-prop.ts
  83. +14 −7 lib/rules/no-missing-keys-in-other-locales.ts
  84. +20 −19 lib/rules/no-missing-keys.ts
  85. +697 −133 lib/rules/no-raw-text.ts
  86. +271 −0 lib/rules/no-unknown-locale.ts
  87. +53 −26 lib/rules/no-unused-keys.ts
  88. +4 −2 lib/rules/no-v-html.ts
  89. +34 −143 lib/rules/prefer-linked-key-with-paren.ts
  90. +67 −0 lib/rules/prefer-sfc-lang-attr.ts
  91. +69 −0 lib/rules/sfc-locale-attr.ts
  92. +28 −22 lib/rules/valid-message-syntax.ts
  93. +37 −11 lib/types/eslint.ts
  94. +9 −1 lib/types/i18n.ts
  95. +12 −2 lib/types/settings.ts
  96. +4 −4 lib/types/vue-parser-services.ts
  97. +0 −34 lib/utils.ts
  98. +63 −3 lib/utils/casing.ts
  99. +47 −74 lib/utils/collect-keys.ts
  100. +6 −6 lib/utils/collect-linked-keys.ts
  101. +9 −0 lib/utils/compat.ts
  102. +6 −0 lib/utils/get-cwd.ts
  103. +0 −42 lib/utils/glob-sync.ts
  104. +27 −23 lib/utils/glob-utils.ts
  105. +12 −7 lib/utils/ignored-paths.ts
  106. +349 −27 lib/utils/index.ts
  107. +2 −2 lib/utils/key-path.ts
  108. +76 −24 lib/utils/locale-messages.ts
  109. +0 −331 lib/utils/message-compiler/parser-v8.ts
  110. +59 −0 lib/utils/message-compiler/parser-v9.ts
  111. +1 −3 lib/utils/message-compiler/parser.ts
  112. +4 −2 lib/utils/message-compiler/traverser.ts
  113. +32 −6 lib/utils/message-compiler/utils.ts
  114. +14 −0 lib/utils/parser-config-resolver/build-parser-using-flat-config.ts
  115. +48 −0 lib/utils/parser-config-resolver/build-parser-using-legacy-config.ts
  116. +24 −0 lib/utils/parser-config-resolver/index.ts
  117. +45 −0 lib/utils/parser-config-resolver/parse-by-parser.ts
  118. +67 −0 lib/utils/parser-config-resolver/should-use-flat-config.ts
  119. +41 −0 lib/utils/parser-config-resolver/worker.ts
  120. +4 −4 lib/utils/parsers/index.ts
  121. +20 −0 lib/utils/regexp.ts
  122. +2 −2 lib/utils/resource-loader.ts
  123. +99 −0 lib/utils/rule.ts
  124. +6 −0 netlify.toml
  125. +122 −91 package.json
  126. +6,467 −0 pnpm-lock.yaml
  127. +5 −1 renovate.json
  128. +26 −0 repro/eslint.config.js
  129. +11 −0 repro/package.json
  130. +4 −0 repro/src/components/MyComponent.vue
  131. +4 −0 repro/src/resources/en.json
  132. +11 −0 scripts/lib/changesets-util.ts
  133. +1 −0 scripts/lib/configs.ts
  134. +85 −0 scripts/lib/eslint-compat.ts
  135. +68 −33 scripts/lib/rules.ts
  136. +18 −38 scripts/lib/utils.ts
  137. +137 −0 scripts/new-rule.ts
  138. +41 −0 scripts/update-flat-base-configs.ts
  139. +42 −0 scripts/update-flat-recommended-configs.ts
  140. +25 −13 scripts/{update-docs-index.ts → update-index-docs.ts}
  141. +44 −0 scripts/update-index.ts
  142. +31 −0 scripts/update-legacy-base-configs.ts
  143. +31 −0 scripts/update-legacy-recommended-configs.ts
  144. +0 −38 scripts/update-recommended-rules.ts
  145. +132 −114 scripts/update-rule-docs.ts
  146. +25 −22 scripts/update.ts
  147. +0 −71 ship.config.js
  148. +0 −44 tests-integrations/config-recommended.js
  149. +0 −11 tests-integrations/config-recommended/.eslintrc.js
  150. +0 −7 tests-integrations/config-recommended/.vscode/settings.json
  151. +0 −12 tests-integrations/config-recommended/package.json
  152. +0 −821 tests-integrations/config-recommended/yarn.lock
  153. +1 −0 tests/fixtures/no-deprecated-modulo-syntax/test.json
  154. +1 −0 tests/fixtures/no-deprecated-modulo-syntax/test.yaml
  155. +7 −0 tests/fixtures/no-missing-keys/complex-locales/locales/en.json
  156. +7 −0 tests/fixtures/no-missing-keys/complex-locales/locales/ja.json
  157. +1 −0 tests/fixtures/no-unknown-locale/file/en.json
  158. +1 −0 tests/fixtures/no-unknown-locale/file/en.yaml
  159. +1 −0 tests/fixtures/no-unknown-locale/file/test.json
  160. +1 −0 tests/fixtures/no-unknown-locale/file/test.yaml
  161. +1 −0 tests/fixtures/no-unknown-locale/key/test.json
  162. +1 −0 tests/fixtures/no-unknown-locale/key/test.yaml
  163. +2 −1 tests/fixtures/no-unused-keys/invalid/constructor-option-format/src/App.vue
  164. +39 −0 tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/eslint.config.cjs
  165. +13 −0 tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/locales/en.json
  166. +9 −0 tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/locales/ja.yaml
  167. +14 −0 tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/src/App.vue
  168. +2 −0 tests/fixtures/no-unused-keys/invalid/typescript-with-flat-config/src/main.ts
  169. 0 tests/fixtures/no-unused-keys/invalid/typescript/{.eslintrc.js → .eslintrc.cjs}
  170. +10 −0 tests/fixtures/no-unused-keys/valid/path-locales/locales/en/message.json
  171. +7 −0 tests/fixtures/no-unused-keys/valid/path-locales/locales/ja/message.yaml
  172. +17 −0 tests/fixtures/no-unused-keys/valid/path-locales/src/App.vue
  173. +2 −0 tests/fixtures/no-unused-keys/valid/path-locales/src/main.js
  174. +1 −0 tests/fixtures/utils/locale-messages/locales/en.yaml
  175. +3 −0 tests/fixtures/utils/locale-messages/locales/en/message.json
  176. +8 −0 tests/fixtures/utils/locale-messages/locales/message.json5
  177. +51 −0 tests/integrations/flat-config.ts
  178. 0 {tests-integrations/config-recommended → tests/integrations/flat-config}/.npmrc
  179. +3 −0 tests/integrations/flat-config/.vscode/settings.json
  180. +18 −0 tests/integrations/flat-config/eslint.config.js
  181. +11 −0 tests/integrations/flat-config/package.json
  182. +1 −2 {tests-integrations/config-recommended → tests/integrations/flat-config}/src/a.vue
  183. 0 {tests-integrations/config-recommended → tests/integrations/flat-config}/src/resources/en.json
  184. +8 −0 tests/integrations/helper.ts
  185. +51 −0 tests/integrations/legacy-config.ts
  186. +18 −0 tests/integrations/legacy-config/.eslintrc.cjs
  187. +1 −0 tests/integrations/legacy-config/.npmrc
  188. +3 −0 tests/integrations/legacy-config/.vscode/settings.json
  189. +10 −0 tests/integrations/legacy-config/package.json
  190. +4 −0 tests/integrations/legacy-config/src/a.vue
  191. +3 −0 tests/integrations/legacy-config/src/resources/en.json
  192. +9 −0 tests/lib/eslint-compat.ts
  193. +265 −16 tests/lib/rules/key-format-style.ts
  194. +4 −4 tests/lib/rules/no-deprecated-i18n-component.ts
  195. +4 −4 tests/lib/rules/no-deprecated-i18n-place-attr.ts
  196. +4 −4 tests/lib/rules/no-deprecated-i18n-places-prop.ts
  197. +169 −0 tests/lib/rules/no-deprecated-modulo-syntax.ts
  198. +38 −0 tests/lib/rules/no-deprecated-tc.ts
  199. +30 −0 tests/lib/rules/no-deprecated-v-t.ts
  200. +502 −551 tests/lib/rules/no-duplicate-keys-in-locale.ts
  201. +4 −4 tests/lib/rules/no-dynamic-keys.ts
  202. +61 −73 tests/lib/rules/no-html-messages.ts
  203. +4 −4 tests/lib/rules/no-i18n-t-path-prop.ts
  204. +28 −28 tests/lib/rules/no-missing-keys-in-other-locales.ts
  205. +150 −15 tests/lib/rules/no-missing-keys.ts
  206. +1,287 −34 tests/lib/rules/no-raw-text.ts
  207. +273 −0 tests/lib/rules/no-unknown-locale.ts
  208. +2,026 −518 tests/lib/rules/no-unused-keys.ts
  209. +8 −4 tests/lib/rules/no-v-html.ts
  210. +20 −191 tests/lib/rules/prefer-linked-key-with-paren.ts
  211. +131 −0 tests/lib/rules/prefer-sfc-lang-attr.ts
  212. +111 −0 tests/lib/rules/sfc-locale-attr.ts
  213. +23 −268 tests/lib/rules/valid-message-syntax.ts
  214. +152 −202 tests/lib/test-utils.ts
  215. +3 −3 tests/lib/utils/cache-function.ts
  216. +5 −5 tests/lib/utils/cache-loader.ts
  217. +24 −15 tests/lib/utils/collect-keys.ts
  218. +10 −28 tests/lib/utils/collect-linked-keys.ts
  219. +16 −14 tests/lib/utils/index.ts
  220. +51 −0 tests/lib/utils/locale-messages.ts
  221. +0 −475 tests/lib/utils/message-compiler/parser-v8-data.ts
  222. +0 −141 tests/lib/utils/message-compiler/parser-v8.ts
  223. +95 −115 tests/lib/utils/message-compiler/utils.ts
  224. +13 −13 tests/lib/utils/resource-loader.ts
  225. +14 −0 tests/lib/utils/rule.ts
  226. +1 −0 tsconfig.build.json
  227. +7 −11 tsconfig.json
  228. +0 −11,426 yarn.lock
8 changes: 8 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Changesets

Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)

We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
15 changes: 15 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://unpkg.com/@changesets/config/schema.json",
"changelog": [
"@changesets/changelog-github",
{
"repo": "intlify/eslint-plugin-vue-i18n"
}
],
"commit": false,
"linked": [],
"baseBranch": "master",
"updateInternalDependencies": "patch",
"bumpVersionsWithWorkspaceProtocolOnly": true,
"ignore": []
}
5 changes: 5 additions & 0 deletions .env-cmdrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"version-ci": {
"IN_VERSION_CI_SCRIPT": "true"
}
}
11 changes: 0 additions & 11 deletions .eslintignore

This file was deleted.

43 changes: 0 additions & 43 deletions .eslintrc.js

This file was deleted.

2 changes: 2 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
npx --no-install lint-staged
18 changes: 9 additions & 9 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@

- It's OK to have multiple small commits as you work on the PR - we will let GitHub automatically squash it before merging.

- Make sure `yarn test` passes. (see [development setup](#development-setup))
- Make sure `pnpm test` passes. (see [development setup](#development-setup))

- If adding new feature:

@@ -52,23 +52,23 @@

## Development Setup

You will need [Node.js](http://nodejs.org) and [Yarn](https://yarnpkg.com/en/)
You will need [Node.js](http://nodejs.org) and [pnpm](https://pnpm.io/)

After cloning the repo, run:

$ yarn
$ pnpm install

### Commonly used scirpt with Yarn
### Commonly used scirpt with `pnpm`

# lint source codes and docs
$ yarn lint
$ yarn lint:docs
$ pnpm lint
$ pnpm lint:docs

# run the vuepress
$ yarn docs
# run vitepress
$ pnpm docs

# run the test suite
$ yarn test
$ pnpm test

There are some other scripts available in the `scripts` section of the `package.json` file.

6 changes: 4 additions & 2 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# These are supported funding model platforms

github: kazupon
patreon: kazupon
github:
- ota-meshi
- kazupon
patreon: # kazupon
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
7 changes: 6 additions & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ about: Create a report to help us improve
<!--
Before posting the issue, please confirm that the problem you're getting
is not related with your code editor configuration.
To make sure it's not, run: yarn eslint src/your-file.vue
To make sure it's not, run: pnpm eslint src/your-file.vue
-->

**Tell us about your environment**
@@ -41,3 +41,8 @@ about: Create a report to help us improve
If you are only looking at the results of your editor extension, also check the CLI results.
-->

**Repository to reproduce this issue**
<!--
Please share a repository that can reproduce your issue.
If you don't share it, we will most likely add a comment asking you to share the repository.
-->
8 changes: 4 additions & 4 deletions .github/ISSUE_TEMPLATE/rule-proposal.md
Original file line number Diff line number Diff line change
@@ -16,10 +16,10 @@ about: Suggest an idea for a new rule
**What category should the rule belong to?**
<!-- (place an "X" next to just one item) -->

[ ] Enforces code style (layout)
[ ] Warns about a potential error (problem)
[ ] Suggests an alternate way of doing something (suggestion)
[ ] Other (please specify:)
- [ ] Enforces code style (layout)
- [ ] Warns about a potential error (problem)
- [ ] Suggests an alternate way of doing something (suggestion)
- [ ] Other (please specify:)

**Provide 2-3 code examples that this rule should warn about:**

43 changes: 43 additions & 0 deletions .github/workflows/Release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Release

on:
push:
branches:
- master

jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
with:
# This makes Actions fetch all Git history so that Changesets can generate changelogs with the correct commits
fetch-depth: 0

- name: Setup Node.js 18
uses: actions/setup-node@v4
with:
node-version: 18

- uses: pnpm/action-setup@v4

- name: Install Dependencies
run: pnpm install
- name: Build
run: pnpm run build

- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
with:
# this expects you to have a npm script called version that runs some logic and then calls `changeset version`.
version: pnpm run version:ci
# This expects you to have a script called release which does a build for your packages and calls changeset publish
publish: pnpm run release
commit: 'chore: release `@intlify/eslint-plugin-vue-i18n`'
title: 'chore: release `@intlify/eslint-plugin-vue-i18n`'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
29 changes: 0 additions & 29 deletions .github/workflows/release.yml

This file was deleted.

54 changes: 42 additions & 12 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -8,29 +8,59 @@ env:
CI: true

jobs:
lint:
name: 'Lint'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
- uses: pnpm/action-setup@v4
- name: Install
run: pnpm install
- name: Lint
run: pnpm lint
test:
name: 'Test on Node.js ${{ matrix.node }} OS: ${{matrix.os}}'
name: 'Test for Node.js ${{ matrix.node }} on ${{ matrix.os }}'
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
node: [10, 12, 14]
node: [18, 20, 22]
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node }}
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- uses: pnpm/action-setup@v4
- name: Install
run: yarn install
- name: Lint
run: yarn lint
- name: Lint docs
run: yarn lint:docs
run: pnpm install
- name: Test
run: yarn test
run: pnpm test
- name: Integration Test
run: |
yarn build
yarn test:integrations
pnpm build
pnpm test:integrations
test-for-old-eslint:
name: 'Test for ESLint ${{ matrix.eslint }} on ${{ matrix.os }} using Node.js ${{ matrix.node }}'
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
eslint: [8]
node: [20]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- uses: pnpm/action-setup@v4
- name: Install
run: pnpm add eslint@${{ matrix.eslint }}
- name: Test
run: pnpm test
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
node_modules
.nyc_output
.DS_Store
docs/.vuepress/dist
docs/.vitepress/cache
docs/.vitepress/dist
coverage
*.log
*.swp
1 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
shamefully-hoist=true
force=true
7 changes: 5 additions & 2 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
!/docs/.vuepress
!/docs/.vitepress
!/.github
!/.vscode
/.nyc_output
/assets
/coverage
/dist
/docs/.vuepress/dist
/docs/.vitepress/dist
/node_modules
/tests/fixtures
/tests-integrations/config-recommended
/docs/.vitepress/cache

# ignore files
/CHANGELOG.md
/.github/ISSUE_TEMPLATE/
/pnpm-lock.yaml
/.changeset
3 changes: 3 additions & 0 deletions .prettierrc.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
// Make the VSCode extension use a local prettier.
}
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"eslint.validate": ["javascript", "typescript", "vue", "markdown"],
"eslint.alwaysShowStatus": true,
"eslint.packageManager": "yarn",
"eslint.packageManager": "pnpm",
"eslint.run": "onSave",
"npm.packageManager": "yarn",
"npm.packageManager": "pnpm",
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
565 changes: 472 additions & 93 deletions CHANGELOG.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
The MIT License (MIT)

Copyright (c) 2021 Yosuke Ota
Copyright (c) 2019 kazuya kawaguchi

Permission is hereby granted, free of charge, to any person obtaining a copy of
36 changes: 30 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -2,29 +2,53 @@

<h1 align="center">@intlify/eslint-plugin-vue-i18n</h1>
<p align="center">
<a href="https://github.com/intlify/eslint-plugin-vue-i18n/actions?query=workflow%3ATest"><img src="https://github.com/intlify/eslint-plugin-vue-i18n/workflows/Test/badge.svg?branch=master" alt="Test Status"></a>
<a href="https://github.com/intlify/eslint-plugin-vue-i18n/actions?query=workflow%3ATest"><img src="https://github.com/intlify/eslint-plugin-vue-i18n/actions/workflows/test.yml/badge.svg" alt="Test Status"></a>
</p>
<p align="center">
<a href="https://www.npmjs.com/package/@intlify/eslint-plugin-vue-i18n"><img src="https://img.shields.io/npm/v/@intlify/eslint-plugin-vue-i18n.svg" alt="NPM"></a>
</p>
<p align="center">ESLint plugin for Vue I18n</p>

## :book: Documentation
<div align="center">

[Online Playground](https://eslint-online-playground.netlify.app/#eslint-plugin-vue-i18n)
[Documentation](https://eslint-plugin-vue-i18n.intlify.dev/)

</div>

<h2 align="center">Supporting Intlify Project</h2>

Intlify Project is an open source project that is included Vue I18n and i18n tooling and libraries with its ongoing development made possible entirely by the support of Sponsors. If you would like to become a sponsor, please consider:

- [Become a Sponsor on GitHub](https://github.com/sponsors/ota-meshi)

## 📔 Documentation

See [here](https://eslint-plugin-vue-i18n.intlify.dev)

## :scroll: Changelog
## 📜 Changelog

Details changes for each release are documented in the [CHANGELOG.md](https://github.com/intlify/eslint-plugin-vue-i18n/blob/master/CHANGELOG.md).

## :exclamation: Issues
## 🚥 Versioning policy

This plugin follows [Semantic Versioning].
However, please note that we do not follow [ESLint's Semantic Versioning Policy].
In minor version releases, this plugin may change the sharable configs provided by the plugin or the default behavior of the plugin's rules in order to add features to the plugin. Because we want to add many features to the plugin soon, so that users can easily take advantage of new features in Vue I18n, Vue and Nuxt.

According to our policy, any minor update may report more linting errors than the previous release. As such, we recommend using the [tilde (`~`)](https://semver.npmjs.com/#syntax-examples) in `package.json` to guarantee the results of your builds.

[Semantic Versioning]: https://semver.org/
[ESLint's Semantic Versioning Policy]: https://github.com/eslint/eslint#semantic-versioning-policy

## ❗ Issues

Please make sure to read the [Issue Reporting Checklist](https://github.com/intlify/eslint-plugin-vue-i18n/blob/master/CONTRIBUTING.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines may be closed immediately.

## :muscle: Contribution
## 💪 Contribution

Please make sure to read the [Contributing Guide](https://github.com/intlify/eslint-plugin-vue-i18n/blob/master/.github/CONTRIBUTING.md) before making a pull request.

## :copyright: License
## ©️ License

[MIT](http://opensource.org/licenses/MIT)
Original file line number Diff line number Diff line change
@@ -2,12 +2,12 @@
<div class="eslint-code-container">
<eslint-editor
ref="editor"
v-model="code"
:linter="linter"
:config="config"
v-model="code"
:style="{ height }"
class="eslint-code-block"
:filename="resplvedFilename"
:filename="'/path/' + resplvedFilename"
:language="language"
dark
:format="format"
@@ -17,7 +17,6 @@
</template>

<script>
import './setup'
import EslintEditor from 'vue-eslint-editor'
import { rules } from '../../../'
import { setTimeouts } from '../../../dist/utils/default-timeouts'
@@ -46,7 +45,8 @@ export default {
}
},
filename: {
type: String
type: String,
default: undefined
},
language: {
type: String,
@@ -58,7 +58,7 @@ export default {
},
messageSyntaxVersion: {
type: String,
default: '^9'
default: '^11'
}
},
@@ -73,14 +73,6 @@ export default {
}
},
watch: {
code(newCode) {
if (this.$resourceGroup) {
this.$resourceGroup.set(this.resplvedFilename, newCode)
}
}
},
computed: {
isResource() {
return this.language === 'json' || this.language === 'yaml'
@@ -91,10 +83,10 @@ export default {
(this.language === 'json'
? 'example.json'
: this.language === 'yaml'
? 'example.yaml'
: this.language === 'javascript'
? 'example.js'
: 'example.vue')
? 'example.yaml'
: this.language === 'javascript'
? 'example.js'
: 'example.vue')
)
},
config() {
@@ -130,8 +122,8 @@ export default {
this.language === 'json'
? 'jsonc-eslint-parser'
: this.language === 'yaml'
? 'yaml-eslint-parser'
: 'vue-eslint-parser',
? 'yaml-eslint-parser'
: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
@@ -146,8 +138,8 @@ export default {
.getFiles()
.filter(file => /\.(?:json5?|ya?ml)$/i.test(file))
: this.isResource
? [this.resplvedFilename]
: []
? [this.resplvedFilename]
: []
).map(pattern => ({ pattern, localeKey: this.localeKey })),
messageSyntaxVersion: this.messageSyntaxVersion
}
@@ -161,22 +153,11 @@ export default {
}
},
methods: {
computeCodeFromSlot(nodes) {
if (!Array.isArray(nodes)) {
return ''
watch: {
code(newCode) {
if (this.$resourceGroup) {
this.$resourceGroup.set(this.resplvedFilename, newCode)
}
return nodes
.map(node => node.text || this.computeCodeFromSlot(node.children))
.join('')
},
verifyHook() {
setFileContents(
this.$resourceGroup ? this.$resourceGroup.getFileContents() : {}
)
},
lint() {
this.$refs.editor.lint()
}
},
@@ -193,23 +174,16 @@ export default {
)
})
// Load linter.
const [
{ default: Linter },
{ default: coreRules },
vueESLintParser,
jsoncESLintParser,
yamlESLintParser
] = await Promise.all([
import('eslint4b/dist/linter'),
import('eslint4b/dist/core-rules'),
import('espree').then(() => import('vue-eslint-parser')),
import('espree').then(() => import('jsonc-eslint-parser')),
import('yaml-eslint-parser')
])
const [{ Linter }, vueESLintParser, jsoncESLintParser, yamlESLintParser] =
await Promise.all([
import('eslint'),
import('espree').then(() => import('vue-eslint-parser')),
import('espree').then(() => import('jsonc-eslint-parser')),
import('yaml-eslint-parser')
])
const linter = (this.linter = new Linter({ cwd: '/path' }))
linter.defineRules(coreRules)
for (const ruleId of Object.keys(rules)) {
linter.defineRule(`@intlify/vue-i18n/${ruleId}`, rules[ruleId])
}
@@ -229,6 +203,25 @@ export default {
verifyHook()
return verifyAndFix.apply(this, args)
}
},
methods: {
computeCodeFromSlot(nodes) {
if (!Array.isArray(nodes)) {
return ''
}
return nodes
.map(node => node.text || this.computeCodeFromSlot(node.children))
.join('')
},
verifyHook() {
setFileContents(
this.$resourceGroup ? this.$resourceGroup.getFileContents() : {}
)
},
lint() {
this.$refs.editor.lint()
}
}
}
</script>
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@
</template>

<script>
import Vue from 'vue'
export default {
provide() {
let waitSeq = 0
@@ -14,7 +13,7 @@ export default {
return {
$resourceGroup: {
async set(fileName, code) {
Vue.set(data.fileContents, fileName, code)
data.fileContents[`/path/${fileName}`] = code
const timeSeq = ++waitSeq
await Vue.nextTick()
73 changes: 73 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { defineConfig } from 'vitepress'
import { getRulesWithCategories } from '../../scripts/lib/rules'
import '../../scripts/update-rule-docs'
import '../../scripts/update-index-docs'

// https://vitepress.dev/reference/site-config
export default async () => {
const rules = await getRulesWithCategories()
return defineConfig({
base: '/',
title: 'eslint-plugin-vue-i18n',
description: 'ESLint plugin for Vue I18n',
head: [['meta', { name: 'theme-color', content: '#3eaf7c' }]],
lastUpdated: true,
themeConfig: {
editLink: {
pattern:
'https://github.com/intlify/eslint-plugin-vue-i18n/edit/master/docs/:path',
text: 'Edit this page on GitHub'
},
search: {
provider: 'local'
},
nav: [
{
text: 'Support Intlify',
items: [
{
text: 'GitHub Sponsors',
link: 'https://github.com/sponsors/kazupon'
},
{
text: 'Patreon',
link: 'https://www.patreon.com/kazupon'
}
]
},
{
text: 'Release Notes',
link: 'https://github.com/intlify/eslint-plugin-vue-i18n/releases'
}
],
sidebar: [
{
text: 'Introduction',
link: '/intro'
},
{
text: 'Getting Started',
link: '/started'
},
{
text: 'Available Rules',
link: '/rules/'
},
...rules.map(({ category, rules }) => ({
text: `Rules in ${category}`,
collapsed: false,
items: rules.map(rule => ({
text: rule.name,
link: `/rules/${rule.name}`
}))
}))
],
socialLinks: [
{
icon: 'github',
link: 'https://github.com/intlify/eslint-plugin-vue-i18n'
}
]
}
})
}
7 changes: 0 additions & 7 deletions docs/.vuepress/components/setup.js

This file was deleted.

79 changes: 0 additions & 79 deletions docs/.vuepress/config.js

This file was deleted.

Empty file removed docs/.vuepress/public/.nojekyll
Empty file.

This file was deleted.

13 changes: 0 additions & 13 deletions docs/.vuepress/shim/eslint/index.js

This file was deleted.

22 changes: 0 additions & 22 deletions docs/.vuepress/shim/fs/fake-fs.js

This file was deleted.

4 changes: 0 additions & 4 deletions docs/.vuepress/shim/fs/index.js

This file was deleted.

5 changes: 0 additions & 5 deletions docs/.vuepress/shim/glob.js

This file was deleted.

3 changes: 0 additions & 3 deletions docs/.vuepress/shim/module.js

This file was deleted.

23 changes: 0 additions & 23 deletions docs/README.md

This file was deleted.

29 changes: 29 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home

hero:
name: 'eslint-plugin-vue-i18n'
# text: 'ESLint plugin for Vue I18n'
tagline: ESLint plugin for Vue I18n
image:
src: /eslint-plugin-vue-i18n.svg
alt: Logo
actions:
- theme: brand
text: Introduction
link: /intro
- theme: alt
text: Get started
link: /started
- theme: alt
text: Online Playground
link: https://eslint-online-playground.netlify.app/#eslint-plugin-vue-i18n
# features:
# - title: Feature A
# details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
# - title: Feature B
# details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
# - title: Feature C
# details: Lorem ipsum dolor sit amet, consectetur adipiscing elit
---
15 changes: 15 additions & 0 deletions docs/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Introduction

<p align="center"><img width="143px" height="130px" src="/eslint-plugin-vue-i18n.svg" alt="ESLint plugin for Vue I18n logo"></p>

eslint-plugin-vue-i18n is ESLint plugin of Vue I18n. It easily integrates some localization features to your Vue.js Application.

Go to [Get Started](./started.md)

## Become a Sponsor

Is your company using eslint-plugin-vue-i18n, and related [Intlify](https://github.com/intlify) project i18n tools to build awesome apps? Become a sponsor to add your logo on this documentation! Supporting me on GitHub Sponsors allows me to work less for a job and to work more on Free Open Source Software such as eslint-plugin-vue-i18n! Thank you!

<p style="text-align: center;">
<iframe src="https://github.com/sponsors/ota-meshi/card" title="Sponsor ota-meshi" height="225" width="600" style="border: 0;"></iframe>
</p>
2 changes: 2 additions & 0 deletions docs/public/_headers
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
https://eslint-plugin-vue-i18n.pages.dev/*
X-Robots-Tag: noindex
File renamed without changes
File renamed without changes
18 changes: 12 additions & 6 deletions docs/rules/README.md → docs/rules/index.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
# Available Rules

- :star: mark: the rule which is enabled by `plugin:@intlify/vue-i18n/recommended` preset.
- :star: mark: the rule which is enabled by `plugin:@intlify/vue-i18n/recommended` or `*.configs["flat/recommended"]` preset.
- :black_nib: mark: the rule which is fixable by `eslint --fix` command.

## Recommended

<!--prettier-ignore-->
| Rule ID | Description | |
|:--------|:------------|:---|
| [@intlify/vue-i18n/<wbr>no-deprecated-i18n-component](./no-deprecated-i18n-component.html) | disallow using deprecated `<i18n>` components (in Vue I18n 9.0.0+) | :black_nib: |
| [@intlify/vue-i18n/<wbr>no-deprecated-i18n-places-prop](./no-deprecated-i18n-places-prop.html) | disallow using deprecated `places` prop (Removed in Vue I18n 9.0.0+) | |
| [@intlify/vue-i18n/<wbr>no-deprecated-i18n-place-attr](./no-deprecated-i18n-place-attr.html) | disallow using deprecated `place` attribute (Removed in Vue I18n 9.0.0+) | |
| [@intlify/vue-i18n/<wbr>no-deprecated-i18n-component](./no-deprecated-i18n-component.html) | disallow using deprecated `<i18n>` components (in Vue I18n 9.0.0+) | :star::black_nib: |
| [@intlify/vue-i18n/<wbr>no-deprecated-i18n-place-attr](./no-deprecated-i18n-place-attr.html) | disallow using deprecated `place` attribute (Removed in Vue I18n 9.0.0+) | :star: |
| [@intlify/vue-i18n/<wbr>no-deprecated-i18n-places-prop](./no-deprecated-i18n-places-prop.html) | disallow using deprecated `places` prop (Removed in Vue I18n 9.0.0+) | :star: |
| [@intlify/vue-i18n/<wbr>no-deprecated-modulo-syntax](./no-deprecated-modulo-syntax.html) | enforce modulo interpolation to be named interpolation | :star::black_nib: |
| [@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) | :star: |
| [@intlify/vue-i18n/<wbr>no-deprecated-v-t](./no-deprecated-v-t.html) | disallow using deprecated `v-t` custom directive (Deprecated in Vue I18n 11.0.0, removed fully in Vue I18n 12.0.0) | :star: |
| [@intlify/vue-i18n/<wbr>no-html-messages](./no-html-messages.html) | disallow use HTML localization messages | :star: |
| [@intlify/vue-i18n/<wbr>no-i18n-t-path-prop](./no-i18n-t-path-prop.html) | disallow using `path` prop with `<i18n-t>` | :black_nib: |
| [@intlify/vue-i18n/<wbr>no-i18n-t-path-prop](./no-i18n-t-path-prop.html) | disallow using `path` prop with `<i18n-t>` | :star::black_nib: |
| [@intlify/vue-i18n/<wbr>no-missing-keys](./no-missing-keys.html) | disallow missing locale message key at localization methods | :star: |
| [@intlify/vue-i18n/<wbr>no-raw-text](./no-raw-text.html) | disallow to string literal in template or JSX | :star: |
| [@intlify/vue-i18n/<wbr>no-v-html](./no-v-html.html) | disallow use of localization methods on v-html to prevent XSS attack | :star: |
| [@intlify/vue-i18n/<wbr>valid-message-syntax](./valid-message-syntax.html) | disallow invalid message syntax | |
| [@intlify/vue-i18n/<wbr>valid-message-syntax](./valid-message-syntax.html) | disallow invalid message syntax | :star: |

## Best Practices

@@ -27,11 +30,14 @@
| [@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 | |
| [@intlify/vue-i18n/<wbr>no-unknown-locale](./no-unknown-locale.html) | disallow unknown locale name | |
| [@intlify/vue-i18n/<wbr>no-unused-keys](./no-unused-keys.html) | disallow unused localization keys | :black_nib: |
| [@intlify/vue-i18n/<wbr>prefer-sfc-lang-attr](./prefer-sfc-lang-attr.html) | require lang attribute on `<i18n>` block | :black_nib: |

## Stylistic Issues

<!--prettier-ignore-->
| Rule ID | Description | |
|:--------|:------------|:---|
| [@intlify/vue-i18n/<wbr>prefer-linked-key-with-paren](./prefer-linked-key-with-paren.html) | enforce linked key to be enclosed in parentheses | :black_nib: |
| [@intlify/vue-i18n/<wbr>sfc-locale-attr](./sfc-locale-attr.html) | require or disallow the locale attribute on `<i18n>` block | |
37 changes: 34 additions & 3 deletions docs/rules/key-format-style.md
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ This rule aims to enforces specific casing for localization key names.
```yaml
camelCaseKey: The key for this value is camel case.
kebab-case-key: The key for this value is kebab case.
lowercase: The key for this value is lower case.
snake_case_key: The key for this value is snake case.
mixed_Case-key: Perhaps you don't want to use this casing.
```
@@ -41,22 +42,24 @@ Also, the following localization key definitions are reported as errors, because
</eslint-code-block>
## Options
## :gear: Options
```json
{
"@intlify/vue-i18n/key-format-style": [
"error",
"camelCase" | "kebab-case" | "snake_case",
"camelCase" | "kebab-case" | "lowercase" | "snake_case",
{
"allowArray": false
"allowArray": false,
"splitByDots": false
}
]
}
```

- Primary Option: Select the casing you want to apply. It set to `"camelCase"` as default
- `allowArray`: If `true`, allow the use of arrays. If `false`, disallow the use of arrays. It set to `false` as default.
- `splitByDots`: If `true`, check the values of the key name split by dots.

:+1: Examples of **correct** code for this rule with `"camelCase"`:

@@ -112,6 +115,34 @@ app_title: I18N Management System
</eslint-code-block>
:+1: Examples of **correct** code for this rule with `"lowercase"`:
<eslint-code-block language="yaml">
```yaml
# eslint @intlify/vue-i18n/key-format-style: ['error', 'lowercase']

# ✓ GOOD
apptitle: I18N Management System
```
</eslint-code-block>
:-1: Examples of **incorrect** code for this rule with `"lowercase"`:
<eslint-code-block language="yaml">
```yaml
# eslint @intlify/vue-i18n/key-format-style: ['error', 'lowercase']

# ✗ BAD
appTitle: I18N Management System
app_title: I18N Management System
APP_TITLE: I18N Management System
```
</eslint-code-block>
:+1: Examples of **correct** code for this rule with `"snake_case"`:
<eslint-code-block language="yaml">
2 changes: 1 addition & 1 deletion docs/rules/keys-order.md
Original file line number Diff line number Diff line change
@@ -44,7 +44,7 @@ This rule is useful to provide browsability of localization keys.

</eslint-code-block>

## Options
## :gear: Options

```json
{
1 change: 1 addition & 0 deletions docs/rules/no-deprecated-i18n-component.md
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ since: v0.11.0

> disallow using deprecated `<i18n>` components (in Vue I18n 9.0.0+)
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.
- :black_nib:️ The `--fix` option on the [command line](http://eslint.org/docs/user-guide/command-line-interface#fix) can automatically fix some of the problems reported by this rule.

If you are migrating from Vue I18n v8 to v9, the `<i18n>` component should be replaced with the `<i18n-t>` component.
2 changes: 2 additions & 0 deletions docs/rules/no-deprecated-i18n-place-attr.md
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@ since: v0.11.0

> disallow using deprecated `place` attribute (Removed in Vue I18n 9.0.0+)
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.

If you are migrating from Vue I18n v8 to v9, the `place` attribute should be replaced with the `v-slot`.

## :book: Rule Details
2 changes: 2 additions & 0 deletions docs/rules/no-deprecated-i18n-places-prop.md
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@ since: v0.11.0

> disallow using deprecated `places` prop (Removed in Vue I18n 9.0.0+)
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.

If you are migrating from Vue I18n v8 to v9, the `places` prop should be replaced with the `v-slot`.

## :book: Rule Details
57 changes: 57 additions & 0 deletions docs/rules/no-deprecated-modulo-syntax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
title: '@intlify/vue-i18n/no-deprecated-modulo-syntax'
description: enforce modulo interpolation to be named interpolation
since: v3.0.0
---

# @intlify/vue-i18n/no-deprecated-modulo-syntax

> enforce modulo interpolation to be named interpolation
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.
- :black_nib:️ The `--fix` option on the [command line](http://eslint.org/docs/user-guide/command-line-interface#fix) can automatically fix some of the problems reported by this rule.

This rule enforces modulo interpolation to be named interpolation

## :book: Rule Details

:-1: Examples of **incorrect** code for this rule:

locale messages:

<eslint-code-block fix language="json">

```json
/* eslint @intlify/vue-i18n/no-deprecated-modulo-syntax: 'error' */
{
/* ✗ BAD */
"hello": "%{msg} world"
}
```

</eslint-code-block>

:+1: Examples of **correct** code for this rule:

locale messages (for vue-i18n v9+):

<eslint-code-block fix message-syntax-version="^9" language="json">

```json
/* eslint @intlify/vue-i18n/no-deprecated-modulo-syntax: 'error' */
{
/* ✓ GOOD */
"hello": "{msg} world"
}
```

</eslint-code-block>

## :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-modulo-syntax.ts)
- [Test source](https://github.com/intlify/eslint-plugin-vue-i18n/tree/master/tests/lib/rules/no-deprecated-modulo-syntax.ts)
66 changes: 66 additions & 0 deletions docs/rules/no-deprecated-tc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
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)
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.

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)
66 changes: 66 additions & 0 deletions docs/rules/no-deprecated-v-t.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
title: '@intlify/vue-i18n/no-deprecated-v-t'
description: disallow using deprecated `v-t` custom directive (Deprecated in Vue I18n 11.0.0, removed fully in Vue I18n 12.0.0)
since: v3.2.0
---

# @intlify/vue-i18n/no-deprecated-v-t

> disallow using deprecated `v-t` custom directive (Deprecated in Vue I18n 11.0.0, removed fully in Vue I18n 12.0.0)
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.

If you are migrating from Vue I18n v10 to v11, `v-t` custom directive should be replaced with `t` or `$t`.

## :book: Rule Details

This rule reports use of deprecated `v-t` custom directive (Deprecated in Vue I18n 11.0.0, removed fully in Vue I18n 12.0.0)

:-1: Examples of **incorrect** code for this rule:

<eslint-code-block>

<!-- eslint-skip -->

```vue
<script>
/* eslint @intlify/vue-i18n/no-deprecated-v-t: 'error' */
</script>
<template>
<!-- ✗ BAD -->
<p v-t="'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-v-t: 'error' */
</script>
<template>
<!-- ✓ GOOD -->
<p>{{ $t('banana') }}</p>
</template>
```

</eslint-code-block>

## :books: Further reading

- [Vue I18n > Breaking Changes in v11 - Deprecate Custom Directive `v-t`](https://vue-i18n.intlify.dev/guide/migration/breaking11.html#deprecate-custom-directive-v-t)

## :rocket: Version

This rule was introduced in `@intlify/eslint-plugin-vue-i18n` v3.2.0

## :mag: Implementation

- [Rule source](https://github.com/intlify/eslint-plugin-vue-i18n/blob/master/lib/rules/no-deprecated-v-t.ts)
- [Test source](https://github.com/intlify/eslint-plugin-vue-i18n/tree/master/tests/lib/rules/no-deprecated-v-t.ts)
8 changes: 6 additions & 2 deletions docs/rules/no-dynamic-keys.md
Original file line number Diff line number Diff line change
@@ -66,10 +66,12 @@ localization codes:
import Vue from 'vue'
import VueI18n from 'vue-i18n'

import en from './locales/en.json'

const i18n = new VueI18n({
locale: 'en',
messages: {
en: require('./locales/en.json')
en
}
})

@@ -129,10 +131,12 @@ localization codes:
import Vue from 'vue'
import VueI18n from 'vue-i18n'

import en from './locales/en.json'

const i18n = new VueI18n({
locale: 'en',
messages: {
en: require('./locales/en.json')
en
}
})

12 changes: 8 additions & 4 deletions docs/rules/no-html-messages.md
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ since: v0.1.0

> disallow use HTML localization messages
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` property in a configuration file enables this rule.
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.

This rule reports in order to reduce the risk of injecting potentially unsafe localization message into the browser leading to supply-chain attack or XSS attack.

@@ -29,7 +29,7 @@ locale messages:
{
"hello": "Hello! DIO!",
"hi": "Hi! <span>DIO!</span>",
"contenst": {
"contents": {
"banner": "banner: <iframe src=\"https://banner.domain.com\" frameBorder=\"0\" style=\"z-index:100001;position:fixed;bottom:0;right:0\"/>",
"modal": "modal: <span onmouseover=\"alert(document.cookie);\">modal content</span>"
}
@@ -56,10 +56,12 @@ In localization codes of application:
import Vue from 'vue'
import VueI18n from 'vue-i18n'

import en from './locales/en.json'

const i18n = new VueI18n({
locale: 'en',
messages: {
en: require('./locales/en.json')
en
}
})

@@ -111,6 +113,8 @@ In localization codes of application:
import Vue from 'vue'
import VueI18n from 'vue-i18n'

import en from './locales/en.json'

// import some components used in i18n component
import Banner from './path/to/components/Banner.vue'
import Modal from './path/to/components/Modal.vue'
@@ -122,7 +126,7 @@ Vue.component('Modal', Modal)
const i18n = new VueI18n({
locale: 'en',
messages: {
en: require('./locales/en.json')
en
}
})

1 change: 1 addition & 0 deletions docs/rules/no-i18n-t-path-prop.md
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ since: v0.11.0

> disallow using `path` prop with `<i18n-t>`
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.
- :black_nib:️ The `--fix` option on the [command line](http://eslint.org/docs/user-guide/command-line-interface#fix) can automatically fix some of the problems reported by this rule.

You cannot use `path` prop with `<i18n-t>` component. Perhaps it's an old habit mistake.
2 changes: 1 addition & 1 deletion docs/rules/no-missing-keys-in-other-locales.md
Original file line number Diff line number Diff line change
@@ -68,7 +68,7 @@ locale messages:

</resource-group>

## Options
## :gear: Options

```json
{
10 changes: 7 additions & 3 deletions docs/rules/no-missing-keys.md
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ since: v0.1.0

> disallow missing locale message key at localization methods
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` property in a configuration file enables this rule.
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.

This rule warns locale message key missing if the key does not exist in locale messages.

@@ -71,10 +71,12 @@ localization codes:
/* eslint @intlify/vue-i18n/no-missing-keys: 'error' */
import VueI18n from 'vue-i18n'

import en from './locales/en.json'

const i18n = new VueI18n({
locale: 'en',
messages: {
en: require('./locales/en.json')
en
}
})

@@ -134,10 +136,12 @@ localization codes:
/* eslint @intlify/vue-i18n/no-missing-keys: 'error' */
import VueI18n from 'vue-i18n'

import en from './locales/en.json'

const i18n = new VueI18n({
locale: 'en',
messages: {
en: require('./locales/en.json')
en
}
})

70 changes: 61 additions & 9 deletions docs/rules/no-raw-text.md
Original file line number Diff line number Diff line change
@@ -8,9 +8,12 @@ since: v0.2.0

> disallow to string literal in template or JSX
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` property in a configuration file enables this rule.
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.

This rule warns the usage of string literal.
This rule warns the usage of literal the bellow:

- string literal
- template literals (no epressions, plain text only)

This rule encourage i18n in about the application needs to be localized.

@@ -26,11 +29,11 @@ This rule encourage i18n in about the application needs to be localized.

```js
/* eslint @intlify/vue-i18n/no-raw-text: 'error' */
export default {
export default Vue.extend({
// ✗ BAD
template: '<p>hello</p>'
// ...
}
})
```

</eslint-code-block>
@@ -80,11 +83,11 @@ export default {

```js
/* eslint @intlify/vue-i18n/no-raw-text: 'error' */
export default {
export default Vue.extend({
// ✓ GOOD
template: `<p>{{ \$t('hello') }}</p>`
// ...
}
})
```

</eslint-code-block>
@@ -131,6 +134,17 @@ export default {
"@intlify/vue-i18n/no-raw-text": [
"error",
{
"attributes": {
"/.+/": [
"title",
"aria-label",
"aria-placeholder",
"aria-roledescription",
"aria-valuetext"
],
"input": ["placeholder"],
"img": ["alt"]
},
"ignoreNodes": ["md-icon", "v-icon"],
"ignorePattern": "^[-#:()&]+$",
"ignoreText": ["EUR", "HKD", "USD"]
@@ -139,9 +153,47 @@ export default {
}
```

- `ignoreNodes`: specify nodes to ignore such as icon components
- `ignorePattern`: specify a regexp pattern that matches strings to ignore
- `ignoreText`: specify an array of strings to ignore
- `attributes`: An object whose keys are tag name or patterns and value is an array of attributes to check for that tag name. Default empty.
- `ignoreNodes`: specify nodes to ignore such as icon components. Default empty.
- `ignorePattern`: specify a regexp pattern that matches strings to ignore. Default none.
- `ignoreText`: specify an array of strings to ignore. Default empty.

### `attributes`

<eslint-code-block>

<!-- eslint-skip -->

```vue
<script>
/* eslint @intlify/vue-i18n/no-raw-text: ['error', {attributes: { '/.+/': ['label'] }}] */
</script>
<template>
<!-- ✗ BAD -->
<my-input label="hello" />
<any-component label="hello" />
</template>
```

</eslint-code-block>

<eslint-code-block>

<!-- eslint-skip -->

```vue
<script>
/* eslint @intlify/vue-i18n/no-raw-text: ['error', {attributes: { 'MyInput': ['label'] }}] */
</script>
<template>
<!-- ✗ BAD -->
<my-input label="hello" />
<!-- ✓ GOOD -->
<other-component label="hello" />
</template>
```

</eslint-code-block>

## :rocket: Version

71 changes: 71 additions & 0 deletions docs/rules/no-unknown-locale.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
title: '@intlify/vue-i18n/no-unknown-locale'
description: disallow unknown locale name
since: v1.3.0
---

# @intlify/vue-i18n/no-unknown-locale

> disallow unknown locale name
## :book: Rule Details

This rule reports the use of unknown locale names.

By default, this rule only commonly known locale names specified in [RFC 5646] are allowed.
The rule uses the [is-language-code] package to check if the locale name is compatible with [RFC 5646].

[rfc 5646]: https://datatracker.ietf.org/doc/html/rfc5646
[is-language-code]: https://www.npmjs.com/package/is-language-code

<eslint-code-block>

<!-- eslint-skip -->

```vue
<script>
/* eslint @intlify/vue-i18n/no-unknown-locale: "error" */
</script>
<!-- ✓ GOOD -->
<i18n locale="en">
{
"hello": "Hello!"
}
</i18n>
<!-- ✗ BAD -->
<i18n locale="foo">
{
"hello": "Foo!"
}
</i18n>
```

</eslint-code-block>

## :gear: Options

```json
{
"@intlify/vue-i18n/no-unknown-locale": [
"error",
{
"locales": [],
"disableRFC5646": false
}
]
}
```

- `locales` ... Specify the locale names you want to use specially in an array. The rule excludes the specified name from the check.
- `disableRFC5646` ... If `true`, only the locale names listed in `locales` are allowed.

## :rocket: Version

This rule was introduced in `@intlify/eslint-plugin-vue-i18n` v1.3.0

## :mag: Implementation

- [Rule source](https://github.com/intlify/eslint-plugin-vue-i18n/blob/master/lib/rules/no-unknown-locale.ts)
- [Test source](https://github.com/intlify/eslint-plugin-vue-i18n/tree/master/tests/lib/rules/no-unknown-locale.ts)
10 changes: 8 additions & 2 deletions docs/rules/no-unused-keys.md
Original file line number Diff line number Diff line change
@@ -55,10 +55,12 @@ In localization codes of application:
```js
import VueI18n from 'vue-i18n'

import en from './locales/en.json'

const i18n = new VueI18n({
locale: 'en',
messages: {
en: require('./locales/en.json')
en
}
})

@@ -136,10 +138,12 @@ In localization codes of application:
```js
import VueI18n from 'vue-i18n'

import en from './locales/en.json'

const i18n = new VueI18n({
locale: 'en',
messages: {
en: require('./locales/en.json')
en
}
})

@@ -159,6 +163,7 @@ i18n.t('hi')
{
"src": "./src",
"extensions": [".js", ".vue"],
"ignores": [],
"enableFix": false
}
]
@@ -167,6 +172,7 @@ i18n.t('hi')

- `src`: specify the source codes directory to be able to lint. If you don't set any options, it set to `process.cwd()` as default.
- `extensions`: an array to allow specified lintable target file extension. If you don't set any options, it set to `.js` and `.vue` as default.
- `ignores`: An array of key names and patterns to exclude from the check. If you want to specify a pattern, specify a string such as `/pattern/`.
- `enableFix`: if `true`, enable automatically remove unused keys on `eslint --fix`. If you don't set any options, it set to `false` as default. (This is an experimental feature.)

## :couple: Related Rules
2 changes: 1 addition & 1 deletion docs/rules/no-v-html.md
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ since: v0.1.0

> disallow use of localization methods on v-html to prevent XSS attack
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` property in a configuration file enables this rule.
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.

This rule reports all uses of localization methods on `v-html` directive in order to reduce the risk of injecting potentially unsafe / unescaped html into the browser leading to Cross-Site Scripting (XSS) attacks.

70 changes: 70 additions & 0 deletions docs/rules/prefer-sfc-lang-attr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
title: '@intlify/vue-i18n/prefer-sfc-lang-attr'
description: require lang attribute on `<i18n>` block
since: v1.2.0
---

# @intlify/vue-i18n/prefer-sfc-lang-attr

> require lang attribute on `<i18n>` block
- :black_nib:️ The `--fix` option on the [command line](http://eslint.org/docs/user-guide/command-line-interface#fix) can automatically fix some of the problems reported by this rule.

## :book: Rule Details

This rule enforce `lang` attribute to be specified to `<i18n>` custom block.

:-1: Examples of **incorrect** code for this rule:

locale messages:

<eslint-code-block fix>

<!-- eslint-skip -->

```vue
<i18n>
{
"en": {
"message": "hello!"
}
}
</i18n>
<script>
/* eslint @intlify/vue-i18n/prefer-sfc-lang-attr: 'error' */
</script>
```

</eslint-code-block>

:+1: Examples of **correct** code for this rule:

locale messages:

<eslint-code-block fix>

<!-- eslint-skip -->

```vue
<i18n lang="json">
{
"en": {
"message": "hello!"
}
}
</i18n>
<script>
/* eslint @intlify/vue-i18n/prefer-sfc-lang-attr: 'error' */
</script>
```

</eslint-code-block>

## :rocket: Version

This rule was introduced in `@intlify/eslint-plugin-vue-i18n` v1.2.0

## :mag: Implementation

- [Rule source](https://github.com/intlify/eslint-plugin-vue-i18n/blob/master/lib/rules/prefer-sfc-lang-attr.ts)
- [Test source](https://github.com/intlify/eslint-plugin-vue-i18n/tree/master/tests/lib/rules/prefer-sfc-lang-attr.ts)
94 changes: 94 additions & 0 deletions docs/rules/sfc-locale-attr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
title: '@intlify/vue-i18n/sfc-locale-attr'
description: require or disallow the locale attribute on `<i18n>` block
since: v1.3.0
---

# @intlify/vue-i18n/sfc-locale-attr

> require or disallow the locale attribute on `<i18n>` block
## :book: Rule Details

This rule aims to enforce the `<i18n>` block to use or not the `locale` attribute.

<eslint-code-block>

<!-- eslint-skip -->

```vue
<script>
/* eslint @intlify/vue-i18n/sfc-locale-attr: "error" */
</script>
<!-- ✓ GOOD -->
<i18n locale="en">
{
"message": "hello!"
}
</i18n>
<!-- ✗ BAD -->
<i18n>
{
"en": {
"message": "hello!"
}
}
</i18n>
```

</eslint-code-block>

## :gear: Options

```json
{
"@intlify/vue-i18n/sfc-locale-attr": [
"error",
"always" //"always" or "never"
]
}
```

- `"always"` ... The `<i18n>` blocks requires the locale attribute.
- `"never"` ... Do not use the locale attribute on `<i18n>` blocks.

### Examples of code for this rule with `"never"` option:

<eslint-code-block>

<!-- eslint-skip -->

```vue
<script>
/* eslint @intlify/vue-i18n/sfc-locale-attr: ["error", "never"] */
</script>
<!-- ✓ GOOD -->
<i18n>
{
"en": {
"message": "hello!"
}
}
</i18n>
<!-- ✗ BAD -->
<i18n locale="en">
{
"message": "hello!"
}
</i18n>
```

</eslint-code-block>

## :rocket: Version

This rule was introduced in `@intlify/eslint-plugin-vue-i18n` v1.3.0

## :mag: Implementation

- [Rule source](https://github.com/intlify/eslint-plugin-vue-i18n/blob/master/lib/rules/sfc-locale-attr.ts)
- [Test source](https://github.com/intlify/eslint-plugin-vue-i18n/tree/master/tests/lib/rules/sfc-locale-attr.ts)
2 changes: 2 additions & 0 deletions docs/rules/valid-message-syntax.md
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@ since: v0.10.0

> disallow invalid message syntax
- :star: The `"extends": "plugin:@intlify/vue-i18n/recommended"` or `*.configs["flat/recommended"]` property in a configuration file enables this rule.

This rule warns invalid message syntax.

This rule is useful localization leaks with incorrect message syntax.
204 changes: 151 additions & 53 deletions docs/started.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Getting Started

## :cd: Installation
## 💿 Installation

Use [npm](https://www.npmjs.com/) or a compatible tool.

@@ -10,23 +10,99 @@ npm install --save-dev eslint @intlify/eslint-plugin-vue-i18n

::: tip Requirements

- ESLint v5.0.0 or later
- Node.js v10.13.0 or later
- ESLint v8.0.0 or later
- Node.js v18.x or later

:::

## :rocket: Usage
## 🚀 Usage

Configure your `.eslintrc.*` file.
### Configuration `eslint.config.[c|m]js`

Use `eslint.config.[c|m]js` file to configure rules. This is the default in ESLint v9, but can be used starting from ESLint v8.57.0. See also: https://eslint.org/docs/latest/use/configure/configuration-files-new.

Example eslint.config.js:

```js
import vueI18n from '@intlify/eslint-plugin-vue-i18n'

export default [
// add more generic rulesets here, such as:
// js.configs.recommended, // '@eslint/js'
// ...vue.configs['flat/recommended'], // 'eslint-plugin-vue'

...vueI18n.configs.recommended,
{
rules: {
// Optional.
'@intlify/vue-i18n/no-dynamic-keys': 'error',
'@intlify/vue-i18n/no-unused-keys': [
'error',
{
extensions: ['.js', '.vue']
}
]
},
settings: {
'vue-i18n': {
localeDir: './path/to/locales/*.{json,json5,yaml,yml}', // extension is glob formatting!
// or
// localeDir: {
// pattern: './path/to/locales/*.{json,json5,yaml,yml}', // extension is glob formatting!
// localeKey: 'file' // or 'path' or 'key'
// }
// or
// localeDir: [
// {
// // 'file' case
// pattern: './path/to/locales1/*.{json,json5,yaml,yml}',
// localeKey: 'file'
// },
// {
// // 'path' case
// pattern: './path/to/locales2/*.{json,json5,yaml,yml}',
// localePattern: /^.*\/(?<locale>[A-Za-z0-9-_]+)\/.*\.(json5?|ya?ml)$/,
// localeKey: 'path'
// },
// {
// // 'key' case
// pattern: './path/to/locales3/*.{json,json5,yaml,yml}',
// localeKey: 'key'
// },
// ]

// Specify the version of `vue-i18n` you are using.
// If not specified, the message will be parsed twice.
messageSyntaxVersion: '^11.0.0'
}
}
}
]
```

See the [rule list](./rules/index.md) to get the `configs` & `rules` that this plugin provides.

#### Bundle Configurations `eslint.config.[c|m]js`

This plugin provides some predefined configs. You can use the following configs by adding them to `eslint.config.[c|m]js`. (All flat configs in this plugin are provided as arrays, so spread syntax is required when combining them with other configs.)

- `*.configs.base`: Settings and rules to enable correct ESLint parsing.
- `*.configs.recommended`: Above, plus rules to enforce subjective community defaults to ensure consistency.

### Configuration `.eslintrc.*`

Use `.eslintrc.*` file to configure rules in ESLint < v9. See also: https://eslint.org/docs/latest/use/configure/.

Example `.eslintrc.js`:

For example:

```js
module.export = {
module.exports = {
extends: [
'eslint:recommended',
// Recommended
'plugin:@intlify/vue-i18n/recommended'
'plugin:@intlify/vue-i18n/recommended-legacy'
],
rules: {
// Optional.
@@ -44,49 +120,74 @@ module.export = {
// or
// localeDir: {
// pattern: './path/to/locales/*.{json,json5,yaml,yml}', // extension is glob formatting!
// localeKey: 'file' // or 'key'
// localeKey: 'file' // or 'path' or 'key'
// }
// or
// localeDir: [
// {
// // 'file' case
// pattern: './path/to/locales1/*.{json,json5,yaml,yml}',
// localeKey: 'file' // or 'key'
// localeKey: 'file'
// },
// {
// // 'path' case
// pattern: './path/to/locales2/*.{json,json5,yaml,yml}',
// localeKey: 'file' // or 'key'
// localePattern: /^.*\/(?<locale>[A-Za-z0-9-_]+)\/.*\.(json5?|ya?ml)$/,
// localeKey: 'path'
// },
// {
// // 'key' case
// pattern: './path/to/locales3/*.{json,json5,yaml,yml}',
// localeKey: 'key'
// },
// ]

// Specify the version of `vue-i18n` you are using.
// If not specified, the message will be parsed twice.
messageSyntaxVersion: '^9.0.0'
messageSyntaxVersion: '^11.0.0'
}
}
}
```

See [the rule list](../rules/)
See the [rule list](./rules/index.md) to get the `configs` & `rules` that this plugin provides.

#### Bundle Configurations `.eslintrc.*`

This plugin provides some predefined configs. You can use the following configs by adding them to `.eslintrc.*`.

- `"plugin:@intlify/vue-i18n/base-legacy"`: Settings and rules to enable correct ESLint parsing.
- `"plugin:@intlify/vue-i18n/recommended-legacy"`: Above, plus rules to enforce subjective community defaults to ensure consistency.

### `settings['vue-i18n']`

- `localeDir` ... You can specify a string or an object or an array.
- String option ... A glob for specifying files that store localization messages of project.
- Object option
- `pattern` (`string`) ... A glob for specifying files that store localization messages of project.
- `localeKey` (`'file' | 'key'`) ... Specifies how to determine the locale for localization messages.
- `localeKey` (`'file' | 'path' | 'key'`) ... Specifies how to determine the locale for localization messages.
- `'file'` ... Determine the locale name from the filename. The resource file should only contain messages for that locale. Use this option if you use `vue-cli-plugin-i18n`. This option is also used when String option is specified.
- `'path'` ... Determine the locale name from the path. In this case, the locale must be had structured with your rule on the path. It can be captured with the regular expression named capture. The resource file should only contain messages for that locale.
- `'key'` ... Determine the locale name from the root key name of the file contents. The value of that key should only contain messages for that locale. Used when the resource file is in the format given to the `messages` option of the `VueI18n` constructor option.
- `localePattern` ... Specifies how to determine pattern the locale for localization messages. This option means, when `localeKey` is `'path'`, you will need to capture the locale using a regular expression. You need to use the locale capture as a named capture `?<locale>`, so it’s be able to capture from the path of the locale resources. If you omit it, it will be captured from the resource path with the same regular expression pattern as `vue-cli-plugin-i18n`.
- Array option ... An array of String option and Object option. Useful if you have multiple locale directories.
- `messageSyntaxVersion` (Optional) ... Specify the version of `vue-i18n` you are using. If not specified, the message will be parsed twice. Also, some rules require this setting.

::: warning NOTE

The `localePattern` options does not support SFC i18n custom blocks (`src` attribute), only for resources of files to import when specified in VueI18n's `messages` options (VueI18n v9 and later, `messages` specified in `createI18n`) for resources of files to import.

JavaScript-based locale files have limited support. JavaScript (`.js`) files can be loaded and used with rules that check for missing keys (like `no-missing-keys`), but TypeScript (`.ts`) locale files are not supported. Rules that analyze locale file contents (like `no-duplicate-keys-in-locale` and `no-html-messages`) are not supported for either format. For more information, please see [#32](https://github.com/intlify/eslint-plugin-vue-i18n/issues/32).

:::

### Running ESLint from command line

If you want to run `eslint` from command line, make sure you include the `.vue`, `.json`, `.json5`, `.yaml` and `.yml` extension using [the `--ext` option](https://eslint.org/docs/user-guide/configuring#specifying-file-extensions-to-lint) or a glob pattern because ESLint targets only `.js` files by default.

Examples:

```bash
```sh
eslint --ext .js,.vue,.json src
eslint "src/**/*.{js,vue,json}"
# Specify the extension you use.
@@ -102,24 +203,21 @@ eslint "src/**/*.{js,vue,json}"

If you want to use custom parsers such as [babel-eslint](https://www.npmjs.com/package/babel-eslint) or [typescript-eslint-parser](https://www.npmjs.com/package/typescript-eslint-parser), you have to use `parserOptions.parser` option instead of `parser` option. Because this plugin requires [vue-eslint-parser](https://www.npmjs.com/package/vue-eslint-parser) to parse `.vue` files, so this plugin doesn't work if you overwrote `parser` option.

Also, `parserOptions` configured at the top level affect `.json` and `.yaml`. This plugin needs to use special parsers to parse `.json` and `.yaml`, so to correctly parse each extension, use the `overrides` option and overwrite the options again.

```diff
- "parser": "babel-eslint",
"parserOptions": {
+ "parser": "babel-eslint",
import vueEslintParser from "vue-eslint-parser"
import babelEslint from "babel-eslint"

export default {
"files": ["**/*.vue"],
"languageOptions": {
- "parser": babelEslint,
+ "parser": vueEslintParser,
"parserOptions": {
+ "parser": babelEslint,
"sourceType": "module"
},
+ "overrides": [
+ {
+ "files": ["*.json", "*.json5"],
+ "extends": ["plugin:@intlify/vue-i18n/base"],
+ },
+ {
+ "files": ["*.yaml", "*.yml"],
+ "extends": ["plugin:@intlify/vue-i18n/base"],
+ }
+ ]
},
}
}
```

### More lint on JSON and YAML in `<i18n>` block
@@ -128,41 +226,41 @@ You can install [eslint-plugin-jsonc](https://ota-meshi.github.io/eslint-plugin-

You can also use [jsonc/vue-custom-block/no-parsing-error](https://ota-meshi.github.io/eslint-plugin-jsonc/rules/vue-custom-block/no-parsing-error.html) and [yml/vue-custom-block/no-parsing-error](https://ota-meshi.github.io/eslint-plugin-yml/rules/vue-custom-block/no-parsing-error.html) rules to find JSON and YAML parsing errors.

## :question: FAQ
## 🚥 Versioning policy

### What is the "Use the latest vue-eslint-parser" error?
This plugin follows [Semantic Versioning].
However, please note that we do not follow [ESLint's Semantic Versioning Policy].
In minor version releases, this plugin may change the sharable configs provided by the plugin or the default behavior of the plugin's rules in order to add features to the plugin. Because we want to add many features to the plugin soon, so that users can easily take advantage of new features in Vue I18n, Vue and Nuxt.

The most rules of `eslint-plugin-vue-i18n` require `vue-eslint-parser` to check `<template>` ASTs.
According to our policy, any minor update may report more linting errors than the previous release. As such, we recommend using the [tilde (`~`)](https://semver.npmjs.com/#syntax-examples) in `package.json` to guarantee the results of your builds.

Make sure you have one of the following settings in your **.eslintrc**:
[Semantic Versioning]: https://semver.org/
[ESLint's Semantic Versioning Policy]: https://github.com/eslint/eslint#semantic-versioning-policy

- `"extends": ["plugin:@intlify/vue-i18n/recommended"]`
- `"extends": ["plugin:@intlify/vue-i18n/base"]`
## ❓ FAQ

If you already use other parser (e.g. `"parser": "babel-eslint"`), please move it into `parserOptions`, so it doesn't collide with the `vue-eslint-parser` used by this plugin's configuration:
### What is the "Use the latest vue-eslint-parser" error?

```diff
- "parser": "babel-eslint",
"parserOptions": {
+ "parser": "babel-eslint",
"ecmaVersion": 2017,
"sourceType": "module"
}
```
The most rules of `eslint-plugin-vue-i18n` require `vue-eslint-parser` to check `<template>` ASTs.

Make sure you have one of the following settings in your **eslint.config.js**:

- `plugin.configs.base`
- `plugin.configs.recommended`

See also: "[Use together with custom parsers](#use-together-with-custom-parsers)" section.
See also: "[How to use custom parser](#how-to-use-custom-parser)" section.

### Why doesn't it work on .vue file?

1. Make sure you don't have `eslint-plugin-html` in your config. The `eslint-plugin-html` extracts the content from `<script>` tags, but `eslint-plugin-vue` requires `<script>` tags and `<template>` tags in order to distinguish template and script in single file components.

```diff
"plugins": [
"vue",
- "html"
]
```
```diff
"plugins": [
"vue",
- "html"
]
```

2. Make sure your tool is set to lint `.vue` and `.json` files.

- CLI targets only `.js` files by default. You have to specify additional extensions by `--ext` option or glob patterns. E.g. `eslint "src/**/*.{js,vue,json}"` or `eslint src --ext .vue,.json`.o
- CLI targets only `.js` files by default. You have to specify additional extensions by `--ext` option or glob patterns. E.g. `eslint "src/**/*.{js,vue,json}"` or `eslint src --ext .vue,.json`.
93 changes: 93 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import pluginVue from 'eslint-plugin-vue'
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
import markdown from 'eslint-plugin-markdown'
import tseslint from 'typescript-eslint'

export default [
...pluginVue.configs['flat/recommended'],
eslintPluginPrettierRecommended,
...markdown.configs.recommended,
{
rules: {
'object-shorthand': 'error',
'no-debugger': 'error',
'vue/multi-word-component-names': 'off',
'prefer-template': 'error',
'no-restricted-properties': [
'error',
{
object: 'context',
property: 'getSourceCode',
message: 'Use lib/utils/compat.ts'
},
{
object: 'context',
property: 'getFilename',
message: 'Use lib/utils/compat.ts'
},
{
object: 'context',
property: 'getPhysicalFilename',
message: 'Use lib/utils/compat.ts'
},
{
object: 'context',
property: 'getCwd',
message: 'Use lib/utils/compat.ts'
},
{
object: 'context',
property: 'getScope',
message: 'Use lib/utils/compat.ts'
},
{
object: 'context',
property: 'parserServices',
message: 'Use lib/utils/compat.ts'
}
]
}
},
...tseslint.config({
files: ['*.ts', '**/*.ts', '*.mts', '**/*.mts'],
extends: [...tseslint.configs.recommended],
rules: {
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/consistent-type-imports': 'error'
}
}),
{
files: ['*.vue', '**/*.vue'],
languageOptions: {
parserOptions: {
parser: tseslint.parser
}
}
},
{
files: ['js', 'mjs', 'cjs', 'ts', 'mts', 'cts', 'vue'].map(ext => [
`**/*.md/*.${ext}`
]),
rules: {
'prettier/prettier': 'off',
'vue/no-v-html': 'off'
}
},
{
ignores: [
'!docs/.vitepress/',
'!.github/',
'!.vscode/',
'.nyc_output/',
'assets/',
'coverage/',
'dist/',
'docs/.vitepress/cache/',
'docs/.vitepress/dist/',
'lib/configs/**/*.ts',
'node_modules/',
'tests/integrations/'
// 'tests/fixtures/' // Do not specify it here for the test to work.
]
}
]
8 changes: 0 additions & 8 deletions lib/configs.ts

This file was deleted.

7 changes: 1 addition & 6 deletions lib/configs/base.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
/** DON'T EDIT THIS FILE; was created by scripts. */
export = {
parser: require.resolve('vue-eslint-parser'),
plugins: ['@intlify/vue-i18n'],
overrides: [
{
files: ['*.json', '*.json5'],
// TODO: If you do not use vue-eslint-parser, you will get an error in vue rules.
// see https://github.com/vuejs/eslint-plugin-vue/pull/1262
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
parser: require.resolve('jsonc-eslint-parser')
}
},
{
files: ['*.yaml', '*.yml'],
// TODO: If you do not use vue-eslint-parser, you will get an error in vue rules.
// see https://github.com/vuejs/eslint-plugin-vue/pull/1262
parser: require.resolve('vue-eslint-parser'),
parserOptions: {
parser: require.resolve('yaml-eslint-parser')
},
rules: {
// ESLint core rules known to cause problems with YAML.
// https://github.com/ota-meshi/eslint-plugin-yml/blob/4e468109b9d2f4376b8d4d1221adba27c6ee04b2/src/configs/base.ts#L7-L11
'no-irregular-whitespace': 'off',
'spaced-comment': 'off'
}
35 changes: 35 additions & 0 deletions lib/configs/flat/base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/** DON'T EDIT THIS FILE; was created by scripts. */
export = [
{
name: '@intlify/vue-i18n:base:setup',
plugins: {
get '@intlify/vue-i18n'() {
return require('../../index')
}
}
},
{
name: '@intlify/vue-i18n:base:setup:json',
files: ['*.json', '**/*.json', '*.json5', '**/*.json5'],
languageOptions: {
parser: require('vue-eslint-parser'),
parserOptions: {
parser: require('jsonc-eslint-parser')
}
}
},
{
name: '@intlify/vue-i18n:base:setup:yaml',
files: ['*.yaml', '**/*.yaml', '*.yml', '**/*.yml'],
languageOptions: {
parser: require('vue-eslint-parser'),
parserOptions: {
parser: require('yaml-eslint-parser')
}
},
rules: {
'no-irregular-whitespace': 'off',
'spaced-comment': 'off'
}
}
]
36 changes: 36 additions & 0 deletions lib/configs/flat/recommended.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/** DON'T EDIT THIS FILE; was created by scripts. */
const globals = require('globals')
const config = require('./base')
export = [
...config,
{
name: '@intlify/vue-i18n:recommended:setup',
languageOptions: {
ecmaVersion: 2018,
sourceType: 'module',
globals: globals.browser,
parserOptions: {
ecmaFeatures: {
jsx: true
}
}
}
},
{
name: '@intlify/vue-i18n:recommended:rules',
rules: {
'@intlify/vue-i18n/no-deprecated-i18n-component': 'warn',
'@intlify/vue-i18n/no-deprecated-i18n-place-attr': 'warn',
'@intlify/vue-i18n/no-deprecated-i18n-places-prop': 'warn',
'@intlify/vue-i18n/no-deprecated-modulo-syntax': 'warn',
'@intlify/vue-i18n/no-deprecated-tc': 'warn',
'@intlify/vue-i18n/no-deprecated-v-t': 'warn',
'@intlify/vue-i18n/no-html-messages': 'warn',
'@intlify/vue-i18n/no-i18n-t-path-prop': 'warn',
'@intlify/vue-i18n/no-missing-keys': 'warn',
'@intlify/vue-i18n/no-raw-text': 'warn',
'@intlify/vue-i18n/no-v-html': 'warn',
'@intlify/vue-i18n/valid-message-syntax': 'warn'
}
}
]
10 changes: 9 additions & 1 deletion lib/configs/recommended.ts
Original file line number Diff line number Diff line change
@@ -13,9 +13,17 @@ export = {
es6: true
},
rules: {
'@intlify/vue-i18n/no-deprecated-i18n-component': 'warn',
'@intlify/vue-i18n/no-deprecated-i18n-place-attr': 'warn',
'@intlify/vue-i18n/no-deprecated-i18n-places-prop': 'warn',
'@intlify/vue-i18n/no-deprecated-modulo-syntax': 'warn',
'@intlify/vue-i18n/no-deprecated-tc': 'warn',
'@intlify/vue-i18n/no-deprecated-v-t': 'warn',
'@intlify/vue-i18n/no-html-messages': 'warn',
'@intlify/vue-i18n/no-i18n-t-path-prop': 'warn',
'@intlify/vue-i18n/no-missing-keys': 'warn',
'@intlify/vue-i18n/no-raw-text': 'warn',
'@intlify/vue-i18n/no-v-html': 'warn'
'@intlify/vue-i18n/no-v-html': 'warn',
'@intlify/vue-i18n/valid-message-syntax': 'warn'
}
}
73 changes: 65 additions & 8 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,68 @@
/**
* @fileoverview ESLint plugin for vue-i18n
* @author kazuya kawaguchi (a.k.a. kazupon)
*/
import configs from './configs'
import rules from './rules'
/** DON'T EDIT THIS FILE; was created by scripts. */
// configs
import baseLegacy from './configs/base'
import recommendedLegacy from './configs/recommended'
import base from './configs/flat/base'
import recommended from './configs/flat/recommended'

// rules
import keyFormatStyle from './rules/key-format-style'
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 noDeprecatedVT from './rules/no-deprecated-v-t'
import noDuplicateKeysInLocale from './rules/no-duplicate-keys-in-locale'
import noDynamicKeys from './rules/no-dynamic-keys'
import noHtmlMessages from './rules/no-html-messages'
import noI18nTPathProp from './rules/no-i18n-t-path-prop'
import noMissingKeysInOtherLocales from './rules/no-missing-keys-in-other-locales'
import noMissingKeys from './rules/no-missing-keys'
import noRawText from './rules/no-raw-text'
import noUnknownLocale from './rules/no-unknown-locale'
import noUnusedKeys from './rules/no-unused-keys'
import noVHtml from './rules/no-v-html'
import preferLinkedKeyWithParen from './rules/prefer-linked-key-with-paren'
import preferSfcLangAttr from './rules/prefer-sfc-lang-attr'
import sfcLocaleAttr from './rules/sfc-locale-attr'
import validMessageSyntax from './rules/valid-message-syntax'

// export plugin
export = {
configs,
rules
configs: {
// eslintrc configs
'base-legacy': baseLegacy,
'recommended-legacy': recommendedLegacy,

// flat configs
base,
recommended,
// flat configs (Backward compatibility)
'flat/base': base,
'flat/recommended': recommended
},
rules: {
'key-format-style': keyFormatStyle,
'no-deprecated-i18n-component': noDeprecatedI18nComponent,
'no-deprecated-i18n-place-attr': noDeprecatedI18nPlaceAttr,
'no-deprecated-i18n-places-prop': noDeprecatedI18nPlacesProp,
'no-deprecated-modulo-syntax': noDeprecatedModuloSyntax,
'no-deprecated-tc': noDeprecatedTc,
'no-deprecated-v-t': noDeprecatedVT,
'no-duplicate-keys-in-locale': noDuplicateKeysInLocale,
'no-dynamic-keys': noDynamicKeys,
'no-html-messages': noHtmlMessages,
'no-i18n-t-path-prop': noI18nTPathProp,
'no-missing-keys-in-other-locales': noMissingKeysInOtherLocales,
'no-missing-keys': noMissingKeys,
'no-raw-text': noRawText,
'no-unknown-locale': noUnknownLocale,
'no-unused-keys': noUnusedKeys,
'no-v-html': noVHtml,
'prefer-linked-key-with-paren': preferLinkedKeyWithParen,
'prefer-sfc-lang-attr': preferSfcLangAttr,
'sfc-locale-attr': sfcLocaleAttr,
'valid-message-syntax': validMessageSyntax
}
}
34 changes: 0 additions & 34 deletions lib/rules.ts

This file was deleted.

142 changes: 114 additions & 28 deletions lib/rules/key-format-style.ts
Original file line number Diff line number Diff line change
@@ -9,39 +9,118 @@ import debugBuilder from 'debug'
import type { RuleContext, RuleListener } from '../types'
import { getCasingChecker } from '../utils/casing'
import type { LocaleMessage } from '../utils/locale-messages'
import { createRule } from '../utils/rule'
import { getFilename, getSourceCode } from '../utils/compat'
const debug = debugBuilder('eslint-plugin-vue-i18n:key-format-style')

const allowedCaseOptions = ['camelCase', 'kebab-case', 'snake_case'] as const
type CaseOption = typeof allowedCaseOptions[number]
const allowedCaseOptions = [
'camelCase',
'kebab-case',
'lowercase',
'snake_case',
'SCREAMING_SNAKE_CASE'
] as const
type CaseOption = (typeof allowedCaseOptions)[number]

function create(context: RuleContext): RuleListener {
const filename = context.getFilename()
const filename = getFilename(context)
const sourceCode = getSourceCode(context)
const expectCasing: CaseOption = context.options[0] ?? 'camelCase'
const checker = getCasingChecker(expectCasing)
const allowArray: boolean = context.options[1]?.allowArray
const splitByDotsOption: boolean = context.options[1]?.splitByDots

function reportUnknown(reportNode: YAMLAST.YAMLNode) {
context.report({
message: `Unexpected object key. Use ${expectCasing} string key instead`,
loc: reportNode.loc
})
}
function verifyKey(
key: string | number,
reportNode: JSONAST.JSONNode | YAMLAST.YAMLNode
) {
if (typeof key === 'number') {
if (!allowArray) {
function verifyKeyForString(
key: string,
reportNode:
| JSONAST.JSONProperty['key']
| NonNullable<YAMLAST.YAMLPair['key']>
): void {
for (const target of splitByDotsOption && key.includes('.')
? splitByDots(key, reportNode)
: [{ key, loc: reportNode.loc }]) {
if (!checker(target.key)) {
context.report({
message: `Unexpected array element`,
loc: reportNode.loc
message: `"{{key}}" is not {{expectCasing}}`,
loc: target.loc,
data: {
key: target.key,
expectCasing
}
})
}
} else {
if (!checker(key)) {
context.report({
message: `"${key}" is not ${expectCasing}`,
loc: reportNode.loc
}
}
function verifyKeyForNumber(
key: number,
reportNode:
| NonNullable<JSONAST.JSONArrayExpression['elements'][number]>
| NonNullable<YAMLAST.YAMLSequence['entries'][number]>
): void {
if (!allowArray) {
context.report({
message: `Unexpected array element`,
loc: reportNode.loc
})
}
}

function splitByDots(
key: string,
reportNode:
| JSONAST.JSONProperty['key']
| NonNullable<YAMLAST.YAMLPair['key']>
) {
const result: {
key: string
loc: JSONAST.SourceLocation
}[] = []
let startIndex = 0
let index
while ((index = key.indexOf('.', startIndex)) >= 0) {
const getLoc = buildGetLocation(startIndex, index)
result.push({
key: key.slice(startIndex, index),
get loc() {
return getLoc()
}
})

startIndex = index + 1
}

const getLoc = buildGetLocation(startIndex, key.length)
result.push({
key: key.slice(startIndex, index),
get loc() {
return getLoc()
}
})

return result

function buildGetLocation(start: number, end: number) {
const offset =
reportNode.type === 'JSONLiteral' ||
(reportNode.type === 'YAMLScalar' &&
(reportNode.style === 'double-quoted' ||
reportNode.style === 'single-quoted'))
? reportNode.range[0] + 1
: reportNode.range[0]
let cachedLoc: JSONAST.SourceLocation | undefined
return () => {
if (cachedLoc) {
return cachedLoc
}
return (cachedLoc = {
start: sourceCode.getLocFromIndex(offset + start),
end: sourceCode.getLocFromIndex(offset + end)
})
}
}
@@ -58,7 +137,7 @@ function create(context: RuleContext): RuleListener {
upper?: KeyStack
}
let keyStack: KeyStack = {
inLocale: targetLocaleMessage.localeKey === 'file'
inLocale: targetLocaleMessage.isResolvedLocaleByFileName()
}
return {
JSONProperty(node: JSONAST.JSONProperty) {
@@ -75,7 +154,7 @@ function create(context: RuleContext): RuleListener {
const key =
node.key.type === 'JSONLiteral' ? `${node.key.value}` : node.key.name

verifyKey(key, node.key)
verifyKeyForString(key, node.key)
},
'JSONProperty:exit'() {
keyStack = keyStack.upper!
@@ -86,7 +165,7 @@ function create(context: RuleContext): RuleListener {
}
) {
const key = node.parent.elements.indexOf(node)
verifyKey(key, node)
verifyKeyForNumber(key, node)
}
}
}
@@ -105,7 +184,7 @@ function create(context: RuleContext): RuleListener {
upper?: KeyStack
}
let keyStack: KeyStack = {
inLocale: targetLocaleMessage.localeKey === 'file'
inLocale: targetLocaleMessage.isResolvedLocaleByFileName()
}
function withinKey(node: YAMLAST.YAMLNode) {
for (const keyNode of yamlKeyNodes) {
@@ -141,7 +220,7 @@ function create(context: RuleContext): RuleListener {
} else if (node.key.type === 'YAMLScalar') {
const keyValue = node.key.value
const key = typeof keyValue === 'string' ? keyValue : String(keyValue)
verifyKey(key, node.key)
verifyKeyForString(key, node.key)
} else {
reportUnknown(node)
}
@@ -158,7 +237,7 @@ function create(context: RuleContext): RuleListener {
return
}
const key = node.parent.entries.indexOf(node)
verifyKey(key, node)
verifyKeyForNumber(key, node)
}
}
}
@@ -187,17 +266,20 @@ function create(context: RuleContext): RuleListener {
return createVisitorForYaml(targetLocaleMessage)
}
)
} else if (context.parserServices.isJSON || context.parserServices.isYAML) {
} else if (
sourceCode.parserServices.isJSON ||
sourceCode.parserServices.isYAML
) {
const localeMessages = getLocaleMessages(context)
const targetLocaleMessage = localeMessages.findExistLocaleMessage(filename)
if (!targetLocaleMessage) {
debug(`ignore ${filename} in key-format-style`)
return {}
}

if (context.parserServices.isJSON) {
if (sourceCode.parserServices.isJSON) {
return createVisitorForJson(targetLocaleMessage)
} else if (context.parserServices.isYAML) {
} else if (sourceCode.parserServices.isYAML) {
return createVisitorForYaml(targetLocaleMessage)
}
return {}
@@ -207,29 +289,33 @@ function create(context: RuleContext): RuleListener {
}
}

export = {
export = createRule({
meta: {
type: 'layout',
docs: {
description: 'enforce specific casing for localization keys',
category: 'Best Practices',
url: 'https://eslint-plugin-vue-i18n.intlify.dev/rules/key-format-style.html',
recommended: false
},
fixable: null,
schema: [
{
enum: allowedCaseOptions
enum: [...allowedCaseOptions]
},
{
type: 'object',
properties: {
allowArray: {
type: 'boolean'
},
splitByDots: {
type: 'boolean'
}
},
additionalProperties: false
}
]
},
create
}
})
12 changes: 8 additions & 4 deletions lib/rules/no-deprecated-i18n-component.ts
Original file line number Diff line number Diff line change
@@ -4,14 +4,17 @@
import { defineTemplateBodyVisitor } from '../utils/index'
import type { RuleContext, RuleListener } from '../types'
import type { AST as VAST } from 'vue-eslint-parser'
import { createRule } from '../utils/rule'
import { getSourceCode } from '../utils/compat'

function create(context: RuleContext): RuleListener {
return defineTemplateBodyVisitor(context, {
VElement(node: VAST.VElement) {
if (node.name !== 'i18n') {
return
}
const tokenStore = context.parserServices.getTemplateBodyTokenStore()
const sourceCode = getSourceCode(context)
const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
const tagNameToken = tokenStore.getFirstToken(node.startTag)
context.report({
node: tagNameToken,
@@ -81,14 +84,15 @@ function create(context: RuleContext): RuleListener {
})
}

export = {
export = createRule({
meta: {
type: 'problem',
docs: {
description:
'disallow using deprecated `<i18n>` components (in Vue I18n 9.0.0+)',
category: 'Recommended',
recommended: false
url: 'https://eslint-plugin-vue-i18n.intlify.dev/rules/no-deprecated-i18n-component.html',
recommended: true
},
fixable: 'code',
schema: [],
@@ -98,4 +102,4 @@ export = {
}
},
create
}
})
8 changes: 5 additions & 3 deletions lib/rules/no-deprecated-i18n-place-attr.ts
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import {
} from '../utils/index'
import type { RuleContext, RuleListener } from '../types'
import type { AST as VAST } from 'vue-eslint-parser'
import { createRule } from '../utils/rule'

function create(context: RuleContext): RuleListener {
return defineTemplateBodyVisitor(context, {
@@ -32,14 +33,15 @@ function create(context: RuleContext): RuleListener {
})
}

export = {
export = createRule({
meta: {
type: 'problem',
docs: {
description:
'disallow using deprecated `place` attribute (Removed in Vue I18n 9.0.0+)',
category: 'Recommended',
recommended: false
url: 'https://eslint-plugin-vue-i18n.intlify.dev/rules/no-deprecated-i18n-place-attr.html',
recommended: true
},
fixable: null,
schema: [],
@@ -48,4 +50,4 @@ export = {
}
},
create
}
})
8 changes: 5 additions & 3 deletions lib/rules/no-deprecated-i18n-places-prop.ts
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import {
} from '../utils/index'
import type { RuleContext, RuleListener } from '../types'
import type { AST as VAST } from 'vue-eslint-parser'
import { createRule } from '../utils/rule'

function create(context: RuleContext): RuleListener {
return defineTemplateBodyVisitor(context, {
@@ -27,14 +28,15 @@ function create(context: RuleContext): RuleListener {
})
}

export = {
export = createRule({
meta: {
type: 'problem',
docs: {
description:
'disallow using deprecated `places` prop (Removed in Vue I18n 9.0.0+)',
category: 'Recommended',
recommended: false
url: 'https://eslint-plugin-vue-i18n.intlify.dev/rules/no-deprecated-i18n-places-prop.html',
recommended: true
},
fixable: null,
schema: [],
@@ -43,4 +45,4 @@ export = {
}
},
create
}
})
130 changes: 130 additions & 0 deletions lib/rules/no-deprecated-modulo-syntax.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* @author kazuya kawaguchi (a.k.a. kazupon)
*/
import type { AST as JSONAST } from 'jsonc-eslint-parser'
import type { AST as YAMLAST } from 'yaml-eslint-parser'
import type { RuleContext, RuleListener } from '../types'
import type { GetReportOffset } from '../utils/rule'
import type { CustomBlockVisitorFactory } from '../types/vue-parser-services'
import { extname } from 'node:path'
import debugBuilder from 'debug'
import { defineCustomBlocksVisitor, getLocaleMessages } from '../utils/index'
import {
getMessageSyntaxVersions,
NodeTypes
} from '../utils/message-compiler/utils'
import { parse } from '../utils/message-compiler/parser-v9'
import { traverseNode } from '../utils/message-compiler/traverser'
import {
createRule,
defineCreateVisitorForJson,
defineCreateVisitorForYaml
} from '../utils/rule'
import { getFilename, getSourceCode } from '../utils/compat'

const debug = debugBuilder('eslint-plugin-vue-i18n:no-deprecated-modulo-syntax')

function create(context: RuleContext): RuleListener {
const filename = getFilename(context)
const sourceCode = getSourceCode(context)
const messageSyntaxVersions = getMessageSyntaxVersions(context)

function verifyForV9(
message: string,
reportNode: JSONAST.JSONStringLiteral | YAMLAST.YAMLScalar,
getReportOffset: GetReportOffset
) {
const { ast, errors } = parse(message)
if (errors.length) {
return
}
traverseNode(ast, node => {
if (node.type !== NodeTypes.Named || !node.modulo) {
return
}
let range: [number, number] | null = null
const start = getReportOffset(node.loc!.start.offset)
const end = getReportOffset(node.loc!.end.offset)
if (start != null && end != null) {
// Subtract `%` length (1), because we want to fix modulo
range = [start - 1, end]
}
context.report({
loc: range
? {
start: sourceCode.getLocFromIndex(range[0]),
end: sourceCode.getLocFromIndex(range[1])
}
: reportNode.loc,
message:
'The modulo interpolation must be enforced to named interpolation.',
fix(fixer) {
return range ? fixer.removeRange([range[0], range[0] + 1]) : null
}
})
})
}

function verifyMessage(
message: string,
reportNode: JSONAST.JSONStringLiteral | YAMLAST.YAMLScalar,
getReportOffset: GetReportOffset
) {
if (messageSyntaxVersions.reportIfMissingSetting()) {
return
}
if (messageSyntaxVersions.v9) {
verifyForV9(message, reportNode, getReportOffset)
}
}

const createVisitorForJson = defineCreateVisitorForJson(verifyMessage)
const createVisitorForYaml = defineCreateVisitorForYaml(verifyMessage)

if (extname(filename) === '.vue') {
return defineCustomBlocksVisitor(
context,
createVisitorForJson,
createVisitorForYaml
)
} else if (
sourceCode.parserServices.isJSON ||
sourceCode.parserServices.isYAML
) {
const localeMessages = getLocaleMessages(context)
const targetLocaleMessage = localeMessages.findExistLocaleMessage(filename)
if (!targetLocaleMessage) {
debug(`ignore ${filename} in no-deprecated-modulo-syntax`)
return {}
}

if (sourceCode.parserServices.isJSON) {
return createVisitorForJson(
context as Parameters<CustomBlockVisitorFactory>[0]
)
} else if (sourceCode.parserServices.isYAML) {
return createVisitorForYaml(
context as Parameters<CustomBlockVisitorFactory>[0]
)
}
return {}
} else {
debug(`ignore ${filename} in no-deprecated-modulo-syntax`)
return {}
}
}

export = createRule({
meta: {
type: 'problem',
docs: {
description: 'enforce modulo interpolation to be named interpolation',
category: 'Recommended',
url: 'https://eslint-plugin-vue-i18n.intlify.dev/rules/no-deprecated-modulo-syntax.html',
recommended: true
},
fixable: 'code',
schema: []
},
create
})
60 changes: 60 additions & 0 deletions lib/rules/no-deprecated-tc.ts
Original file line number Diff line number Diff line change
@@ -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: 'problem',
docs: {
description:
'disallow using deprecated `tc` or `$tc` (Deprecated in Vue I18n 10.0.0, removed fully in Vue I18n 11.0.0)',
category: 'Recommended',
url: 'https://eslint-plugin-vue-i18n.intlify.dev/rules/no-deprecated-tc.html',
recommended: true
},
fixable: null,
schema: []
},
create
})
43 changes: 43 additions & 0 deletions lib/rules/no-deprecated-v-t.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* @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 checkDirective(context: RuleContext, node: VAST.VDirective) {
context.report({
node,
message: `'v-t' custom directive is used, but it is deprecated. Use 't' or '$t' instead.`
})
}

function create(context: RuleContext): RuleListener {
return defineTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name='t']"(node: VAST.VDirective) {
checkDirective(context, node)
},

"VAttribute[directive=true][key.name.name='t']"(node: VAST.VDirective) {
checkDirective(context, node)
}
})
}

export = createRule({
meta: {
type: 'problem',
docs: {
description:
'disallow using deprecated `v-t` custom directive (Deprecated in Vue I18n 11.0.0, removed fully in Vue I18n 12.0.0)',
category: 'Recommended',
url: 'https://eslint-plugin-vue-i18n.intlify.dev/rules/no-deprecated-v-t.html',
recommended: true
},
fixable: null,
schema: []
},
create
})
82 changes: 49 additions & 33 deletions lib/rules/no-duplicate-keys-in-locale.ts
Original file line number Diff line number Diff line change
@@ -14,6 +14,9 @@ import type {
SourceCode
} from '../types'
import { joinPath } from '../utils/key-path'
import { getCwd } from '../utils/get-cwd'
import { createRule } from '../utils/rule'
import { getFilename, getSourceCode } from '../utils/compat'
const debug = debugBuilder('eslint-plugin-vue-i18n:no-duplicate-keys-in-locale')

interface DictData {
@@ -29,23 +32,25 @@ interface PathStack {
upper?: PathStack
}

function getMessageFilepath(fullPath: string) {
if (fullPath.startsWith(process.cwd())) {
return fullPath.replace(process.cwd() + '/', './')
function getMessageFilepath(fullPath: string, context: RuleContext) {
const cwd = getCwd(context)
if (fullPath.startsWith(cwd)) {
return fullPath.replace(`${cwd}/`, './')
}
return fullPath
}

function create(context: RuleContext): RuleListener {
const filename = context.getFilename()
const filename = getFilename(context)
const sourceCode = getSourceCode(context)
const options = (context.options && context.options[0]) || {}
const ignoreI18nBlock = Boolean(options.ignoreI18nBlock)

function createInitPathStack(
targetLocaleMessage: LocaleMessage,
otherLocaleMessages: LocaleMessage[]
): PathStack {
if (targetLocaleMessage.localeKey === 'file') {
if (targetLocaleMessage.isResolvedLocaleByFileName()) {
const locale = targetLocaleMessage.locales[0]
return createInitLocalePathStack(locale, otherLocaleMessages)
} else {
@@ -110,34 +115,43 @@ function create(context: RuleContext): RuleListener {
}
return
}
const keyOtherValues = pathStack.otherDictionaries
.filter(dict => dict.dict[key] != null)
.map(dict => {
return {
value: dict.dict[key],
source: dict.source
}
})
const keyOtherValues = pathStack.otherDictionaries.map(dict => {
return {
value: dict.dict[key],
source: dict.source
}
})
const keyPath = [...pathStack.keyPath, key]
const keyPathStr = joinPath(...keyPath)
const nextOtherDictionaries: DictData[] = []
const reportFiles = []
for (const value of keyOtherValues) {
if (typeof value.value === 'string') {
context.report({
message: `duplicate key '${keyPathStr}' in '${
pathStack.locale
}'. "${getMessageFilepath(
value.source.fullpath
)}" has the same key`,
loc: reportNode.loc
})
if (value.value == null) {
continue
}
if (typeof value.value !== 'object') {
reportFiles.push(
`"${getMessageFilepath(value.source.fullpath, context)}"`
)
} else {
nextOtherDictionaries.push({
dict: value.value,
source: value.source
})
}
}
if (reportFiles.length) {
reportFiles.sort()
const last = reportFiles.pop()
context.report({
message: `duplicate key '${keyPathStr}' in '${pathStack.locale}'. ${
reportFiles.length === 0
? last
: `${reportFiles.join(', ')}, and ${last}`
} has the same key`,
loc: reportNode.loc
})
}

pushKey(
existsKeyNodes[pathStack.locale] ||
@@ -304,7 +318,7 @@ function create(context: RuleContext): RuleListener {
lm => lm !== targetLocaleMessage
)
return createVisitorForJson(
ctx.getSourceCode(),
getSourceCode(ctx),
targetLocaleMessage,
otherLocaleMessages
)
@@ -323,32 +337,33 @@ function create(context: RuleContext): RuleListener {
lm => lm !== targetLocaleMessage
)
return createVisitorForYaml(
ctx.getSourceCode(),
getSourceCode(ctx),
targetLocaleMessage,
otherLocaleMessages
)
}
)
} else if (context.parserServices.isJSON || context.parserServices.isYAML) {
} else if (
sourceCode.parserServices.isJSON ||
sourceCode.parserServices.isYAML
) {
const localeMessages = getLocaleMessages(context)
const targetLocaleMessage = localeMessages.findExistLocaleMessage(filename)
if (!targetLocaleMessage) {
debug(`ignore ${filename} in no-duplicate-keys-in-locale`)
return {}
}

const sourceCode = context.getSourceCode()
const otherLocaleMessages: LocaleMessage[] = localeMessages.localeMessages.filter(
lm => lm !== targetLocaleMessage
)
const otherLocaleMessages: LocaleMessage[] =
localeMessages.localeMessages.filter(lm => lm !== targetLocaleMessage)

if (context.parserServices.isJSON) {
if (sourceCode.parserServices.isJSON) {
return createVisitorForJson(
sourceCode,
targetLocaleMessage,
otherLocaleMessages
)
} else if (context.parserServices.isYAML) {
} else if (sourceCode.parserServices.isYAML) {
return createVisitorForYaml(
sourceCode,
targetLocaleMessage,
@@ -362,13 +377,14 @@ function create(context: RuleContext): RuleListener {
}
}

export = {
export = createRule({
meta: {
type: 'problem',
docs: {
description:
'disallow duplicate localization keys within the same locale',
category: 'Best Practices',
url: 'https://eslint-plugin-vue-i18n.intlify.dev/rules/no-duplicate-keys-in-locale.html',
recommended: false
},
fixable: null,
@@ -385,4 +401,4 @@ export = {
]
},
create
}
})
48 changes: 18 additions & 30 deletions lib/rules/no-dynamic-keys.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,21 @@
/**
* @author kazuya kawaguchi (a.k.a. kazupon)
*/
import { defineTemplateBodyVisitor } from '../utils/index'
import {
compositingVisitors,
defineTemplateBodyVisitor,
isStaticLiteral
} from '../utils/index'
import type { RuleContext, RuleListener } from '../types'
import type { AST as VAST } from 'vue-eslint-parser'

function isStatic(
node:
| VAST.ESLintExpression
| VAST.ESLintSpreadElement
| VAST.VFilterSequenceExpression
| VAST.VForExpression
| VAST.VOnExpression
| VAST.VSlotScopeExpression
): boolean {
if (node.type === 'Literal') {
return true
}
if (node.type === 'TemplateLiteral' && node.expressions.length === 0) {
return true
}
return false
}
import { createRule } from '../utils/rule'
import { getSourceCode } from '../utils/compat'

function getNodeName(context: RuleContext, node: VAST.Node): string {
if (node.type === 'Identifier') {
return node.name
}
const sourceCode = context.getSourceCode()
const sourceCode = getSourceCode(context)
if (
sourceCode.ast.range[0] <= node.range[0] &&
node.range[1] <= sourceCode.ast.range[1]
@@ -37,7 +25,7 @@ function getNodeName(context: RuleContext, node: VAST.Node): string {
.map(t => t.value)
.join('')
}
const tokenStore = context.parserServices.getTemplateBodyTokenStore()
const tokenStore = sourceCode.parserServices.getTemplateBodyTokenStore()
return tokenStore
.getTokens(node)
.map(t => t.value)
@@ -49,7 +37,7 @@ function checkDirective(context: RuleContext, node: VAST.VDirective) {
node.value &&
node.value.type === 'VExpressionContainer' &&
node.value.expression &&
!isStatic(node.value.expression)
!isStaticLiteral(node.value.expression)
) {
const name = getNodeName(context, node.value.expression)
context.report({
@@ -69,7 +57,7 @@ function checkComponent(context: RuleContext, node: VAST.VDirectiveKey) {
node.parent.value &&
node.parent.value.type === 'VExpressionContainer' &&
node.parent.value.expression &&
!isStatic(node.parent.value.expression)
!isStaticLiteral(node.parent.value.expression)
) {
const name = getNodeName(context, node.parent.value.expression)
context.report({
@@ -99,7 +87,7 @@ function checkCallExpression(
}

const [keyNode] = node.arguments
if (!isStatic(keyNode)) {
if (!isStaticLiteral(keyNode)) {
const name = getNodeName(context, keyNode)
context.report({
node,
@@ -109,9 +97,8 @@ function checkCallExpression(
}

function create(context: RuleContext): RuleListener {
return defineTemplateBodyVisitor(
context,
{
return compositingVisitors(
defineTemplateBodyVisitor(context, {
"VAttribute[directive=true][key.name='t']"(node: VAST.VDirective) {
checkDirective(context, node)
},
@@ -129,7 +116,7 @@ function create(context: RuleContext): RuleListener {
CallExpression(node: VAST.ESLintCallExpression) {
checkCallExpression(context, node)
}
},
}),
{
CallExpression(node: VAST.ESLintCallExpression) {
checkCallExpression(context, node)
@@ -138,16 +125,17 @@ function create(context: RuleContext): RuleListener {
)
}

export = {
export = createRule({
meta: {
type: 'suggestion',
docs: {
description: 'disallow localization dynamic keys at localization methods',
category: 'Best Practices',
url: 'https://eslint-plugin-vue-i18n.intlify.dev/rules/no-dynamic-keys.html',
recommended: false
},
fixable: null,
schema: []
},
create
}
})
Loading