Skip to content

Added Georgian locale for Portal - #29777

Open
coachsergio wants to merge 1 commit into
TryGhost:mainfrom
coachsergio:main
Open

Added Georgian locale for Portal#29777
coachsergio wants to merge 1 commit into
TryGhost:mainfrom
coachsergio:main

Conversation

@coachsergio

Copy link
Copy Markdown

Added complete .json files with the Georgian locale translation, please review and accept the pull request if everything's according to standards.

@github-actions github-actions Bot left a comment

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.

🌐 Automated translation review

Verdict: ⏭️ Skipped — PR is too large for automated review

Reviewed 516 translations across 5 files.

This PR adds 516 translation lines, which is beyond the automated reviewer's per-PR limit (max 15 files / 500 lines). A maintainer should review the translations manually.


Advisory review by i18n-review-bot. Non-blocking — a maintainer still owns the merge decision, and the bot cannot approve PRs on its own. Translator expertise wins where there is doubt.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds Georgian locale files under packages/i18n/locales/ge. It introduces translations for the main ghost.json resource, comments UI, portal UI, search UI, and signup form UI. The new strings cover authentication, subscriptions, memberships, gifts, billing, comments, moderation, navigation, email-related text, search labels, and signup validation or confirmation messages. The files preserve existing interpolation placeholders and pluralized forms where those keys exist.

Possibly related PRs

  • TryGhost/Ghost#29768: Both PRs add or complete translations in the same Georgian-adjacent i18n surfaces, including comments.json, ghost.json, and portal.json, but for different languages.
  • TryGhost/Ghost#29265: Both PRs update portal.json translation resources and add localized portal message keys for a language pack.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding the Georgian locale for Portal.
Description check ✅ Passed The description directly relates to adding Georgian JSON translation files and requests review of the localization changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 11

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/i18n/locales/ge/comments.json`:
- Line 25: Correct the Georgian member-status translations in the locale entries
“Deleted member” and the entry at line 37: replace the misspelled `ხელმომზერი`
with `ხელმომწერი`, and change `დამალურია` to the established correct `დამალ...`
form used by related translations.
- Line 40: Update the ge locale entries in comments.json for the affected action
labels so each key maps to a distinct, accurate translation. In particular,
adjust the values for "Join the discussion" and "Sign up now" instead of using
wording that means “return to the discussion” or “subscribe.” Keep the existing
keys unchanged and ensure each label clearly reflects its own user action.

In `@packages/i18n/locales/ge/ghost.json`:
- Line 67: Update the Georgian translation value for the signup completion
message to include the `{siteTitle}` placeholder, preserving the existing
placeholder syntax and natural Georgian wording.
- Line 35: Correct the Georgian spelling in the translations at lines 35 and 98:
update `მოგიხოვიათ` to match the established `მოგითხოვიათ` wording, and replace
the misspelled `დაადასტირეთ` with the file’s consistent confirmation wording.
Preserve the surrounding translation text.
- Line 26: Update the Georgian translations for the messages at keys “For your
security, the link will expire in 24 hours time.”, “Member since”, and “You will
not be subscribed” so each preserves the exact source meaning: an expiring link,
membership start date, and non-subscription outcome respectively.
- Line 1: Add the ge locale entry to the locale registry in locale-data.json so
i18n-core.js includes ge in SUPPORTED_LOCALES and registers the existing
packages/i18n/locales/ge/ghost.json translations.
- Around line 1-112: Add non-empty context entries for "{count} month_many" and
"{count} year_many" matching the reference definitions so Georgian locale
synchronization passes. Apply the direct change at
packages/i18n/locales/ge/ghost.json lines 1-112;
packages/i18n/locales/ge/comments.json lines 1-88,
packages/i18n/locales/ge/search.json lines 1-9,
packages/i18n/locales/ge/signup-form.json lines 1-9, and
packages/i18n/locales/ge/portal.json lines 1-299 require no direct changes.

In `@packages/i18n/locales/ge/portal.json`:
- Line 31: Correct the portal UI copy in packages/i18n/locales/ge/portal.json:
add the missing space after </a> in the line 31 translation, and fix the
spelling errors in the translations at lines 36, 174, 219, 232, 247, 289, and
293 without changing their intended meaning.
- Line 206: Update the Georgian translation for “Sorry, no paid plans are
available.” so it states that paid plans are unavailable, preserving the source
meaning and avoiding any reference to free memberships.
- Line 171: Update the Georgian portal translations for the `Open Mail.ru` and
`Subscribe` string keys in the locale JSON so they read as clear action labels
rather than questions or temporal phrases. Keep the existing keys and only
adjust the translated values in the portal controls section to match
button-style imperative wording.
- Line 290: Update the `Your gift is ready` entry in the Georgian locale so its
value is a Georgian translation rather than the existing French text, while
preserving the key and JSON structure.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 25093ca1-e97f-40e8-b791-12d5c71c1d17

📥 Commits

Reviewing files that changed from the base of the PR and between e56a230 and a413fc4.

📒 Files selected for processing (5)
  • packages/i18n/locales/ge/comments.json
  • packages/i18n/locales/ge/ghost.json
  • packages/i18n/locales/ge/portal.json
  • packages/i18n/locales/ge/search.json
  • packages/i18n/locales/ge/signup-form.json

"Complete your profile": "შეავსეთ ანკეტა",
"Delete": "წაშლა",
"Deleted": "წაშლილია",
"Deleted member": "ხელმომზერი წაშლილია",

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix the member-status spelling errors.

Line 25 uses ხელმომზერი, while related text uses ხელმომწერი. Line 37 uses დამალურია, which should match the correct დამალ... forms used elsewhere.

Also applies to: 37-37

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/i18n/locales/ge/comments.json` at line 25, Correct the Georgian
member-status translations in the locale entries “Deleted member” and the entry
at line 37: replace the misspelled `ხელმომზერი` with `ხელმომწერი`, and change
`დამალურია` to the established correct `დამალ...` form used by related
translations.

"Hidden for members": "დამალურია წევრებისთვის",
"Hide comment": "დამალეთ კომენტარი",
"Jamie Larson": "ნინო ნინიძე",
"Join the discussion": "დაუბრუნდით დისკუსიას",

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the comment actions distinct.

Join the discussion is translated as “return to the discussion”. Sign up now is translated as “subscribe”. These labels can show users the wrong action.

Also applies to: 74-74

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/i18n/locales/ge/comments.json` at line 40, Update the ge locale
entries in comments.json for the affected action labels so each key maps to a
distinct, accurate translation. In particular, adjust the values for "Join the
discussion" and "Sign up now" instead of using wording that means “return to the
discussion” or “subscribe.” Keep the existing keys unchanged and ensure each
label clearly reflects its own user action.

@@ -0,0 +1,112 @@
{

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

pnpm exec node <<'NODE'
const fs = require('fs');
const path = require('path');

function walk(dir) {
  return fs.readdirSync(dir, {withFileTypes: true}).flatMap(entry => {
    const file = path.join(dir, entry.name);
    return entry.isDirectory() ? walk(file) : [file];
  });
}

for (const file of walk('packages/i18n')) {
  if (!/\.(js|ts|json)$/.test(file)) continue;
  const lines = fs.readFileSync(file, 'utf8').split('\n');
  lines.forEach((line, index) => {
    if (/SUPPORTED_LOCALES|LOCALE_DATA|["']ge["']/.test(line)) {
      console.log(`${file}:${index + 1}:${line}`);
    }
  });
}
NODE

Repository: TryGhost/Ghost

Length of output: 2974


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "== Supported locale codes =="
python3 - <<'PY'
import json
from pathlib import Path
p = Path('packages/i18n/lib/locale-data.json')
data = json.loads(p.read_text())
codes = [entry.get('code') for entry in data if isinstance(entry, dict)]
print(f'count={len(codes)} ge_included={("ge" in codes)} de_included={("de" in codes)}')
print('\n'.join(codes[:120]))
print('\n...')
print('\n'.join(codes[-120:]))
PY

echo
echo "== package/i18n files =="
git ls-files packages/i18n | sed -n '1,120p'

echo
echo "== localization entry if present =="
python3 - <<'PY'
import json
for i, entry in enumerate(json.load(open('packages/i18n/lib/locale-data.json'))):
    if entry.get('code') == 'ge':
        print(i, json.dumps(entry, ensure_ascii=False, indent=2))
        break
else:
    print('ge not found')
PY

echo
echo "== ge locale files =="
git ls-files 'packages/i18n/locales/ge/*' packages/i18n/locales/ge/ge.json packages/i18n/locales/ge/ghost.json packages/i18n/locales/context.json | sed -n '1,200p'

echo
echo "== context keys containing ge/ghost =="
python3 - <<'PY'
import json
from pathlib import Path
ctx = json.load(Path('packages/i18n/locales/context.json').open())
for k, v in ctx.items():
    if 'ge' in k.lower() or 'ghost' in k.lower():
        print(k, v)
PY

Repository: TryGhost/Ghost

Length of output: 8841


Add ge to the i18n locale registry.

packages/i18n/lib/i18n-core.js derives SUPPORTED_LOCALES from packages/i18n/lib/locale-data.json, and ge is missing from that file. The locale files exist under packages/i18n/locales/ge/, but packages/i18n/locales/ge/ghost.json will not be registered until the entry is added.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/i18n/locales/ge/ghost.json` at line 1, Add the ge locale entry to
the locale registry in locale-data.json so i18n-core.js includes ge in
SUPPORTED_LOCALES and registers the existing packages/i18n/locales/ge/ghost.json
translations.

Comment on lines +1 to +112
{
"{count} month_one": "{count} თვე",
"{count} month_many": "{count} თვეები",
"{count} month_other": "{count} თვე",
"{count} year_one": "{count} წელი",
"{count} year_many": "{count} წლები",
"{count} year_other": "{count} წელი",
"{date}": "{date}",
"{tierName} membership": "აბონემენტი {tierName}",
"All the best!": "წარმატებები !",
"Become a paid member of {site} to get access to all premium content.": "თუკი აბონემენტს შეიძენთ {site}-ზე ფასიანი სტატიების ნახვას შეძლებთ.",
"By {authors}": " {authors}",
"Comment": "კომენტარი",
"Complete signup for {siteTitle}!": "დაასრულეთ {siteTitle}-ზე გაწევრება !",
"Complete your sign up to {siteTitle}!": "დაასრულეთ {siteTitle}-ზე გაწევრება !",
"complimentary": "ნაჩუქარი",
"Confirm email address": "დაადასტურეთ ელ.ფოსტის მისამართი",
"Confirm signup": "დაადასტურეთ გაწევრება",
"Confirm your email address": "დაადასტურეთ ელ.ფოსტის მისამართი",
"Confirm your email update for {siteTitle}!": "დაადასტურეთ ელ.ფოსტის განახლება საიტისთვის {siteTitle} !",
"Confirm your subscription to {siteTitle}": "დაადასტურეთ, რომ გამოიწერეთ {siteTitle}",
"Continue subscription": "გააგრძელეთ გამოწერა",
"Device:": "მოწყობილობა:",
"Email": "ელ. ფოსტა",
"For security verification, enter the code below to sign in to {siteTitle}:": "უსაფრთხოების მიზნით, შეიყვანეთ მოცემული კოდი საიტზე {siteTitle} :",
"For your security, the link will expire in 24 hours time.": "უსაფრთხოებისათვის, კოდს ვადა 24 საათში გაუვა.",
"free": "უფასო",
"Gift subscription": "სასაჩუქრე აბონემენტი",
"Here's how to keep your {tierName} membership.": "თქვენი აბონემენტის ({tierName}) შესანარჩუნებლად...",
"Here's your code to login to {siteTitle}": "ინებეთ კოდი საიტზე {siteTitle} შესასვლელად",
"Hey there,": "გამარჯობათ,",
"Hey there!": "გამარჯობათ !",
"Hi {firstName},": "გამარჯობა {firstName},",
"If you did not make this request, you can safely ignore this email.": "თუ არაფერი მოგითხოვიათ, ამ გზავნილს ყურადღებას ნუ მიაქცევთ.",
"If you did not make this request, you can simply delete this message.": "თუკი თქვენ არაფერი მოგიხოვიათ, შეგიძლიათ ეს გზავნილი წაშალოთ.",
"If you didn't try to sign in recently, you can safely ignore this email to deny access.": "თუკი უკანასკნელ ხანებში არ გიცდიათ საიტზე შესვლა, შეგიძლიათ ყურადღება არ მიაქციოთ ამ გზავნილს.",
"Keep reading": "განაგრძეთ კითხვა",
"Less like this": "ნაკლები ასეთი სტატია მიჩვენე",
"Manage subscription": "აბონემენტის მართვა",
"Manage your preferences": "ანგარიშის მართვა",
"Member since": "აბონემენტი მოქმედია",
"More like this": "მეტი ასეთი სტატია მიჩვენე",
"Name": "სახელი",
"New comment on {postTitle}": "ახალი კომენტარია პოსტზე {postTitle}",
"New reply to your comment on {siteTitle}": "თქვენს კომენტარზე ახალი პასუხი მოვიდა საიტზე {siteTitle}",
"one-month": "ერთი თვე",
"one-year": "ერთი წელი",
"Open this link to redeem your gift.": "დააჭირეთ ნაჩუქარი აბონემენტის გასააქტიურებლად.",
"Or use this link to securely sign in": "ან გამოიყენეთ ეს ბმული უსაფრთხოდ შესასვლელად",
"Or, skip the code and sign in directly": "ან ნუ გამოიყენებთ კოდს და პირდაპირ დაუკავშირდით საიტს",
"paid": "გადახდილია",
"Please confirm your email address with this link:": "ბმულზე დაჭერით დაადასტურებთ ელ.ფოსტის მისამართს:",
"Redeem your gift subscription": "ნაჩუქარი აბონემენტის გააქტიურება",
"Secure sign in link for {siteTitle}": "{siteTitle}-თან დაკავშირების დაცული ბმული",
"See you soon!": "ნახვამდის !",
"Sent to {email}": "გაგზავნეთ მისამართზე {email}",
"Share": "გააზიარეთ",
"Sign in": "შესვლა",
"Sign in now": "შედით საიტზე",
"Sign in to {siteTitle}": "შედით საიტზე {siteTitle}",
"Sign in to {siteTitle} with code {otc}": "საიტთან {siteTitle} დაკავშირება კოდით {otc}",
"Sign in verification": "კავშირის დადასტურება",
"Someone just replied to your comment": "ვიღაცამ თქვენს კომენტარს უპასუხა",
"Someone just replied to your comment on {postTitle}.": "ვიღაცამ თქვენს კომენტარს უპასუხა პოსტზე {postTitle}.",
"Subscription details": "აბონემენტის დეტალები",
"Support {siteTitle}": "დაეხმარეთ საიტს {siteTitle}",
"Tap the link below to complete the signup process for {siteTitle}, and be automatically signed in:": "ბმულზე დაჭერით დაასრულებთ გაწევრების პროცესს და ავტომატურად შეხვალთ საიტზე:",
"Thank you for signing up to {siteTitle}!": "მადლობა რომ გამოიწერეთ {siteTitle} !",
"Thank you for subscribing to {siteTitle}!": "მადლობა, რომ შეიძინეთ აბონემენტი საიტისთვის {siteTitle} !",
"Thank you for subscribing to {siteTitle}.": "მადლობა, რომ შეიძინეთ აბონემენტი საიტისთვის {siteTitle}.",
"Thank you for subscribing to {siteTitle}. Tap the link below to be automatically signed in:": "მადლობა, რომ შეიძინეთ აბონემენტი საიტისთვის {siteTitle}. დააჭირეთ ბმულს საიტზე ავტომატურად შესასვლელად:",
"Thank you for supporting {siteTitle}.": "მადლობა, რომ დაეხმარეთ საიტს {siteTitle}.",
"Thank you for your support. Share the link below with whoever you'd like to gift them a {cadenceLabel} {tierName} membership to {siteTitle}.": "მადლობა მხარდაჭერისთვის, შეგიძლიათ ეს ბმული სასურველ პიროვნებას გაუზიაროთ და {cadenceLabel} აბონემენტს {tierName} მიიღებს საიტისთვის {siteTitle}.",
"Thanks for reading {siteTitle}.": "მადლობა, რომ კითხულობთ საიტს {siteTitle}.",
"The link expires on {expiresAt} and can only be redeemed once.": "ამ ბმულს ვადა გასდის {expiresAt} და მისი გამოყენება მხოლოდ ერთხელ შეგიძლიათ.",
"This email address will not be used.": "ელ. ფოსტის ეს მისამართი არ იქნება გამოყენებული.",
"This message was sent from {siteDomain} to {email}.": "ეს გზავნილი გაიგზავნა საიტიდან {siteDomain} მისამართზე {email}.",
"To keep your {tierName} membership, continue with a paid subscription today and we'll automatically add the rest of your gift period as a free trial.": "თუკი თქვენი აბონემენტის {tierName} შენარჩუნება გსურთ, იყიდეთ ფასიანი აბონემენტი და ძველ აბონემენტზე დარჩენილი დრო ახალს უფასო ვადად დაემატება.",
"trialing": "მიმდინარეობს საცდელი ვადა",
"Unsubscribe": "აბონემენტის გაუქმება",
"Unsubscribe from comment reply notifications": "კომენტარებზე პასუხების შესახებ შეტყობინებების გათიშვა",
"Upgrade": "აბონემენტის დონის შეცვლა",
"Upgrade to continue reading.": "წასაკითხად, გააუმჯობესეთ აბონემენტის დონე.",
"View comments": "კომენტარების ნახვა",
"View in browser": "ბრაუზერში ნახვა",
"Welcome back to {siteTitle}!": "სასიამოვნოა თქვენი ხილვა ჩვენს გვერდზე {siteTitle} !",
"Welcome back to {siteTitle}! Your verification code is {otc}.": "გვიხარია, რომ დაგვიბრუნდით გვერდზე {siteTitle}! დადასტურების კოდია {otc}.",
"Welcome back! Here's your code to sign in to {siteTitle}": "გაგვახარეთ, რომ დაგვიბრუნდით ! ინებეთ გვერდზე {siteTitle} შემოსასვლელი კოდი",
"Welcome back! Use this link to securely sign in to your {siteTitle} account:": "გაგვახარეთ, რომ დაბრუნდით! მიჰყევით ამ ბმულს და უსაფრთხოდ დაუკავშირდით თქვენს ანგარიშს გვერდზე {siteTitle} :",
"When:": "როდის :",
"Where:": "სად :",
"You are receiving this because you are a <strong>%%{status}%% subscriber</strong> to {site}.": "ამ გზავნილს გიგზავნით როგორც გვერდის {site} <strong>%%{status}%% აბონემენტის მქონეს </strong>.",
"You can also copy & paste this URL into your browser:": "ანდა დააკოპირეთ ეს მისამართი თქვენს ბრაუზერში:",
"You can unsubscribe from these notifications at {profileUrl}.": "შეტყობინებების გამორთვა შეგიძლიათ აქ: {profileUrl}.",
"You just tried to access your account from a new device.": "ახალი აპარატიდან შეეცადეთ თქვენს ანგარიშზე შესვლას.",
"You will not be signed up, and no account will be created for you.": "არ გაივლით რეგისტრაციას და არც ანგარიში შეიქმნება.",
"You will not be subscribed.": "არ გაივლით რეგისტრაციას.",
"You're one tap away from subscribing to {siteTitle} — please confirm your email address with this link:": "ერთი ნაბიჯიღა დარჩა გვერდზე {siteTitle} რეგისტრაციისათვის — დაადასტირეთ თქვენი ელ. ფოსტის მისამართი ამ ბმულზე დაჭერით:",
"You're one tap away from subscribing to {siteTitle}!": "ერთი ნაბიჯიღა დაგრჩათ საიტზე {siteTitle} რეგისტრაციისათვის !",
"You've been gifted a {duration}-month {tierName} membership to {siteTitle}": "თქვენ გაჩუქეს {duration}-თვიანი აბონემენტი {tierName} საიტისთვის {siteTitle}.",
"You've been gifted a {duration}-year {tierName} membership to {siteTitle}": "თქვენ გაჩუქეს {duration}-წლიანი აბონემენტი {tierName} საიტისთვის {siteTitle}",
"Your free trial ends on {date}, at which time you will be charged the regular price. You can always cancel before then.": "უფასო საცდელი ვადა სრულდება {date}, ამ თარიღის შემდეგ ჩვეულებრივ ფასს გადაიხდით. მანამდე შეგიძლიათ გააუქმოთ ხელმოწერა, როცა მოგესურვებათ.",
"Your gift is ready": "თქვენი საჩუქარი მზადაა",
"Your gift subscription confirmation": "სასაჩუქრე აბონემენტის დადასტურება",
"Your gift subscription is ending soon": "თქვენს სასაჩუქრე აბონემენტს ვადა მალე გასდის",
"Your gift subscription to {siteTitle} ends on {consumesAt}.": "თქვენს სასაჩუქრე აბონემენტს საიტისთვის {siteTitle} ვადა გასდის {consumesAt}.",
"Your subscription has been canceled and will expire on {date}. You can resume your subscription via your account settings.": "თქვენი აბონემენტი გაუქმებულია ან ვადა გასდის {date}. შეგიძლიათ, განაახლოთ გამოწერა თქვენი ანგარიშის პარამეტრების გვერდიდან.",
"Your subscription has expired.": "თქვენს აბონემენტს ვადა გაუვიდა.",
"Your subscription will expire on {date}.": "თქვენს აბონემენტს ვადა გასდის {date}.",
"Your subscription will renew on {date}.": "თქვენი აბონემენტი განახლდება {date}.",
"Your verification code for {siteTitle}": "დადასტურების კოდი საიტისთვის {siteTitle}"
}

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.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

pnpm exec node <<'NODE'
const fs = require('fs');
const path = require('path');

const root = 'packages/i18n/locales';
const geDir = path.join(root, 'ge');
const contextPath = path.join(root, 'context.json');

const readJson = file => JSON.parse(fs.readFileSync(file, 'utf8'));

function findContext(node, key) {
  if (!node || typeof node !== 'object') return undefined;
  if (Object.prototype.hasOwnProperty.call(node, key)) return node[key];

  for (const value of Object.values(node)) {
    const result = findContext(value, key);
    if (result !== undefined) return result;
  }
}

const localeDirs = fs.readdirSync(root, {withFileTypes: true})
  .filter(entry => entry.isDirectory() && entry.name !== 'ge')
  .map(entry => entry.name);

const referenceLocale = localeDirs.includes('en') ? 'en' : localeDirs[0];
const context = fs.existsSync(contextPath) ? readJson(contextPath) : null;

for (const name of fs.readdirSync(geDir).filter(name => name.endsWith('.json'))) {
  const geFile = path.join(geDir, name);
  const ge = readJson(geFile);

  if (context) {
    const missingContext = Object.keys(ge).filter(key => {
      const value = findContext(context, key);
      return typeof value !== 'string' || value.trim() === '';
    });
    console.log(`${name} missing or empty context:`, missingContext);
  } else {
    console.log('Missing context file:', contextPath);
  }

  if (!referenceLocale) {
    console.log(`${name}: no reference locale found`);
    continue;
  }

  const referenceFile = path.join(root, referenceLocale, name);
  if (!fs.existsSync(referenceFile)) {
    console.log(`${name}: missing reference resource ${referenceFile}`);
    continue;
  }

  const reference = readJson(referenceFile);
  console.log(`${name} missing keys:`,
    Object.keys(reference).filter(key => !(key in ge)));
  console.log(`${name} extra keys:`,
    Object.keys(ge).filter(key => !(key in reference)));
}
NODE

Repository: TryGhost/Ghost

Length of output: 1623


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

pnpm exec node <<'NODE'
const fs = require('fs');
const path = require('path');
const root = 'packages/i18n/locales';
const context = JSON.parse(fs.readFileSync(path.join(root, 'context.json'), 'utf8'));

for (const key of ['{count} month_many', '{count} year_many']) {
  const value = Object.prototype.hasOwnProperty.call(context, key)
    ? context[key]
    : '<missing>';
  console.log(`${key}:`, value);
  if (typeof value === 'string') {
    console.log(`${key} empty after trim: ${value.trim().length === 0}`);
  }
}
NODE

Repository: TryGhost/Ghost

Length of output: 622


Add Georgian context for the new plural keys.

packages/i18n/locales/ge/ghost.json has two reference-consistent keys with missing context in packages/i18n/locales/context.json: {count} month_many and {count} year_many. Add non-empty context entries before merge so Georgian locale syncing passes.

📍 Affects 5 files
  • packages/i18n/locales/ge/ghost.json#L1-L112 (this comment)
  • packages/i18n/locales/ge/comments.json#L1-L88
  • packages/i18n/locales/ge/search.json#L1-L9
  • packages/i18n/locales/ge/signup-form.json#L1-L9
  • packages/i18n/locales/ge/portal.json#L1-L299
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/i18n/locales/ge/ghost.json` around lines 1 - 112, Add non-empty
context entries for "{count} month_many" and "{count} year_many" matching the
reference definitions so Georgian locale synchronization passes. Apply the
direct change at packages/i18n/locales/ge/ghost.json lines 1-112;
packages/i18n/locales/ge/comments.json lines 1-88,
packages/i18n/locales/ge/search.json lines 1-9,
packages/i18n/locales/ge/signup-form.json lines 1-9, and
packages/i18n/locales/ge/portal.json lines 1-299 require no direct changes.

Source: Coding guidelines

"Device:": "მოწყობილობა:",
"Email": "ელ. ფოსტა",
"For security verification, enter the code below to sign in to {siteTitle}:": "უსაფრთხოების მიზნით, შეიყვანეთ მოცემული კოდი საიტზე {siteTitle} :",
"For your security, the link will expire in 24 hours time.": "უსაფრთხოებისათვის, კოდს ვადა 24 საათში გაუვა.",

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep these messages aligned with their source meaning.

  • Line 26 says that a link expires, but the translation says that a code expires.
  • Line 41 changes Member since to an active-subscription status.
  • Line 97 changes You will not be subscribed to a registration message.

These changes can mislead users in authentication and subscription flows.

Also applies to: 41-41, 97-97

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/i18n/locales/ge/ghost.json` at line 26, Update the Georgian
translations for the messages at keys “For your security, the link will expire
in 24 hours time.”, “Member since”, and “You will not be subscribed” so each
preserves the exact source meaning: an expiring link, membership start date, and
non-subscription outcome respectively.

"Someone just replied to your comment on {postTitle}.": "ვიღაცამ თქვენს კომენტარს უპასუხა პოსტზე {postTitle}.",
"Subscription details": "აბონემენტის დეტალები",
"Support {siteTitle}": "დაეხმარეთ საიტს {siteTitle}",
"Tap the link below to complete the signup process for {siteTitle}, and be automatically signed in:": "ბმულზე დაჭერით დაასრულებთ გაწევრების პროცესს და ავტომატურად შეხვალთ საიტზე:",

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve {siteTitle} in this translation.

The source key contains {siteTitle}, but the Georgian value omits it. Add the placeholder so the email includes the publication name.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/i18n/locales/ge/ghost.json` at line 67, Update the Georgian
translation value for the signup completion message to include the `{siteTitle}`
placeholder, preserving the existing placeholder syntax and natural Georgian
wording.

"After a free trial ends, you will be charged the regular price for the tier you've chosen. You can always cancel before then.": "საცდელი ვადის დასრულების შემდეგ, აბონემენტის კატეგორიის ჩვეულებრივ ფასს გადაიხდით, გამოწერის გაუქმება მანამდე ნებისმიერ დროს შეგიძლიათ.",
"Already a member?": "უკვე გამოწერილი გაქვთ?",
"An error occurred": "შეცდომა მოხდა",
"An unexpected error occured. Please try again or <a>contact support</a> if the error persists.": "რაღაც არ მუშაობს, კიდევ სცადეთ ანდა <a>ტექმომსახურებას მისწერეთ</a>თუ არ გასწორდა.",

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix the visible copy defects.

Correct the missing space after </a> at line 31 and the spelling errors at lines 36, 174, 219, 232, 247, 289, and 293. These strings are shown directly in the portal UI.

Also applies to: 36-36, 174-174, 219-219, 232-232, 247-247, 289-289, 293-293

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/i18n/locales/ge/portal.json` at line 31, Correct the portal UI copy
in packages/i18n/locales/ge/portal.json: add the missing space after </a> in the
line 31 translation, and fix the spelling errors in the translations at lines
36, 174, 219, 232, 247, 289, and 293 without changing their intended meaning.

"Open Gmail": "გახსენით Gmail",
"Open Hey": "გახსენით Hey",
"Open iCloud Mail": "გახსენით iCloud Mail",
"Open Mail.ru": "რამ გაგახსნევინათ Mail.ru",

@coderabbitai coderabbitai Bot Aug 5, 2026

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use action labels for these portal controls.

Open Mail.ru is translated as a question. Subscribe is translated as “when subscribing”. Both values do not provide a clear action label.

Also applies to: 214-214

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/i18n/locales/ge/portal.json` at line 171, Update the Georgian portal
translations for the `Open Mail.ru` and `Subscribe` string keys in the locale
JSON so they read as clear action labels rather than questions or temporal
phrases. Keep the existing keys and only adjust the translated values in the
portal controls section to match button-style imperative wording.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It is a joke, "what pushed you to have Mail.ru" - works in Georgian context, where .ru domains are suspect. It could be directly translated into "გახსენით Mail.ru"

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.

@JDGit-lab, understood. The joke is clear in the Georgian context.

For this portal control, use გახსენით Mail.ru to provide a direct action label. The separate Subscribe translation should also use an imperative action label.

You are interacting with an AI system.

"Signup error: Invalid link": "შეცდომა გამოწერისას: ბმული არასწორია",
"Signups from this email domain are currently restricted.": "ელ.ფოსტის ამ დომენიდან გამოწერა შეზღუდულია.",
"Something went wrong, please try again later.": "რაღაც არ მუშაობს, მოგვიანებით სცადეთ.",
"Sorry, no paid plans are available.": "ბოდიში, უფასო აბონემენტები არა გვაქვს",

@coderabbitai coderabbitai Bot Aug 5, 2026

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not reverse paid and free plan availability.

The source says that paid plans are unavailable. The translation says that free memberships are unavailable. This can direct users to the wrong subscription action.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/i18n/locales/ge/portal.json` at line 206, Update the Georgian
translation for “Sorry, no paid plans are available.” so it states that paid
plans are unavailable, preserving the source meaning and avoiding any reference
to free memberships.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The translation is essentially the same, but it coud be transformed into "ბოდიში, ფასიანი აბონემენტები არა გვაქვს"

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.

@JDGit-lab, agreed. The proposed translation correctly states that paid memberships are unavailable:

"Sorry, no paid plans are available.": "ბოდიში, ფასიანი აბონემენტები არა გვაქვს"

The current text uses უფასო, which means “free,” so it changes the source meaning.

You are interacting with an AI system.

"Your account": "თქვენი ანგარიში",
"Your email": "თქვენი ელ.ფოსტა",
"Your email has failed to resubscribe, please try again": "ხელახალი გამოწერისას, თქვენი ელ.ფოსტის მისამერთი ვერ დარეგისტრირდა, გთხოვთ გაიმეოროთ.",
"Your gift is ready": "Votre abonnement offert est prêt",

@coderabbitai coderabbitai Bot Aug 5, 2026

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Replace the non-Georgian translation.

Your gift is ready still has the French value Votre abonnement offert est prêt. This breaks Georgian locale coverage for this message.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/i18n/locales/ge/portal.json` at line 290, Update the `Your gift is
ready` entry in the Georgian locale so its value is a Georgian translation
rather than the existing French text, while preserving the key and JSON
structure.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Correct translation: "თქვენი საჩუქარი მზადაა"

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.

@JDGit-lab, thank you. The correct value is:

"Your gift is ready": "თქვენი საჩუქარი მზადაა"

✏️ Learnings added
Learnt from: JDGit-lab
Repo: TryGhost/Ghost PR: 29777
File: packages/i18n/locales/ge/portal.json:290-290
Timestamp: 2026-08-06T15:48:36.289Z
Learning: In `packages/i18n/locales/ge/portal.json`, the Georgian translation for the Portal key `Your gift is ready` is `თქვენი საჩუქარი მზადაა`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

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