Community edition generated from private repo via sync pipeline. Includes 9 plugins (WordPress, WooCommerce, WP Advanced, Gitea, n8n, Supabase, OpenPanel, Appwrite, Directus) with ~587 tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
195 lines
7.6 KiB
Python
195 lines
7.6 KiB
Python
"""
|
|
Internationalization (i18n) utilities for OAuth Authorization pages.
|
|
|
|
Supports English (en) and Persian/Farsi (fa) languages.
|
|
Language is auto-detected from Accept-Language header or query parameter.
|
|
"""
|
|
|
|
# Translation dictionary for OAuth authorization pages
|
|
TRANSLATIONS: dict[str, dict[str, str]] = {
|
|
"en": {
|
|
# Page title
|
|
"page_title": "Authorization Required - MCP Hub",
|
|
# Header section
|
|
"auth_required": "Authorization Required",
|
|
"wants_access": "{client_name} wants to access your MCP Hub",
|
|
# Client information
|
|
"client_info": "Client Information",
|
|
"client_id_label": "Client ID:",
|
|
"redirect_uri_label": "Redirect URI:",
|
|
# Permissions section
|
|
"requested_permissions": "Requested Permissions:",
|
|
"no_permissions": "No specific permissions requested",
|
|
# Form section
|
|
"enter_api_key": "Enter your API Key to authorize",
|
|
"api_key_placeholder": "cmp_xxxxxxxxxxxxxxxx",
|
|
"api_key_note": "Your API key will be validated and used to grant permissions to this application.",
|
|
# Buttons
|
|
"approve": "Authorize",
|
|
"deny": "Deny",
|
|
# Security notice
|
|
"security_note": "Security Note:",
|
|
"security_message": "Only authorize applications you trust. The OAuth token will inherit the permissions from your API key.",
|
|
# Error page
|
|
"error_title": "Authorization Error - MCP Hub",
|
|
"auth_error": "Authorization Error",
|
|
"unable_to_complete": "Unable to complete the authorization request",
|
|
"error_code": "Error Code:",
|
|
"error_description": "Description:",
|
|
"common_solutions": "Common Solutions:",
|
|
"solution_1": "Verify your API key is correct and active",
|
|
"solution_2": "Check that the client application is properly configured",
|
|
"solution_3": "Ensure the redirect URI matches the registered URI",
|
|
"return_to_app": "Return to Application",
|
|
"close_window": "Close Window",
|
|
"need_help": "Need help? Check the",
|
|
"documentation": "documentation",
|
|
# Footer
|
|
"secured_with": "This connection is secured with OAuth 2.1 + PKCE",
|
|
},
|
|
"fa": {
|
|
# Page title
|
|
"page_title": "احراز هویت مورد نیاز - MCP Hub",
|
|
# Header section
|
|
"auth_required": "احراز هویت مورد نیاز",
|
|
"wants_access": "{client_name} میخواهد به MCP Hub شما دسترسی داشته باشد",
|
|
# Client information
|
|
"client_info": "اطلاعات برنامه",
|
|
"client_id_label": "شناسه برنامه:",
|
|
"redirect_uri_label": "آدرس بازگشت:",
|
|
# Permissions section
|
|
"requested_permissions": "دسترسیهای درخواستی:",
|
|
"no_permissions": "دسترسی خاصی درخواست نشده",
|
|
# Form section
|
|
"enter_api_key": "API Key خود را برای احراز هویت وارد کنید",
|
|
"api_key_placeholder": "cmp_xxxxxxxxxxxxxxxx",
|
|
"api_key_note": "API Key شما اعتبارسنجی شده و برای اعطای دسترسی به این برنامه استفاده خواهد شد.",
|
|
# Buttons
|
|
"approve": "تایید",
|
|
"deny": "رد",
|
|
# Security notice
|
|
"security_note": "نکته امنیتی:",
|
|
"security_message": "فقط برنامههایی را که به آنها اعتماد دارید تایید کنید. توکن OAuth دسترسیهای API Key شما را به ارث خواهد برد.",
|
|
# Error page
|
|
"error_title": "خطای احراز هویت - MCP Hub",
|
|
"auth_error": "خطای احراز هویت",
|
|
"unable_to_complete": "امکان تکمیل درخواست احراز هویت وجود ندارد",
|
|
"error_code": "کد خطا:",
|
|
"error_description": "توضیحات:",
|
|
"common_solutions": "راهحلهای رایج:",
|
|
"solution_1": "اطمینان حاصل کنید که API Key شما صحیح و فعال است",
|
|
"solution_2": "بررسی کنید که برنامه به درستی پیکربندی شده است",
|
|
"solution_3": "اطمینان حاصل کنید که آدرس بازگشت با آدرس ثبتشده مطابقت دارد",
|
|
"return_to_app": "بازگشت به برنامه",
|
|
"close_window": "بستن پنجره",
|
|
"need_help": "نیاز به کمک دارید؟",
|
|
"documentation": "مستندات",
|
|
# Footer
|
|
"secured_with": "این اتصال با OAuth 2.1 + PKCE امن شده است",
|
|
},
|
|
}
|
|
|
|
def detect_language(accept_language: str | None = None, query_lang: str | None = None) -> str:
|
|
"""
|
|
Detect user's preferred language from Accept-Language header or query parameter.
|
|
|
|
Args:
|
|
accept_language: Accept-Language header value (e.g., "en-US,en;q=0.9,fa;q=0.8")
|
|
query_lang: Explicit language parameter from query string (takes priority)
|
|
|
|
Returns:
|
|
Language code: "en" or "fa"
|
|
"""
|
|
# Priority 1: Explicit query parameter
|
|
if query_lang:
|
|
lang = query_lang.lower().strip()
|
|
if lang in ["fa", "persian", "farsi"]:
|
|
return "fa"
|
|
if lang in ["en", "english"]:
|
|
return "en"
|
|
|
|
# Priority 2: Accept-Language header
|
|
if accept_language:
|
|
# Parse Accept-Language header
|
|
# Format: "en-US,en;q=0.9,fa;q=0.8,fa-IR;q=0.7"
|
|
languages = []
|
|
for lang_entry in accept_language.split(","):
|
|
# Extract language code (ignore quality value)
|
|
lang = lang_entry.split(";")[0].strip().lower()
|
|
languages.append(lang)
|
|
|
|
# Check for Persian/Farsi
|
|
for lang in languages:
|
|
if lang.startswith("fa"): # fa, fa-IR, fa-AF
|
|
return "fa"
|
|
|
|
# Check for English
|
|
for lang in languages:
|
|
if lang.startswith("en"): # en, en-US, en-GB
|
|
return "en"
|
|
|
|
# Default: English
|
|
return "en"
|
|
|
|
def get_translation(lang: str, key: str, **kwargs) -> str:
|
|
"""
|
|
Get translated string for the given language and key.
|
|
|
|
Args:
|
|
lang: Language code ("en" or "fa")
|
|
key: Translation key
|
|
**kwargs: Format arguments for string interpolation
|
|
|
|
Returns:
|
|
Translated string with format arguments applied
|
|
|
|
Example:
|
|
>>> get_translation("en", "wants_access", client_name="ChatGPT")
|
|
'ChatGPT wants to access your MCP Hub'
|
|
|
|
>>> get_translation("fa", "wants_access", client_name="ChatGPT")
|
|
'ChatGPT میخواهد به MCP Hub شما دسترسی داشته باشد'
|
|
"""
|
|
# Get language dictionary (fallback to English)
|
|
lang_dict = TRANSLATIONS.get(lang, TRANSLATIONS["en"])
|
|
|
|
# Get translation (fallback to key itself)
|
|
text = lang_dict.get(key, key)
|
|
|
|
# Apply format arguments if provided
|
|
if kwargs:
|
|
try:
|
|
return text.format(**kwargs)
|
|
except (KeyError, ValueError):
|
|
# If formatting fails, return raw text
|
|
return text
|
|
|
|
return text
|
|
|
|
def get_all_translations(lang: str) -> dict[str, str]:
|
|
"""
|
|
Get all translations for a language as a dictionary.
|
|
|
|
Useful for passing to templates.
|
|
|
|
Args:
|
|
lang: Language code ("en" or "fa")
|
|
|
|
Returns:
|
|
Dictionary of all translations for the language
|
|
"""
|
|
return TRANSLATIONS.get(lang, TRANSLATIONS["en"])
|
|
|
|
def get_language_name(lang: str) -> str:
|
|
"""
|
|
Get human-readable language name.
|
|
|
|
Args:
|
|
lang: Language code
|
|
|
|
Returns:
|
|
Language name in its native form
|
|
"""
|
|
names = {"en": "English", "fa": "فارسی"}
|
|
return names.get(lang, "English")
|