mirror of
https://github.com/Minidoracat/mcp-feedback-enhanced.git
synced 2025-07-27 02:22:26 +08:00
203 lines
6.2 KiB
Python
203 lines
6.2 KiB
Python
![]() |
#!/usr/bin/env python3
|
|||
|
"""
|
|||
|
訊息代碼驗證腳本
|
|||
|
|
|||
|
驗證後端訊息代碼、前端常量和翻譯文件的一致性。
|
|||
|
確保所有訊息代碼都有對應的定義和翻譯。
|
|||
|
|
|||
|
使用方式:
|
|||
|
python scripts/validate_message_codes.py
|
|||
|
"""
|
|||
|
|
|||
|
import json
|
|||
|
import re
|
|||
|
import sys
|
|||
|
from pathlib import Path
|
|||
|
|
|||
|
|
|||
|
def extract_backend_codes():
|
|||
|
"""從後端 Python 文件中提取所有訊息代碼"""
|
|||
|
codes = set()
|
|||
|
|
|||
|
# 讀取 MessageCodes 類別
|
|||
|
message_codes_file = Path(
|
|||
|
"src/mcp_feedback_enhanced/web/constants/message_codes.py"
|
|||
|
)
|
|||
|
if message_codes_file.exists():
|
|||
|
content = message_codes_file.read_text(encoding="utf-8")
|
|||
|
# 匹配形如 SESSION_FEEDBACK_SUBMITTED = "session.feedbackSubmitted"
|
|||
|
pattern = r'([A-Z_]+)\s*=\s*"([^"]+)"'
|
|||
|
matches = re.findall(pattern, content)
|
|||
|
for constant_name, code in matches:
|
|||
|
codes.add(code)
|
|||
|
|
|||
|
return codes
|
|||
|
|
|||
|
|
|||
|
def extract_frontend_codes():
|
|||
|
"""從前端 JavaScript 文件中提取所有訊息代碼"""
|
|||
|
codes = set()
|
|||
|
|
|||
|
# 讀取 message-codes.js
|
|||
|
message_codes_js = Path(
|
|||
|
"src/mcp_feedback_enhanced/web/static/js/modules/constants/message-codes.js"
|
|||
|
)
|
|||
|
if message_codes_js.exists():
|
|||
|
content = message_codes_js.read_text(encoding="utf-8")
|
|||
|
# 匹配形如 FEEDBACK_SUBMITTED: 'session.feedbackSubmitted'
|
|||
|
pattern = r'[A-Z_]+:\s*[\'"]([^\'"]+)[\'"]'
|
|||
|
matches = re.findall(pattern, content)
|
|||
|
codes.update(matches)
|
|||
|
|
|||
|
# 讀取 utils.js 中的 fallback 訊息
|
|||
|
utils_js = Path("src/mcp_feedback_enhanced/web/static/js/modules/utils.js")
|
|||
|
if utils_js.exists():
|
|||
|
content = utils_js.read_text(encoding="utf-8")
|
|||
|
# 匹配 fallbackMessages 物件中的 key
|
|||
|
fallback_section = re.search(
|
|||
|
r"fallbackMessages\s*=\s*\{([^}]+)\}", content, re.DOTALL
|
|||
|
)
|
|||
|
if fallback_section:
|
|||
|
pattern = r'[\'"]([^\'"]+)[\'"]:\s*[\'"][^\'"]+[\'"]'
|
|||
|
matches = re.findall(pattern, fallback_section.group(1))
|
|||
|
codes.update(matches)
|
|||
|
|
|||
|
return codes
|
|||
|
|
|||
|
|
|||
|
def extract_translation_keys(locale="zh-TW"):
|
|||
|
"""從翻譯文件中提取所有 key"""
|
|||
|
keys = set()
|
|||
|
|
|||
|
translation_file = Path(
|
|||
|
f"src/mcp_feedback_enhanced/web/locales/{locale}/translation.json"
|
|||
|
)
|
|||
|
if translation_file.exists():
|
|||
|
try:
|
|||
|
data = json.loads(translation_file.read_text(encoding="utf-8"))
|
|||
|
|
|||
|
def extract_keys_recursive(obj, prefix=""):
|
|||
|
"""遞迴提取所有 key"""
|
|||
|
if isinstance(obj, dict):
|
|||
|
for key, value in obj.items():
|
|||
|
full_key = f"{prefix}.{key}" if prefix else key
|
|||
|
if isinstance(value, dict):
|
|||
|
extract_keys_recursive(value, full_key)
|
|||
|
else:
|
|||
|
keys.add(full_key)
|
|||
|
|
|||
|
extract_keys_recursive(data)
|
|||
|
except json.JSONDecodeError as e:
|
|||
|
print(f"❌ 無法解析翻譯文件 {translation_file}: {e}")
|
|||
|
|
|||
|
return keys
|
|||
|
|
|||
|
|
|||
|
def validate_message_codes():
|
|||
|
"""執行驗證"""
|
|||
|
print("🔍 開始驗證訊息代碼一致性...\n")
|
|||
|
|
|||
|
# 提取所有代碼
|
|||
|
backend_codes = extract_backend_codes()
|
|||
|
frontend_codes = extract_frontend_codes()
|
|||
|
|
|||
|
# 提取所有語言的翻譯 key
|
|||
|
locales = ["zh-TW", "en", "zh-CN"]
|
|||
|
translation_keys = {}
|
|||
|
for locale in locales:
|
|||
|
translation_keys[locale] = extract_translation_keys(locale)
|
|||
|
|
|||
|
# 統計資訊
|
|||
|
print("📊 統計資訊:")
|
|||
|
print(f" - 後端訊息代碼數量: {len(backend_codes)}")
|
|||
|
print(f" - 前端訊息代碼數量: {len(frontend_codes)}")
|
|||
|
for locale in locales:
|
|||
|
print(f" - {locale} 翻譯 key 數量: {len(translation_keys[locale])}")
|
|||
|
print()
|
|||
|
|
|||
|
# 驗證後端代碼是否都有前端定義
|
|||
|
print("🔍 檢查後端代碼是否都有前端定義...")
|
|||
|
missing_in_frontend = backend_codes - frontend_codes
|
|||
|
if missing_in_frontend:
|
|||
|
print("❌ 以下後端代碼在前端沒有定義:")
|
|||
|
for code in sorted(missing_in_frontend):
|
|||
|
print(f" - {code}")
|
|||
|
else:
|
|||
|
print("✅ 所有後端代碼都有前端定義")
|
|||
|
print()
|
|||
|
|
|||
|
# 驗證前端代碼是否都有翻譯
|
|||
|
print("🔍 檢查前端代碼是否都有翻譯...")
|
|||
|
all_frontend_codes = backend_codes | frontend_codes
|
|||
|
|
|||
|
for locale in locales:
|
|||
|
print(f"\n 檢查 {locale} 翻譯:")
|
|||
|
missing_translations = set()
|
|||
|
|
|||
|
for code in all_frontend_codes:
|
|||
|
if code not in translation_keys[locale]:
|
|||
|
missing_translations.add(code)
|
|||
|
|
|||
|
if missing_translations:
|
|||
|
print(" ❌ 缺少以下翻譯:")
|
|||
|
for code in sorted(missing_translations):
|
|||
|
print(f" - {code}")
|
|||
|
else:
|
|||
|
print(" ✅ 所有代碼都有翻譯")
|
|||
|
|
|||
|
# 檢查是否有多餘的翻譯
|
|||
|
print("\n🔍 檢查是否有多餘的翻譯...")
|
|||
|
for locale in locales:
|
|||
|
# 過濾掉非訊息代碼的 key(如 buttons, labels 等)
|
|||
|
message_keys = {
|
|||
|
k
|
|||
|
for k in translation_keys[locale]
|
|||
|
if any(
|
|||
|
k.startswith(prefix)
|
|||
|
for prefix in [
|
|||
|
"system.",
|
|||
|
"session.",
|
|||
|
"settings.",
|
|||
|
"error.",
|
|||
|
"command.",
|
|||
|
"file.",
|
|||
|
"prompt.",
|
|||
|
"notification.",
|
|||
|
]
|
|||
|
)
|
|||
|
}
|
|||
|
|
|||
|
extra_translations = message_keys - all_frontend_codes
|
|||
|
if extra_translations:
|
|||
|
print(f"\n {locale} 有多餘的翻譯:")
|
|||
|
for key in sorted(extra_translations):
|
|||
|
print(f" - {key}")
|
|||
|
|
|||
|
print("\n✅ 驗證完成!")
|
|||
|
|
|||
|
# 返回是否有錯誤
|
|||
|
return len(missing_in_frontend) == 0 and all(
|
|||
|
len(
|
|||
|
[
|
|||
|
code
|
|||
|
for code in all_frontend_codes
|
|||
|
if code not in translation_keys[locale]
|
|||
|
]
|
|||
|
)
|
|||
|
== 0
|
|||
|
for locale in locales
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
if __name__ == "__main__":
|
|||
|
# 切換到專案根目錄
|
|||
|
script_dir = Path(__file__).parent
|
|||
|
project_root = script_dir.parent
|
|||
|
import os
|
|||
|
|
|||
|
os.chdir(project_root)
|
|||
|
|
|||
|
# 執行驗證
|
|||
|
success = validate_message_codes()
|
|||
|
sys.exit(0 if success else 1)
|