Skip to content

Changed theme i18n to use i18next #24452

Closed
ErisDS wants to merge 4 commits intomainfrom
cathy-theme-i18n
Closed

Changed theme i18n to use i18next #24452
ErisDS wants to merge 4 commits intomainfrom
cathy-theme-i18n

Conversation

@ErisDS
Copy link
Copy Markdown
Member

@ErisDS ErisDS commented Jul 20, 2025

This is the OG change PR'd with browser-tests on

  • i18n support was added to themes a really long time ago, and at the time there weren't great libraries
  • Since then, i18n has been added to more parts of Ghost, but leveraging the awesome i18next library
  • This PR brings theme i18n into line with the rest of Ghost by swapping out the custom code with i18next
  • The change exists behind a labs flag, with duplicated logic to make it easier to clean up later

cathysarisky and others added 2 commits July 20, 2025 16:31
- i18n support was added to themes a _really_ long time ago, and at the time there weren't great libraries
- Since then, i18n has been added to more parts of Ghost, but leveraging the awesome i18next library
- This PR brings theme i18n into line with the rest of Ghost by swapping out the custom code with i18next 
- The change exists behind a labs flag, with duplicated logic to make it easier to clean up later
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jul 20, 2025

Warning

Rate limit exceeded

@ErisDS has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 26 minutes and 31 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 5a68773 and d328bfd.

📒 Files selected for processing (3)
  • ghost/core/test/unit/frontend/helpers/t-new.test.js (1 hunks)
  • ghost/core/test/unit/frontend/services/theme-engine/i18next.test.js (1 hunks)
  • ghost/i18n/test/i18n.test.js (4 hunks)

Walkthrough

This change introduces a new feature flag, themeTranslation, to enable theme translation using i18next instead of the legacy translation package. The BetaFeatures React component is updated to include a toggle for this feature. The translation helper function and theme initialization logic are modified to conditionally use the new i18next-based translation service when the flag is enabled. A new ThemeI18n class and its service module are implemented for theme-specific i18n support. Comprehensive unit tests are added for both the new translation helper behavior and the i18n module, including theme namespace handling and fallback logic.

Possibly related PRs

Suggested labels

feature, affects:i18n, affects:theme, pinned

✨ 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
Copy Markdown
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: 2

🧹 Nitpick comments (4)
ghost/i18n/lib/i18n.js (2)

2-2: Unnecessary fs-extra import

The code uses fs.readFileSync from the built-in fs module, not fs-extra. Consider removing this import if it's not used elsewhere in the file.

-const fs = require('fs-extra');
+const fs = require('fs');

118-128: Consider using asynchronous file operations

The synchronous fs.readFileSync operations could block the event loop, especially if translation files are large or the filesystem is slow. Since this appears to be called during initialization, consider if the calling code could be refactored to support async operations.

ghost/core/test/unit/frontend/helpers/t-new.test.js (1)

9-9: Consider using a more robust approach for test setup

The test directly manipulates module properties (basePath and _i18n). This approach is brittle and may break if the internal implementation changes. Consider adding test-specific methods or using dependency injection for better testability.

Also applies to: 13-13, 18-18, 23-23

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

296-304: Important security consideration verified

Good test verifying that HTML is not escaped in theme translations. This is intentional for theme flexibility but requires theme developers to be aware of potential XSS risks when using user-provided content in translations.

Consider documenting this behavior prominently in theme development documentation to ensure developers are aware of the security implications.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c157d82 and 5a68773.

⛔ Files ignored due to path filters (1)
  • ghost/core/test/e2e-api/admin/__snapshots__/config.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (11)
  • apps/admin-x-settings/src/components/settings/advanced/labs/BetaFeatures.tsx (1 hunks)
  • ghost/core/core/frontend/helpers/t.js (2 hunks)
  • ghost/core/core/frontend/services/handlebars.js (1 hunks)
  • ghost/core/core/frontend/services/theme-engine/active.js (2 hunks)
  • ghost/core/core/frontend/services/theme-engine/i18next/ThemeI18n.js (1 hunks)
  • ghost/core/core/frontend/services/theme-engine/i18next/index.js (1 hunks)
  • ghost/core/core/shared/labs.js (1 hunks)
  • ghost/core/test/unit/frontend/helpers/t-new.test.js (1 hunks)
  • ghost/core/test/unit/frontend/services/theme-engine/i18next.test.js (1 hunks)
  • ghost/i18n/lib/i18n.js (3 hunks)
  • ghost/i18n/test/i18n.test.js (4 hunks)
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-06T00:47:48.488Z
Learning: The theme i18n initialization in Ghost only happens during server boot, theme changes, and language changes - not on every page load or `t` helper call. The initialization check in the `t` helper is a safeguard that doesn't execute during normal page rendering once the theme is loaded.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-20T21:08:21.026Z
Learning: In the Ghost project, translation files (ghost/i18n/locales/*/*.json) commonly have blank values for translations, and this is normal behavior that should not be flagged in reviews. These empty translations will be filled in separate PRs.
Learnt from: cathysarisky
PR: TryGhost/Ghost#21982
File: ghost/i18n/locales/hu v2/portal.json:70-70
Timestamp: 2025-01-29T15:23:58.658Z
Learning: Typos and spelling errors within localization files (ghost/i18n/locales/*.json) must be treated as important problems, not nitpicks, as they directly affect the user experience for speakers of that language. These issues should be flagged with high priority during code review.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-01-29T15:27:29.391Z
Learning: When reviewing translation files in ghost/i18n/locales, translators should only modify the translated values and must not change the keys or placeholder styles (e.g., %%{status}%%, {site}, etc.) as they serve specific purposes like Mailgun string replacement.
Learnt from: cathysarisky
PR: TryGhost/Ghost#22175
File: ghost/i18n/locales/pt-BR/portal.json:18-18
Timestamp: 2025-02-12T20:09:12.731Z
Learning: In the Ghost project, new translation keys are first added with empty string values, and the actual translations are added later in separate PRs to maintain better organization of translation work.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23070
File: ghost/i18n/locales/hu/comments.json:45-45
Timestamp: 2025-04-30T16:27:03.542Z
Learning: In the Ghost project, new translation string keys are intentionally added with empty values initially and will be translated in a separate PR. Do not report missing translations for entirely new string additions.
Learnt from: kevinansfield
PR: TryGhost/Ghost#23770
File: apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModalLabs.tsx:435-450
Timestamp: 2025-06-10T11:07:10.800Z
Learning: In Ghost's newsletter customization features, when promoting a feature from alpha to beta status, the feature flag guards are updated to make the feature available under both the `emailCustomization` (beta) and `emailCustomizationAlpha` (alpha) flags. This is done by either removing conditional guards entirely when the component is already behind both flags, or by updating conditionals to check for either flag.
Learnt from: SebastianSchroeder
PR: TryGhost/Ghost#23602
File: ghost/i18n/locales/vi/ghost.json:3-4
Timestamp: 2025-05-30T11:41:47.336Z
Learning: SebastianSchroeder prefers to handle missing translations in separate PRs rather than including them in the same PR that adds the i18n infrastructure, to keep PRs focused on specific functionality.
Learnt from: cathysarisky
PR: TryGhost/Ghost#22175
File: ghost/i18n/locales/lt/portal.json:16-16
Timestamp: 2025-02-12T20:23:47.777Z
Learning: When reviewing localization changes, verify the actual history of translations before flagging potential deletions. Empty translation strings may be intentional placeholders rather than deleted content.
ghost/core/core/shared/labs.js (4)
Learnt from: kevinansfield
PR: TryGhost/Ghost#23770
File: apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModalLabs.tsx:435-450
Timestamp: 2025-06-10T11:07:10.800Z
Learning: In Ghost's newsletter customization features, when promoting a feature from alpha to beta status, the feature flag guards are updated to make the feature available under both the `emailCustomization` (beta) and `emailCustomizationAlpha` (alpha) flags. This is done by either removing conditional guards entirely when the component is already behind both flags, or by updating conditionals to check for either flag.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23070
File: ghost/i18n/locales/hu/comments.json:45-45
Timestamp: 2025-04-30T16:27:03.542Z
Learning: In the Ghost project, new translation string keys are intentionally added with empty values initially and will be translated in a separate PR. Do not report missing translations for entirely new string additions.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-20T21:08:21.026Z
Learning: In the Ghost project, translation files (ghost/i18n/locales/*/*.json) commonly have blank values for translations, and this is normal behavior that should not be flagged in reviews. These empty translations will be filled in separate PRs.
Learnt from: cathysarisky
PR: TryGhost/Ghost#22175
File: ghost/i18n/locales/pt-BR/portal.json:18-18
Timestamp: 2025-02-12T20:09:12.731Z
Learning: In the Ghost project, new translation keys are first added with empty string values, and the actual translations are added later in separate PRs to maintain better organization of translation work.
ghost/core/core/frontend/services/handlebars.js (2)
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-06T00:47:48.488Z
Learning: The theme i18n initialization in Ghost only happens during server boot, theme changes, and language changes - not on every page load or `t` helper call. The initialization check in the `t` helper is a safeguard that doesn't execute during normal page rendering once the theme is loaded.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-01-29T15:27:29.391Z
Learning: When reviewing translation files in ghost/i18n/locales, translators should only modify the translated values and must not change the keys or placeholder styles (e.g., %%{status}%%, {site}, etc.) as they serve specific purposes like Mailgun string replacement.
ghost/core/core/frontend/services/theme-engine/i18next/index.js (1)
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-06T00:47:48.488Z
Learning: The theme i18n initialization in Ghost only happens during server boot, theme changes, and language changes - not on every page load or `t` helper call. The initialization check in the `t` helper is a safeguard that doesn't execute during normal page rendering once the theme is loaded.
apps/admin-x-settings/src/components/settings/advanced/labs/BetaFeatures.tsx (2)
Learnt from: kevinansfield
PR: TryGhost/Ghost#23770
File: apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModalLabs.tsx:435-450
Timestamp: 2025-06-10T11:07:10.800Z
Learning: In Ghost's newsletter customization features, when promoting a feature from alpha to beta status, the feature flag guards are updated to make the feature available under both the `emailCustomization` (beta) and `emailCustomizationAlpha` (alpha) flags. This is done by either removing conditional guards entirely when the component is already behind both flags, or by updating conditionals to check for either flag.
Learnt from: sagzy
PR: TryGhost/Ghost#24346
File: apps/admin-x-settings/src/components/settings/growth/Network.tsx:8-12
Timestamp: 2025-07-14T12:20:35.268Z
Learning: The Network component toggle in `apps/admin-x-settings/src/components/settings/growth/Network.tsx` is intentionally implemented as static UI with a no-op onChange handler, as part of a UI-first development approach before connecting actual ActivityPub functionality.
ghost/core/core/frontend/services/theme-engine/active.js (3)
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-06T00:47:48.488Z
Learning: The theme i18n initialization in Ghost only happens during server boot, theme changes, and language changes - not on every page load or `t` helper call. The initialization check in the `t` helper is a safeguard that doesn't execute during normal page rendering once the theme is loaded.
Learnt from: kevinansfield
PR: TryGhost/Ghost#23770
File: apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModalLabs.tsx:435-450
Timestamp: 2025-06-10T11:07:10.800Z
Learning: In Ghost's newsletter customization features, when promoting a feature from alpha to beta status, the feature flag guards are updated to make the feature available under both the `emailCustomization` (beta) and `emailCustomizationAlpha` (alpha) flags. This is done by either removing conditional guards entirely when the component is already behind both flags, or by updating conditionals to check for either flag.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23070
File: ghost/i18n/locales/hu/comments.json:45-45
Timestamp: 2025-04-30T16:27:03.542Z
Learning: In the Ghost project, new translation string keys are intentionally added with empty values initially and will be translated in a separate PR. Do not report missing translations for entirely new string additions.
ghost/core/core/frontend/helpers/t.js (7)
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-06T00:47:48.488Z
Learning: The theme i18n initialization in Ghost only happens during server boot, theme changes, and language changes - not on every page load or `t` helper call. The initialization check in the `t` helper is a safeguard that doesn't execute during normal page rendering once the theme is loaded.
Learnt from: kevinansfield
PR: TryGhost/Ghost#23770
File: apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModalLabs.tsx:435-450
Timestamp: 2025-06-10T11:07:10.800Z
Learning: In Ghost's newsletter customization features, when promoting a feature from alpha to beta status, the feature flag guards are updated to make the feature available under both the `emailCustomization` (beta) and `emailCustomizationAlpha` (alpha) flags. This is done by either removing conditional guards entirely when the component is already behind both flags, or by updating conditionals to check for either flag.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-01-29T15:27:29.391Z
Learning: When reviewing translation files in ghost/i18n/locales, translators should only modify the translated values and must not change the keys or placeholder styles (e.g., %%{status}%%, {site}, etc.) as they serve specific purposes like Mailgun string replacement.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-20T21:08:21.026Z
Learning: In the Ghost project, translation files (ghost/i18n/locales/*/*.json) commonly have blank values for translations, and this is normal behavior that should not be flagged in reviews. These empty translations will be filled in separate PRs.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23070
File: ghost/i18n/locales/hu/comments.json:45-45
Timestamp: 2025-04-30T16:27:03.542Z
Learning: In the Ghost project, new translation string keys are intentionally added with empty values initially and will be translated in a separate PR. Do not report missing translations for entirely new string additions.
Learnt from: cathysarisky
PR: TryGhost/Ghost#22175
File: ghost/i18n/locales/pt-BR/portal.json:18-18
Timestamp: 2025-02-12T20:09:12.731Z
Learning: In the Ghost project, new translation keys are first added with empty string values, and the actual translations are added later in separate PRs to maintain better organization of translation work.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23161
File: ghost/i18n/lib/i18n.js:107-144
Timestamp: 2025-05-14T14:39:09.136Z
Learning: In Ghost's i18n system, translation keys are the actual English phrases, so when a translation is missing, displaying the key itself serves as the English fallback, eliminating the need for explicit fallback resource loading.
ghost/core/test/unit/frontend/services/theme-engine/i18next.test.js (7)
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-06T00:47:48.488Z
Learning: The theme i18n initialization in Ghost only happens during server boot, theme changes, and language changes - not on every page load or `t` helper call. The initialization check in the `t` helper is a safeguard that doesn't execute during normal page rendering once the theme is loaded.
Learnt from: mike182uk
PR: TryGhost/Ghost#22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23070
File: ghost/i18n/locales/hu/comments.json:45-45
Timestamp: 2025-04-30T16:27:03.542Z
Learning: In the Ghost project, new translation string keys are intentionally added with empty values initially and will be translated in a separate PR. Do not report missing translations for entirely new string additions.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-20T21:08:21.026Z
Learning: In the Ghost project, translation files (ghost/i18n/locales/*/*.json) commonly have blank values for translations, and this is normal behavior that should not be flagged in reviews. These empty translations will be filled in separate PRs.
Learnt from: mike182uk
PR: TryGhost/Ghost#22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in Ghost's ActivityPub module are thoroughly tested in `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`, which covers `generatePendingActivityId`, `isPendingActivity`, and `generatePendingActivity` functions.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-01-29T15:27:29.391Z
Learning: When reviewing translation files in ghost/i18n/locales, translators should only modify the translated values and must not change the keys or placeholder styles (e.g., %%{status}%%, {site}, etc.) as they serve specific purposes like Mailgun string replacement.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23161
File: ghost/i18n/lib/i18n.js:107-144
Timestamp: 2025-05-14T14:39:09.136Z
Learning: In Ghost's i18n system, translation keys are the actual English phrases, so when a translation is missing, displaying the key itself serves as the English fallback, eliminating the need for explicit fallback resource loading.
ghost/core/test/unit/frontend/helpers/t-new.test.js (8)
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-06T00:47:48.488Z
Learning: The theme i18n initialization in Ghost only happens during server boot, theme changes, and language changes - not on every page load or `t` helper call. The initialization check in the `t` helper is a safeguard that doesn't execute during normal page rendering once the theme is loaded.
Learnt from: mike182uk
PR: TryGhost/Ghost#22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.
Learnt from: mike182uk
PR: TryGhost/Ghost#22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in Ghost's ActivityPub module are thoroughly tested in `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`, which covers `generatePendingActivityId`, `isPendingActivity`, and `generatePendingActivity` functions.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23070
File: ghost/i18n/locales/hu/comments.json:45-45
Timestamp: 2025-04-30T16:27:03.542Z
Learning: In the Ghost project, new translation string keys are intentionally added with empty values initially and will be translated in a separate PR. Do not report missing translations for entirely new string additions.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-20T21:08:21.026Z
Learning: In the Ghost project, translation files (ghost/i18n/locales/*/*.json) commonly have blank values for translations, and this is normal behavior that should not be flagged in reviews. These empty translations will be filled in separate PRs.
Learnt from: cathysarisky
PR: TryGhost/Ghost#22175
File: ghost/i18n/locales/pt-BR/portal.json:18-18
Timestamp: 2025-02-12T20:09:12.731Z
Learning: In the Ghost project, new translation keys are first added with empty string values, and the actual translations are added later in separate PRs to maintain better organization of translation work.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-01-29T15:27:29.391Z
Learning: When reviewing translation files in ghost/i18n/locales, translators should only modify the translated values and must not change the keys or placeholder styles (e.g., %%{status}%%, {site}, etc.) as they serve specific purposes like Mailgun string replacement.
Learnt from: ErisDS
PR: TryGhost/Ghost#23588
File: ghost/core/test/e2e-api/admin/backup.test.js:136-148
Timestamp: 2025-05-29T10:37:26.369Z
Learning: In the Ghost test framework, when testing CSV exports via the admin API, the response `body` field is empty while the actual CSV data is provided through the `text` field. Tests should use `expectEmptyBody()` and then validate the CSV content via `.expect(({text}) => ...)` - this is not contradictory behavior.
ghost/i18n/lib/i18n.js (4)
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-06T00:47:48.488Z
Learning: The theme i18n initialization in Ghost only happens during server boot, theme changes, and language changes - not on every page load or `t` helper call. The initialization check in the `t` helper is a safeguard that doesn't execute during normal page rendering once the theme is loaded.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-01-29T15:27:29.391Z
Learning: When reviewing translation files in ghost/i18n/locales, translators should only modify the translated values and must not change the keys or placeholder styles (e.g., %%{status}%%, {site}, etc.) as they serve specific purposes like Mailgun string replacement.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23161
File: ghost/i18n/lib/i18n.js:107-144
Timestamp: 2025-05-14T14:39:09.136Z
Learning: In Ghost's i18n system, translation keys are the actual English phrases, so when a translation is missing, displaying the key itself serves as the English fallback, eliminating the need for explicit fallback resource loading.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-20T21:08:21.026Z
Learning: In the Ghost project, translation files (ghost/i18n/locales/*/*.json) commonly have blank values for translations, and this is normal behavior that should not be flagged in reviews. These empty translations will be filled in separate PRs.
ghost/i18n/test/i18n.test.js (13)
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-06T00:47:48.488Z
Learning: The theme i18n initialization in Ghost only happens during server boot, theme changes, and language changes - not on every page load or `t` helper call. The initialization check in the `t` helper is a safeguard that doesn't execute during normal page rendering once the theme is loaded.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-20T21:08:21.026Z
Learning: In the Ghost project, translation files (ghost/i18n/locales/*/*.json) commonly have blank values for translations, and this is normal behavior that should not be flagged in reviews. These empty translations will be filled in separate PRs.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-01-29T15:27:29.391Z
Learning: When reviewing translation files in ghost/i18n/locales, translators should only modify the translated values and must not change the keys or placeholder styles (e.g., %%{status}%%, {site}, etc.) as they serve specific purposes like Mailgun string replacement.
Learnt from: mike182uk
PR: TryGhost/Ghost#22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23070
File: ghost/i18n/locales/hu/comments.json:45-45
Timestamp: 2025-04-30T16:27:03.542Z
Learning: In the Ghost project, new translation string keys are intentionally added with empty values initially and will be translated in a separate PR. Do not report missing translations for entirely new string additions.
Learnt from: cathysarisky
PR: TryGhost/Ghost#21982
File: ghost/i18n/locales/hu v2/portal.json:70-70
Timestamp: 2025-01-29T15:23:58.658Z
Learning: Typos and spelling errors within localization files (ghost/i18n/locales/*.json) must be treated as important problems, not nitpicks, as they directly affect the user experience for speakers of that language. These issues should be flagged with high priority during code review.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23161
File: ghost/i18n/lib/i18n.js:107-144
Timestamp: 2025-05-14T14:39:09.136Z
Learning: In Ghost's i18n system, translation keys are the actual English phrases, so when a translation is missing, displaying the key itself serves as the English fallback, eliminating the need for explicit fallback resource loading.
Learnt from: cathysarisky
PR: TryGhost/Ghost#21651
File: ghost/i18n/locales/ar/comments.json:1-1
Timestamp: 2025-01-29T15:35:26.447Z
Learning: All JSON files containing translation strings in ghost/i18n/locales must include both opening and closing braces to maintain valid JSON structure.
Learnt from: mike182uk
PR: TryGhost/Ghost#22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in Ghost's ActivityPub module are thoroughly tested in `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`, which covers `generatePendingActivityId`, `isPendingActivity`, and `generatePendingActivity` functions.
Learnt from: cathysarisky
PR: TryGhost/Ghost#22075
File: ghost/i18n/locales/pt-BR/portal.json:0-0
Timestamp: 2025-01-30T15:11:25.199Z
Learning: When reviewing translation files in i18n/locales/*, focus on identifying spelling errors in the translation values (right side) of the JSON key-value pairs, not the keys (left side).
Learnt from: cathysarisky
PR: TryGhost/Ghost#22007
File: ghost/i18n/locales/sl/newsletter.json:18-18
Timestamp: 2025-01-28T13:58:08.393Z
Learning: When reviewing translations of newsletter.json, check that the translations of status words (free/complimentary/paid/trialing) will form grammatically correct sentences when substituted into the message template. The status words should agree grammatically with the word "subscriber" in the target language.
Learnt from: cathysarisky
PR: TryGhost/Ghost#22175
File: ghost/i18n/locales/pt-BR/portal.json:18-18
Timestamp: 2025-02-12T20:09:12.731Z
Learning: In the Ghost project, new translation keys are first added with empty string values, and the actual translations are added later in separate PRs to maintain better organization of translation work.
Learnt from: cathysarisky
PR: TryGhost/Ghost#21982
File: ghost/i18n/locales/hu/portal.json:207-207
Timestamp: 2025-02-15T16:00:03.347Z
Learning: In Hungarian translations for Ghost, ensure that all variables like {{title}} are preserved in the translated strings and not omitted.
ghost/core/core/frontend/services/theme-engine/i18next/ThemeI18n.js (6)
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-05-06T00:47:48.488Z
Learning: The theme i18n initialization in Ghost only happens during server boot, theme changes, and language changes - not on every page load or `t` helper call. The initialization check in the `t` helper is a safeguard that doesn't execute during normal page rendering once the theme is loaded.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23161
File: ghost/i18n/lib/i18n.js:107-144
Timestamp: 2025-05-14T14:39:09.136Z
Learning: In Ghost's i18n system, translation keys are the actual English phrases, so when a translation is missing, displaying the key itself serves as the English fallback, eliminating the need for explicit fallback resource loading.
Learnt from: cathysarisky
PR: TryGhost/Ghost#0
File: :0-0
Timestamp: 2025-01-29T15:27:29.391Z
Learning: When reviewing translation files in ghost/i18n/locales, translators should only modify the translated values and must not change the keys or placeholder styles (e.g., %%{status}%%, {site}, etc.) as they serve specific purposes like Mailgun string replacement.
Learnt from: cathysarisky
PR: TryGhost/Ghost#23070
File: ghost/i18n/locales/hu/comments.json:45-45
Timestamp: 2025-04-30T16:27:03.542Z
Learning: In the Ghost project, new translation string keys are intentionally added with empty values initially and will be translated in a separate PR. Do not report missing translations for entirely new string additions.
Learnt from: aileen
PR: TryGhost/Ghost#24073
File: apps/admin-x-settings/src/components/settings/site/DesignAndThemeModal.tsx:68-85
Timestamp: 2025-06-26T14:53:58.238Z
Learning: In Ghost's theme limit checking functionality (useCheckThemeLimitError hook), the `checkThemeLimitError` function is designed to always resolve and return either an error message string if the theme is disallowed or null if allowed. It does not throw/reject promises - any internal errors are caught within the hook and converted to return values, so `.catch()` blocks are not needed when calling this function.
Learnt from: cathysarisky
PR: TryGhost/Ghost#21651
File: ghost/i18n/locales/ar/comments.json:1-1
Timestamp: 2025-01-29T15:35:26.447Z
Learning: All JSON files containing translation strings in ghost/i18n/locales must include both opening and closing braces to maintain valid JSON structure.
🧬 Code Graph Analysis (3)
ghost/core/core/frontend/services/handlebars.js (1)
ghost/core/core/frontend/helpers/t.js (2)
  • require (13-13)
  • require (14-14)
ghost/core/test/unit/frontend/services/theme-engine/i18next.test.js (3)
ghost/core/test/unit/frontend/helpers/t-new.test.js (3)
  • should (1-1)
  • sinon (3-3)
  • path (2-2)
ghost/core/core/frontend/services/theme-engine/i18next/index.js (1)
  • ThemeI18n (3-3)
ghost/core/core/frontend/services/theme-engine/i18next/ThemeI18n.js (1)
  • path (3-3)
ghost/core/test/unit/frontend/helpers/t-new.test.js (3)
ghost/core/test/unit/frontend/services/theme-engine/i18next.test.js (3)
  • should (1-1)
  • path (4-4)
  • sinon (2-2)
ghost/core/core/frontend/helpers/t.js (3)
  • require (13-13)
  • require (14-14)
  • labs (15-15)
ghost/core/core/frontend/services/theme-engine/active.js (2)
  • themeI18next (21-21)
  • labs (22-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Setup
🔇 Additional comments (18)
ghost/core/core/shared/labs.js (1)

37-38: LGTM! Feature flag correctly added to beta features.

The themeTranslation flag is properly positioned and follows the established naming convention for feature flags.

ghost/core/core/frontend/services/handlebars.js (1)

22-22: LGTM! New theme translation service properly exported.

The themeI18next export is correctly added alongside the legacy themeI18n to support the feature flag-controlled transition to the new translation system.

apps/admin-x-settings/src/components/settings/advanced/labs/BetaFeatures.tsx (1)

74-77: LGTM! Feature toggle correctly implemented in admin UI.

The new theme translation toggle follows the established pattern and provides clear messaging about the feature's purpose.

ghost/core/core/frontend/services/theme-engine/i18next/index.js (1)

1-6: LGTM! Clean module structure with proper configuration.

The module correctly exports both an initialized instance and the class itself, with appropriate configuration for the themes directory.

ghost/core/core/frontend/services/theme-engine/active.js (2)

21-22: LGTM! Required imports added for feature flag functionality.

The new imports for themeI18next and labs are correctly added to support the conditional translation system initialization.


106-112: LGTM! Feature flag-controlled translation system initialization implemented correctly.

The conditional logic properly switches between the new i18next-based and legacy translation systems based on the themeTranslation feature flag, maintaining backward compatibility during the transition.

ghost/core/core/frontend/services/theme-engine/i18next/ThemeI18n.js (4)

1-20: LGTM! Constructor properly validates required parameters.

The constructor correctly validates the required basePath parameter and initializes all properties with sensible defaults. Error handling using Ghost's IncorrectUsageError is appropriate.


22-31: LGTM! Simple getter/setter for testing purposes.

The basePath getter/setter implementation is clean and serves its intended purpose for testing scenarios.


41-86: Robust fallback logic with comprehensive error handling.

The initialization method implements a well-thought-out fallback strategy:

  1. Theme locales directory missing → no-op translation
  2. Requested locale missing → fallback to English
  3. English missing → no-op translation

The async file existence checks before calling i18nLib are prudent given the synchronous nature of the underlying library.


95-101: Good safeguard against uninitialized usage.

The method correctly validates that initialization has occurred and ensures a consistent string return type. The error message including the translation key will be helpful for debugging.

ghost/core/test/unit/frontend/services/theme-engine/i18next.test.js (2)

27-44: Well-structured translation and fallback tests

Good coverage of translation retrieval scenarios including successful translation, fallback to English, and fallback to key when no translation files exist.


51-56: Excellent error handling test

Good test for the defensive check that prevents usage before initialization. The error message helpfully includes the key that triggered the error.

ghost/i18n/lib/i18n.js (1)

107-144: Well-structured resource loading with appropriate fallbacks

The implementation correctly handles theme namespace resource loading with a sensible fallback chain (requested locale → English → empty object). The error handling ensures the system remains functional even when translation files are missing or malformed.

ghost/core/test/unit/frontend/helpers/t-new.test.js (2)

26-65: Comprehensive translation tests with good coverage

Excellent test coverage for different locales and fallback scenarios. The tests properly verify the integration between the helper and the theme translation service.


66-81: Confirmed: early return handles empty/undefined keys without initialization

The t helper’s very first check

if (!text || text.length === 0) {
    return '';
}

returns an empty string before any call to themeI18next.init(). The existing tests for '' and undefined keys are therefore valid and don’t require an explicit init() call.

No changes needed.

ghost/i18n/test/i18n.test.js (3)

170-187: Well-structured test setup with proper cleanup

Good use of temporary directories and proper cleanup to ensure test isolation. The beforeEach/afterEach pattern ensures tests don't interfere with each other.


188-275: Comprehensive theme translation test coverage

Excellent test suite covering:

  • Translation loading from theme directories
  • Fallback behavior (missing translations, missing files)
  • Error handling (invalid JSON)
  • Configuration verification
  • Variable interpolation

The tests are well-structured and cover both happy paths and edge cases.


306-373: Thorough i18next configuration tests

Excellent coverage of i18next initialization including:

  • Configuration verification for all namespaces
  • Fallback language chain testing
  • Edge case handling for missing theme paths

These tests ensure the i18next instances are configured consistently and correctly.

Comment on lines +36 to +42
// Initialize only if needed
if (!themeI18next._i18n) {
themeI18next.init({
activeTheme: settingsCache.get('active_theme'),
locale: config.get('locale')
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix initialization logic to avoid race conditions and encapsulation violations

The current implementation has several issues:

  1. Accessing the private _i18n property violates encapsulation
  2. The initialization could race if multiple requests arrive before completion
  3. The init() call appears to be async but isn't awaited

Based on the learning that theme i18n initialization only happens during server boot, theme changes, and language changes, this initialization should not be in the helper function.

Consider moving the initialization to the appropriate lifecycle hooks (server boot, theme change, language change) and ensure the helper assumes themeI18next is already initialized:

    if (labs.isSet('themeTranslation')) {
        // Use the new translation package when feature flag is enabled
-
-        // Initialize only if needed
-        if (!themeI18next._i18n) {
-            themeI18next.init({
-                activeTheme: settingsCache.get('active_theme'),
-                locale: config.get('locale')
-            });
-        }
-
        return themeI18next.t(text, bindings);
    }

The initialization should be handled in the theme engine's active theme service or during application bootstrap.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Initialize only if needed
if (!themeI18next._i18n) {
themeI18next.init({
activeTheme: settingsCache.get('active_theme'),
locale: config.get('locale')
});
}
if (labs.isSet('themeTranslation')) {
// Use the new translation package when feature flag is enabled
return themeI18next.t(text, bindings);
}
🤖 Prompt for AI Agents
In ghost/core/core/frontend/helpers/t.js around lines 36 to 42, remove the
initialization logic that checks and initializes themeI18next inside the helper
function to avoid accessing the private _i18n property, prevent race conditions,
and handle async initialization properly. Instead, move the themeI18next
initialization to the appropriate lifecycle hooks such as server boot, theme
changes, or language changes within the theme engine's active theme service or
application bootstrap. Update the helper to assume themeI18next is already
initialized and remove any initialization code from it.

Comment on lines +19 to +20
i18n._locale.should.eql('en');
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid accessing private properties in tests

The test directly accesses the private property _locale. Consider adding a public getter method to the ThemeI18n class to expose the current locale, making the test less brittle to implementation changes.

🤖 Prompt for AI Agents
In ghost/core/test/unit/frontend/services/theme-engine/i18next.test.js around
lines 19 to 20, the test accesses the private property _locale directly, which
is brittle. Modify the ThemeI18n class to add a public getter method that
returns the current locale, then update the test to use this getter instead of
accessing _locale directly.

@ErisDS ErisDS closed this Jul 20, 2025
@ErisDS ErisDS deleted the cathy-theme-i18n branch July 21, 2025 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants