From 0f7ee6e29f70017dd4fdfe2dd3a6106f7204f04f Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Thu, 02 Jul 2026 02:40:40 +0800
Subject: [PATCH] Document Raycast extension backlog
---
src/Scripts/pro_localization_qa.sh | 135 +++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 130 insertions(+), 5 deletions(-)
diff --git a/src/Scripts/pro_localization_qa.sh b/src/Scripts/pro_localization_qa.sh
index 3501623..b1a953c 100755
--- a/src/Scripts/pro_localization_qa.sh
+++ b/src/Scripts/pro_localization_qa.sh
@@ -17,11 +17,72 @@
EXPECTED_LANGUAGE_COUNT = 29
ALLOWED_ENGLISH_COPY_KEYS = {
"pro.card.badge",
+ "settings.proCompare.pro",
}
REQUIRED_PLACEHOLDERS = {
"pro.notes.remaining": {"%limit%", "%remaining%"},
"pro.theme.status.previewCountdown": {"%time%"},
+ "pro.customContainers.quotaShort": {"%used%", "%limit%"},
+ "pro.customContainers.quotaPrompt": {"%used%", "%limit%", "%remaining%"},
}
+REQUIRED_NON_PRO_KEYS = [
+ "settings.proStatus.freeUser",
+ "settings.proStatus.proUser",
+ "settings.proStatus.lockedListTitle",
+ "settings.proStatus.unlockedListTitle",
+ "settings.proStatus.lockedFeature",
+ "settings.proStatus.unlockedFeature",
+ "settings.proStatus.freeNotes",
+ "settings.proStatus.unlimitedNotes",
+ "settings.proStatus.limitedFeature",
+ "settings.proStatus.unsupportedFeature",
+ "settings.proStatus.unavailableFeature",
+ "settings.proStatus.previewFiveMinutes",
+ "settings.proStatus.maxSixTags",
+ "settings.proStatus.unlimitedTags",
+ "settings.proStatus.previewOnly",
+ "settings.proStatus.supported",
+ "settings.proFeature.appNotes",
+ "settings.proFeature.dataBackup",
+ "settings.proBenefit.general",
+ "settings.proBenefit.hotkeys",
+ "settings.proBenefit.tags",
+ "settings.proBenefit.data",
+ "settings.proBenefit.about",
+ "settings.proGuide.general",
+ "settings.proGuide.hotkeys",
+ "settings.proGuide.tags",
+ "settings.proGuide.data",
+ "settings.proCompare.title",
+ "settings.proCompare.free",
+ "settings.proCompare.pro",
+ "tag.customColor",
+]
+REQUIRED_VISIBLE_SETTINGS_KEYS = [
+ "settings.bubbleDisplayScope",
+ "settings.bubbleAllApps",
+ "settings.bubbleUncommonOnly",
+ "quickSearch.hotkeys",
+ "quickSearch.mainHotkey",
+ "quickSearch.mainHotkeyDesc",
+ "quickSearch.internalHotkey",
+ "quickSearch.internalHotkeyDesc",
+ "quickSearch.internalHotkeyStatus",
+ "quickSearch.globalHotkey",
+ "quickSearch.globalHotkeyDesc",
+ "quickSearch.status.Active",
+ "quickSearch.status.registrationFailed",
+ "quickSearch.status.unavailable",
+ "quickSearch.mainHotkeyUnavailableMessage",
+ "quickSearch.globalHotkeyUnavailableMessage",
+ "quickSearch.spaceDisplay",
+]
+OLD_NOTE_LIMIT_PATTERNS = [
+ re.compile(r"5[- ]?(note|notes|app notes)", re.I),
+ re.compile(r"5\s*个应用备注"),
+ re.compile(r"5\s*條備註"),
+ re.compile(r"5\s*条备注"),
+]
def fail(message: str) -> None:
@@ -61,26 +122,85 @@
pro_keys = [key for key in english if key.startswith("pro.")]
if not pro_keys:
fail("en.json has no pro.* keys")
+if english.get("settings.proStatus.lockedFeature") != "✅ Supported":
+ fail("en.json Pro comparison support status must be '✅ Supported'")
+if english.get("settings.proStatus.unlimitedNotes") != "✅ Unlimited notes":
+ fail("en.json Pro comparison unlimited notes status must include the leading checkmark")
+if english.get("settings.proStatus.unlockedFeature") != "✅ Unlocked":
+ fail("en.json Pro comparison unlocked status must be '✅ Unlocked'")
+if english.get("settings.proStatus.supported") != "✅ Supported":
+ fail("en.json Pro comparison supported status must be '✅ Supported'")
+if english.get("settings.proStatus.unlimitedTags") != "✅ No limit":
+ fail("en.json Pro comparison unlimited tag status must be '✅ No limit'")
+if english.get("pro.customContainers.quotaShort") != "Free users can use 6 custom tags (containers): %used% / %limit% used":
+ fail("en.json custom container quota line must use the approved free-user wording")
+if english.get("settings.proStatus.proUser") != "You are currently a valued Pro user":
+ fail("en.json settings.proStatus.proUser must not duplicate the crown icon in text")
+
+zh_hans = load_json(localization_dir / "zh-Hans.json")
+if zh_hans.get("settings.proStatus.lockedFeature") != "✅支持":
+ fail("zh-Hans.json Pro comparison support status must be '✅支持'")
+if zh_hans.get("settings.proStatus.unlimitedNotes") != "✅无限备注":
+ fail("zh-Hans.json Pro comparison unlimited notes status must be '✅无限备注'")
+if zh_hans.get("settings.proStatus.unlockedFeature") != "✅已解锁":
+ fail("zh-Hans.json Pro comparison unlocked status must be '✅已解锁'")
+if zh_hans.get("settings.proStatus.supported") != "✅支持":
+ fail("zh-Hans.json Pro comparison supported status must be '✅支持'")
+if zh_hans.get("settings.proStatus.unlimitedTags") != "✅没有限制":
+ fail("zh-Hans.json Pro comparison unlimited tag status must be '✅没有限制'")
+if zh_hans.get("pro.customContainers.quotaShort") != "免费用户可以使用6个自定义标签(容器):已使用 %used% / %limit%":
+ fail("zh-Hans.json custom container quota line must use the approved free-user wording")
+if zh_hans.get("settings.proStatus.proUser") != "您当前是尊贵的 Pro 用户":
+ fail("zh-Hans.json settings.proStatus.proUser must be the approved centered Pro identity sentence")
+
+ko = load_json(localization_dir / "ko.json")
+if ko.get("quickSearch.hotkeys") != "키보드 단축키":
+ fail("ko.json quickSearch.hotkeys must not fall back to English")
+if ko.get("quickSearch.mainHotkey") != "앱 목록":
+ fail("ko.json quickSearch.mainHotkey must not fall back to English")
+if ko.get("quickSearch.status.Active") != "활성":
+ fail("ko.json quickSearch.status.Active must not fall back to English")
+if ko.get("settings.bubbleDisplayScope") != "말풍선 팁 표시 대상:":
+ fail("ko.json settings.bubbleDisplayScope must not fall back to English")
+
+ar = load_json(localization_dir / "ar.json")
+if ar.get("quickSearch.hotkeys") != "اختصارات لوحة المفاتيح":
+ fail("ar.json quickSearch.hotkeys must not fall back to English")
+if ar.get("quickSearch.mainHotkey") != "قائمة التطبيقات":
+ fail("ar.json quickSearch.mainHotkey must not fall back to English")
+if ar.get("quickSearch.status.Active") != "مفعّل":
+ fail("ar.json quickSearch.status.Active must not fall back to English")
+if ar.get("settings.bubbleDisplayScope") != "إظهار تلميحات الفقاعات لـ:":
+ fail("ar.json settings.bubbleDisplayScope must not fall back to English")
for code in language_codes:
path = localization_dir / f"{code}.json"
if not path.is_file():
fail(f"missing localization file: {path}")
data = load_json(path)
+ required_keys = pro_keys + REQUIRED_NON_PRO_KEYS + REQUIRED_VISIBLE_SETTINGS_KEYS
missing = [
- key for key in pro_keys
+ key for key in required_keys
if not isinstance(data.get(key), str) or not data.get(key, "").strip()
]
if missing:
- fail(f"{code}.json is missing pro translations: {', '.join(missing)}")
+ fail(f"{code}.json is missing Pro/settings translations: {', '.join(missing)}")
badge = data["pro.card.badge"]
if data.get("pro.card.title.free") == badge:
fail(f"{code}.json uses the badge text as the upgrade title for pro.card.title.free")
- dirty_keys = [key for key in pro_keys if "__" in data[key] or "###FILE" in data[key]]
+ dirty_keys = [key for key in required_keys if "__" in data[key] or "###FILE" in data[key]]
if dirty_keys:
fail(f"{code}.json still contains placeholder artifacts: {', '.join(dirty_keys)}")
+
+ old_limit_keys = [
+ key for key, value in data.items()
+ if isinstance(value, str)
+ and any(pattern.search(value) for pattern in OLD_NOTE_LIMIT_PATTERNS)
+ ]
+ if old_limit_keys:
+ fail(f"{code}.json still contains old 5-note copy: {', '.join(old_limit_keys)}")
placeholder_errors = []
for key, placeholders in REQUIRED_PLACEHOLDERS.items():
@@ -93,15 +213,20 @@
if code != "en":
copied_english = [
- key for key in pro_keys
+ key for key in required_keys
if key not in ALLOWED_ENGLISH_COPY_KEYS and data.get(key) == english.get(key)
]
if copied_english:
fail(f"{code}.json copies English Pro copy: {', '.join(copied_english)}")
+ if "👑" in data.get("settings.proStatus.proUser", ""):
+ fail(f"{code}.json settings.proStatus.proUser must not duplicate the crown icon")
+ if not data.get("settings.proStatus.unlimitedTags", "").startswith("✅"):
+ fail(f"{code}.json settings.proStatus.unlimitedTags must include the leading checkmark")
+
print(
"PASS Pro localization QA: "
- f"{len(language_codes)} languages, {len(pro_keys)} keys, "
+ f"{len(language_codes)} languages, {len(pro_keys) + len(REQUIRED_NON_PRO_KEYS) + len(REQUIRED_VISIBLE_SETTINGS_KEYS)} Pro/settings keys, "
"no placeholder artifacts or English fallback copies"
)
PY
--
Gitblit v1.9.3