Skip to content

i18n: added linter for common issues #23394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

vikaspotluri123
Copy link
Member

ref #23385
merge strategy: squash

  • We currently detect issues with translations using Mocha, but this is pretty limiting

  • Created a custom iterator that generates and prints eslint-style messages. It:

    • Reports mismatched brackets (no change)
    • Reports undefined variables (no change)
    • Reports unused ignore rules
    • Can remove unused ignores
    • Can unsafely ignore all errors
    • Provides all errors at once
    • Includes positions in file (mostly accurately)
      Please check your PR against these items:
  • I've read and followed the Contributor Guide

  • I've explained my change

  • I've written an automated test to prove my change works

We appreciate your contribution! 🙏

Copy link
Contributor

coderabbitai bot commented May 19, 2025

Walkthrough

This change refactors the i18n (internationalization) linting and ignore management workflow. The test:update-ignores script and its supporting file remove-unneeded-ignores.js are removed, along with related tests in i18n.test.js. A new linting script, i18n.lint.js, is introduced and integrated into the lint workflow via a new lint:translations script. The structure and comments in i18n-ignores.json are updated for clarity, with renamed keys and standardized comments. The new linting approach consolidates translation validation and ignore management.

Possibly related PRs

Suggested labels

affects:i18n


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bcfe4bb19854e6a69a21c22a0cddb3eaa823c0b2 and 5a7d496.

📒 Files selected for processing (5)
  • ghost/i18n/package.json (1 hunks)
  • ghost/i18n/test/i18n-ignores.json (1 hunks)
  • ghost/i18n/test/i18n.lint.js (1 hunks)
  • ghost/i18n/test/i18n.test.js (6 hunks)
  • ghost/i18n/test/remove-unneeded-ignores.js (0 hunks)
💤 Files with no reviewable changes (1)
  • ghost/i18n/test/remove-unneeded-ignores.js
🚧 Files skipped from review as they are similar to previous changes (3)
  • ghost/i18n/test/i18n-ignores.json
  • ghost/i18n/package.json
  • ghost/i18n/test/i18n.lint.js
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Setup
🔇 Additional comments (2)
ghost/i18n/test/i18n.test.js (2)

17-17: LGTM! Formatting improvements enhance readability.

The added blank lines improve code organization by clearly separating different test suites.

Also applies to: 56-56, 92-92, 116-116, 127-127, 142-142


1-168:

✅ Verification successful

Verify that removed test functionality is covered by the new linter.

According to the PR summary, the following test functionality has been removed from this file and moved to the new i18n.lint.js linter:

  • Mismatched brackets validation in translation keys/values
  • i18n-ignores.json structure validation
  • Translation variable consistency checks

Please ensure the new linter provides equivalent or better coverage for these validation rules.

Run the following script to verify the new linter includes these validation rules:


🏁 Script executed:

#!/bin/bash
# Description: Verify that the new i18n linter includes the validation rules from removed tests

# Check if the new linter file exists and contains the expected validation logic
echo "Checking for i18n.lint.js file..."
fd -t f "i18n.lint.js"

# Search for bracket validation logic in the linter
echo -e "\nSearching for bracket validation logic..."
rg -A 5 "bracket|mismatch" --glob "**/i18n.lint.js"

# Search for variable validation logic
echo -e "\nSearching for variable validation logic..."
rg -A 5 "variable|undefined|unused" --glob "**/i18n.lint.js"

# Search for ignore validation logic
echo -e "\nSearching for ignore validation logic..."
rg -A 5 "ignore|i18n-ignores" --glob "**/i18n.lint.js"

Length of output: 19517


Confirm i18n.lint.js Covers All Removed Test Validations

The new linter at ghost/i18n/test/i18n.lint.js implements all of the validation logic formerly covered by the removed tests:

  • Bracket mismatch checks via parseTranslationString (errors on unexpected { or } and unclosed variables)
  • Translation-variable consistency with rules:
    • ghost/i18n/no-unused-variables
    • ghost/i18n/no-undefined-variables
  • Invalid-translation checks (ghost/i18n/no-invalid-translations)
  • i18n-ignores.json structure enforcement and unused-ignore reporting (parseIgnores & ghost/i18n/no-unused-ignores)

No further action required.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Member

@cathysarisky cathysarisky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your work on this! If I'd realized you were going to follow along behind me and do all of it better, I'd have worked on something different yesterday. laughing

I left a few comments. I'm going to also manually test some common scenarios to confirm that there aren't any surprises. :) Will update when I do.

{
"file": "da/portal.json",
"key": "Try free for {amount} days, then {originalPrice}.",
"comment": "This translation CANNOT work. Please repair."
"comment": "FIXME: This error was automatically ignored. Please update the translation or this comment."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest retaining something similar to the wording I had for the added/undefined variables. There's no way that the translation will work for these addedVariable/no-undefined-variables problems. They're undefined by definition, so updating the comment doesn't make sense here. They need to be fixed in the translation.

No objections for the unused/missing variables.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will follow up once we close on #23394 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this has been addressed

ignores[rule].push({
file,
key,
comment: 'FIXME: This error was automatically ignored. Please update the translation or this comment.'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a different comment for the rule type would be helpful for translators, who are often pretty new to things. Added variables are never fixable with a comment and should be fixed in the ns.json file. Missing variables could be intentional, but there's no functional reason to have an added variable. They are typos, copy-paste errors, or misunderstandings of how the t function works.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO only you should be running --unsafe-ignore-all 😛
Translators should manually update the file if they add an ignore (since it's supposed to be very rare), and --fix will remove unused ignores.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, agreed, and fortunately our average translator doesn't even have a dev install setup and isn't going to run anything.

I'm pretty much expecting not to run it either, BUT, for that initial commit, I'd like the FIXME comments to reflect that updating the comment is /not/ a fix for the made-up variable names, while it might be reasonable for the variables that don't appear in the translations.

In other words, please don't tell translators that updating the comment is an option for cases where they have made up a variable or have a typo in the variable name. It isn't.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I should probably fix the current comments like they're telling me to 😝

Are you okay with this copy for future runs? I was planning on having a single default string, but if you want one per rule that's pretty easy to do.

It can also say "[...], or explain why it's ignored (if applicable)"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I don't plan to merge anything with an added variable in the value (ugh), I don't care too much what the --unsafe-ignore-all behavior is (unless you're overwriting existing comments, which I don't think you are). But I do want the initial commit to not encourage translators to adjust comments for added/incorrect variables in values. :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated!


// Report key parsing errors only for English translations.
// I18next is responsible for keeping all other locales in sync.
if (reportInvalidTranslations && context.file?.locale === 'en') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging this to look at further - are we sure that there won't be errors of this type in the other files once the translators have touched them? I've seen a lot of weirdness...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running yarn translate should cover it, we can configure it to be a gating check in CI (run it with --fail-on-update)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
ghost/i18n/test/i18n-ignores.json (1)

1-173: Rule naming and comment updates properly address reviewer feedback.

The ESLint-style rule naming and standardized comments effectively implement the feedback from previous reviews. The distinction between undefined variables (which must be fixed) and unused variables (which might be intentional) is now clear in the comment text.

ghost/i18n/test/i18n.lint.js (1)

392-393: Default ignore comment could be more specific per rule type.

Based on past review feedback, the comment text should distinguish between rule types. Consider using different default comments for no-undefined-variables vs no-unused-variables rules.

 function ignoreTranslationError(ignores, rule, file, key) {
     ignores[rule] ??= [];
+    const commentMap = {
+        'ghost/i18n/no-undefined-variables': 'FIXME: This translation doesn\'t work and needs to be updated',
+        'ghost/i18n/no-unused-variables': 'FIXME: This error was automatically ignored. Please update the translation, or explain why a variable is ignored'
+    };
     ignores[rule].push({
         file,
         key,
-        comment: 'FIXME: This error was automatically ignored. Please update the translation or this comment.'
+        comment: commentMap[rule] || 'FIXME: This error was automatically ignored. Please update the translation or this comment.'
     });
 }
🧹 Nitpick comments (1)
ghost/i18n/test/i18n.lint.js (1)

48-87: Consider robustness of probabilistic position mapping.

The _analyzedSource() method assumes one key/value pair per line, which may not hold for all JSON formatting styles (e.g., minified JSON or different prettier configurations). This could lead to incorrect line/column reporting.

Consider using a proper JSON parser with position tracking or add validation to ensure the JSON follows the expected format.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a9cd809 and bcfe4bb19854e6a69a21c22a0cddb3eaa823c0b2.

📒 Files selected for processing (5)
  • ghost/i18n/package.json (1 hunks)
  • ghost/i18n/test/i18n-ignores.json (1 hunks)
  • ghost/i18n/test/i18n.lint.js (1 hunks)
  • ghost/i18n/test/i18n.test.js (6 hunks)
  • ghost/i18n/test/remove-unneeded-ignores.js (0 hunks)
💤 Files with no reviewable changes (1)
  • ghost/i18n/test/remove-unneeded-ignores.js
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Database tests (Node 20.11.1, mysql8)
  • GitHub Check: Ghost-CLI tests
  • GitHub Check: Database tests (Node 22.13.1, mysql8)
🔇 Additional comments (4)
ghost/i18n/test/i18n.test.js (1)

1-168: LGTM! Test removal aligns with the new linting approach.

The removal of the ignore validation and translation variable tests is appropriate since this functionality is now handled by the new i18n.lint.js script. The remaining tests focus on core i18n functionality, which is the correct separation of concerns.

ghost/i18n/package.json (1)

12-16: Script updates correctly integrate the new linting workflow.

The removal of test:update-ignores and addition of lint:translations properly reflects the transition from test-based validation to lint-based validation. The new lint script is correctly integrated into the main lint command.

ghost/i18n/test/i18n.lint.js (2)

371-377: Good future-proofing with the fs.glob comment.

The TODO comment about using fs.glob when Node 22 is available shows good awareness of upcoming Node.js features. The current glob implementation works well as a temporary solution.


259-259:

✅ Verification successful

Verify Node.js version compatibility for structuredClone.

The structuredClone function requires Node.js 17 or higher. Ensure this meets your project's minimum Node.js version requirements.


🏁 Script executed:

#!/bin/bash
# Check the minimum Node.js version requirement in the project
fd -t f "package.json" -x grep -l "engines" {} \; | head -5 | xargs grep -A2 -B2 "engines"

# Check if there's a .nvmrc or similar file specifying Node version
fd -t f "\.nvmrc|\.node-version" | head -5 | xargs cat 2>/dev/null

Length of output: 1487


structuredClone compatibility confirmed

The project’s package.json (in both ghost/core and ghost/admin) specifies Node ^18.12.1 || ^20.11.1 || ^22.13.1, which exceeds the Node 17+ requirement for using structuredClone. No changes are necessary.

ref TryGhost#23385

- We currently detect issues with translations using Mocha, but this is pretty limiting
- Created a custom iterator that generates and prints eslint-style messages. It:
  - Reports mismatched brackets (no change)
  - Reports undefined variables (no change)
  - Reports unused ignore rules
  - Can remove unused ignores
  - Can unsafely ignore all errors
  - Provides all errors at once
  - Includes positions in file (mostly accurately)
- superseded by the new linter
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants