Conversation
|
You have run out of free Bugbot PR reviews for this billing cycle. This will reset on January 3. To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial. |
|
@JohnONolan Do we still have any blockers to start translating Official Themes? |
WalkthroughThis PR implements internationalization (i18n) support across the template system by adding translation helpers to user-facing text. A new English locale file (locales/en.json) is introduced with 42 UI string key definitions mapped to empty translations. Eleven template files are updated to wrap static text strings with a translation helper function ({{t ...}}), affecting titles, buttons, labels, placeholders, ARIA attributes, and navigation elements. Changes are presentation-layer substitutions only, with no modifications to control flow, business logic, or template structure. Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
This is the final PR Bugbot will review for you during this billing cycle
Your free Bugbot reviews will reset on March 3
Details
Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.
To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@private.hbs`:
- Around line 51-55: The password input lacks an accessible label—update the
template around the gh-signin form to provide one by either adding a <label>
tied to the input's id or adding an aria-label to the input_password helper; for
example ensure the input_password call includes a stable id (e.g.,
id="password") and either render <label for="password">{{t "Password"}}</label>
before it or add aria-label=(t "Password") to the input_password invocation so
screen readers can identify the field.
🧹 Nitpick comments (3)
partials/email-subscription.hbs (1)
2-4: Consider whether the email placeholder needs translation.The ARIA label and button text localization is correct. However, localizing the placeholder email address (
jamie@example.com) is uncommon—example email addresses are typically left as universal patterns since email format is the same across locales. Unless there's a specific requirement for locale-specific example names, you might consider keeping this as a static placeholder.That said, this is a minor stylistic choice and the current approach works correctly.
private.hbs (1)
31-33: Allow user zoom in the viewport meta.Disabling zoom hurts accessibility on mobile. Consider removing
user-scalable=noandmaximum-scale=1.Proposed change
- <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"> + <meta name="viewport" content="width=device-width, initial-scale=1">partials/pagination.hbs (1)
1-8: Addaria-labelon the nav andrelon prev/next links.
Improves accessibility and SEO without changing behavior.Proposed diff
-<nav class="pagination"> +<nav class="pagination" aria-label="{{t "Pagination"}}"> {{`#if` prev}} - <a class="newer-posts" href="{{page_url prev}}"><span aria-hidden="true">←</span> {{t "Newer Posts"}}</a> + <a class="newer-posts" href="{{page_url prev}}" rel="prev"><span aria-hidden="true">←</span> {{t "Newer Posts"}}</a> {{/if}} <span class="page-number">{{t "Page {page} of {totalPages}" page=page totalPages=pages}}</span> {{`#if` next}} - <a class="older-posts" href="{{page_url next}}">{{t "Older Posts"}} <span aria-hidden="true">→</span></a> + <a class="older-posts" href="{{page_url next}}" rel="next">{{t "Older Posts"}} <span aria-hidden="true">→</span></a> {{/if}} </nav>
private.hbs
Outdated
| <form class="gh-signin" method="post" novalidate="novalidate"> | ||
| <div class="form-group{{#if error}} error{{/if}}"> | ||
| {{input_password class="gh-input" placeholder=(t "Password")}} | ||
| {{#if error}} | ||
| <p class="main-error">{{error.message}}</p> |
There was a problem hiding this comment.
Add an accessible label for the password field.
The password input relies only on a placeholder; add an accessible label or aria-label.
Proposed change
- {{input_password class="gh-input" placeholder=(t "Password")}}
+ {{input_password class="gh-input" placeholder=(t "Password") aria-label=(t "Password")}}📝 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.
| <form class="gh-signin" method="post" novalidate="novalidate"> | |
| <div class="form-group{{#if error}} error{{/if}}"> | |
| {{input_password class="gh-input" placeholder=(t "Password")}} | |
| {{#if error}} | |
| <p class="main-error">{{error.message}}</p> | |
| <form class="gh-signin" method="post" novalidate="novalidate"> | |
| <div class="form-group{{`#if` error}} error{{/if}}"> | |
| {{input_password class="gh-input" placeholder=(t "Password") aria-label=(t "Password")}} | |
| {{`#if` error}} | |
| <p class="main-error">{{error.message}}</p> |
🤖 Prompt for AI Agents
In `@private.hbs` around lines 51 - 55, The password input lacks an accessible
label—update the template around the gh-signin form to provide one by either
adding a <label> tied to the input's id or adding an aria-label to the
input_password helper; for example ensure the input_password call includes a
stable id (e.g., id="password") and either render <label for="password">{{t
"Password"}}</label> before it or add aria-label=(t "Password") to the
input_password invocation so screen readers can identify the field.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@locales/en.json`:
- Around line 1-42: The locales/en.json contains 42 keys but only five are
actually referenced in templates ("1 min read", "% min read", "Page %", "Read
more", "Search this site"); confirm intent for the other 37 keys and either (a)
remove the unused keys from locales/en.json to avoid dead translations, (b) move
them to a separate archive or documentation file (e.g., unused_locale_keys.json)
if you want to keep them for future features, or (c) add a clear comment/README
entry describing which keys are intentionally retained for planned features;
update the file so it only contains the five active keys (above) or clearly
marks retained keys and their purpose.
🧹 Nitpick comments (2)
locales/en.json (2)
17-18: Clarify the purpose of two different "Page" translation keys.There are two "Page" related keys with different placeholder patterns:
"Page {page} of {totalPages}"- uses named placeholders"Page %"- uses single unnamed placeholderIf both are needed for different contexts (e.g., full pagination vs. abbreviated), this is fine. However, if they serve the same purpose, consider consolidating to avoid translator confusion.
11-11: Example email as translation key is unconventional but acceptable.Using
jamie@example.comas a key works for placeholder text, but translators may find it confusing since it looks like actual content rather than a UI string pattern. Consider using a more descriptive key like"email placeholder"if Ghost's translation system supports it, or document its purpose for translators.
| { | ||
| "% min read": "", | ||
| "1 min read": "", | ||
| " and ": "", | ||
| "Access site": "", | ||
| "Account": "", | ||
| "Already have an account?": "", | ||
| "By": "", | ||
| "Close (Esc)": "", | ||
| "Featured": "", | ||
| "jamie@example.com": "", | ||
| "Latest": "", | ||
| "Menu": "", | ||
| "Newer Posts": "", | ||
| "Next (arrow right)": "", | ||
| "Older Posts": "", | ||
| "Page {page} of {totalPages}": "", | ||
| "Page %": "", | ||
| "Password": "", | ||
| "Powered by {ghostlink}": "", | ||
| "Previous (arrow left)": "", | ||
| "Read more": "", | ||
| "Recommendations": "", | ||
| "Search posts, tags and authors": "", | ||
| "Search this site": "", | ||
| "See all": "", | ||
| "Share": "", | ||
| "Sign in": "", | ||
| "Subscribe": "", | ||
| "Subscribe now": "", | ||
| "This page is for paying subscribers only": "", | ||
| "This page is for subscribers on the {tiers} only": "", | ||
| "This page is for subscribers only": "", | ||
| "This post is for paying subscribers only": "", | ||
| "This post is for subscribers on the {tiers} only": "", | ||
| "This post is for subscribers only": "", | ||
| "This site is private.": "", | ||
| "Toggle fullscreen": "", | ||
| "Upgrade": "", | ||
| "Upgrade your account": "", | ||
| "Zoom in/out": "" | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's explore the repository structure
ls -laRepository: TryGhost/Source
Length of output: 1265
🏁 Script executed:
# Find templates and understand the structure
find . -type f -name "*.hbs" -o -name "*.handlebars" | head -20Repository: TryGhost/Source
Length of output: 597
🏁 Script executed:
# Check for locale files
find . -type f -name "*.json" | grep -i localeRepository: TryGhost/Source
Length of output: 76
🏁 Script executed:
#!/bin/bash
# Find all {{t "..."}} calls in templates and compare with locale keys
echo "=== Translation keys used in templates ==="
rg '\{\{t\s+"([^"]+)"' --no-filename -r '$1' --type hbs | sort -u > /tmp/template_keys.txt
cat /tmp/template_keys.txt
echo ""
echo "=== Keys defined in locale file ==="
cat locales/en.json | jq -r 'keys[]' | sort > /tmp/locale_keys.txt
cat /tmp/locale_keys.txt
echo ""
echo "=== Keys in templates but MISSING from locale file ==="
comm -23 /tmp/template_keys.txt /tmp/locale_keys.txt
echo ""
echo "=== Keys in locale file but NOT used in templates ==="
comm -13 /tmp/template_keys.txt /tmp/locale_keys.txtRepository: TryGhost/Source
Length of output: 6699
🏁 Script executed:
# Let's also check the locale file content directly to verify format
cat locales/en.jsonRepository: TryGhost/Source
Length of output: 1129
🏁 Script executed:
# Look at one of the main template files
cat -n default.hbsRepository: TryGhost/Source
Length of output: 3490
🏁 Script executed:
# Search for any translation or i18n patterns in templates
rg -i "{{.*t\s+" --type hbs -A 2 -B 2 | head -50Repository: TryGhost/Source
Length of output: 142
🏁 Script executed:
# Also check for `@t` or other i18n patterns
rg "@t|{{t|trans|i18n" --type hbs | head -20Repository: TryGhost/Source
Length of output: 154
🏁 Script executed:
#!/bin/bash
# Find all (t "...") translation calls in templates
echo "=== Translation keys used in templates ==="
rg '\(t\s+"([^"]+)"' --no-filename -r '$1' --type hbs | sort -u > /tmp/template_keys.txt
cat /tmp/template_keys.txt
echo ""
echo "=== Keys defined in locale file ==="
cat locales/en.json | jq -r 'keys[]' | sort > /tmp/locale_keys.txt
cat /tmp/locale_keys.txt
echo ""
echo "=== Keys in templates but MISSING from locale file ==="
comm -23 /tmp/template_keys.txt /tmp/locale_keys.txt
echo ""
echo "=== Keys in locale file but NOT used in templates ==="
comm -13 /tmp/template_keys.txt /tmp/locale_keys.txtRepository: TryGhost/Source
Length of output: 2071
🏁 Script executed:
# Also verify the Page % pattern is used
rg '\(t\s+"Page' --type hbs -B 1 -A 1Repository: TryGhost/Source
Length of output: 251
🏁 Script executed:
#!/bin/bash
# Find all lines with (t " pattern
echo "=== All translation calls in templates ==="
rg '\(t\s+"' --type hbs -o
echo ""
echo "=== Count ==="
rg '\(t\s+"' --type hbs -o | wc -lRepository: TryGhost/Source
Length of output: 163
🏁 Script executed:
# Let's search more broadly for any helper that might be i18n
rg 'reading_time|t\s+"' --type hbs -B 2 -A 2 | head -40Repository: TryGhost/Source
Length of output: 2876
🏁 Script executed:
# Check what the reading_time helper expects
rg 'reading_time' --type hbs -B 1 -A 1Repository: TryGhost/Source
Length of output: 473
All translation keys used in templates are present, but 37 out of 42 locale keys appear unused.
The empty string values are correct for the English base locale—the key itself will be displayed as text for languages not in the locale file.
However, the verification reveals only 5 translation keys are actually invoked in templates:
- "1 min read" (post.hbs)
- "% min read" (post.hbs)
- "Page %" (default.hbs)
- "Read more" (post.hbs)
- "Search this site" (search-toggle.hbs)
The remaining 37 keys in the locale file have no corresponding template usage. Clarify whether these are:
- Planned for future features
- Remnants from removed code that should be deleted
- Maintained for documentation purposes
🤖 Prompt for AI Agents
In `@locales/en.json` around lines 1 - 42, The locales/en.json contains 42 keys
but only five are actually referenced in templates ("1 min read", "% min read",
"Page %", "Read more", "Search this site"); confirm intent for the other 37 keys
and either (a) remove the unused keys from locales/en.json to avoid dead
translations, (b) move them to a separate archive or documentation file (e.g.,
unused_locale_keys.json) if you want to keep them for future features, or (c)
add a clear comment/README entry describing which keys are intentionally
retained for planned features; update the file so it only contains the five
active keys (above) or clearly marks retained keys and their purpose.
This PR includes t wrapping of (I think) all text strings in Source.
Note
Low Risk
Primarily wraps UI copy in the Ghost
{{t}}helper and adds anen.jsonlocale file; low risk but any missed/incorrect translation keys could cause fallback/missing text in the UI.Overview
Adds translation support across the theme by wrapping hardcoded UI strings in Handlebars templates with the Ghost
{{t}}helper, including menu/search labels, post list headings/CTAs, featured/recommendations sections, email subscription copy, lightbox tooltips, and related-posts headings.Introduces
locales/en.jsonwith the set of translation keys used by the theme (including placeholders like page titles and reading-time strings), and updatesmeta_title/reading_timeusage to pass localized formats and parameters.Written by Cursor Bugbot for commit 8874cf6. This will update automatically on new commits. Configure here.