Skip to main content
Glama
index.ts6.55 kB
/** * i18n Manager * * Provides internationalization support with: * - Multiple language packs (en, zh-CN, etc.) * - Dynamic locale switching * - Parameter interpolation in messages * - Language preference persistence */ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import { I18nMessages, SupportedLocale, TranslationFunction } from './types'; import { en } from './locales/en'; import { zhCN } from './locales/zh-CN'; /** * Available language packs * Note: Not all locales from SupportedLocale are implemented yet. * Use Partial to allow gradual locale additions. */ const locales: Partial<Record<SupportedLocale, I18nMessages>> = { 'en': en, 'zh-CN': zhCN, // TODO: Add more languages // 'zh-TW': zhTW, // 'ja': ja, // 'ko': ko, }; /** * Global config path */ const GLOBAL_CONFIG_DIR = path.join(os.homedir(), '.gitea-mcp'); const GLOBAL_CONFIG_FILE = path.join(GLOBAL_CONFIG_DIR, 'config.json'); /** * I18n Manager Class */ class I18n { private currentLocale: SupportedLocale = 'en'; // Default: English private messages: I18nMessages = en; /** * Initialize i18n system * - Loads language preference from global config * - Falls back to English if preference not found */ async init(): Promise<void> { try { // Load global config to get saved language preference if (fs.existsSync(GLOBAL_CONFIG_FILE)) { const configContent = fs.readFileSync(GLOBAL_CONFIG_FILE, 'utf-8'); const globalConfig = JSON.parse(configContent); const savedLanguage = globalConfig?.settings?.language; if (savedLanguage && locales[savedLanguage as SupportedLocale]) { this.setLocale(savedLanguage as SupportedLocale); } } } catch (error) { // If error reading config, just use default (English) console.warn('Failed to load language preference, using default (English)'); } } /** * Get current locale */ getLocale(): SupportedLocale { return this.currentLocale; } /** * Set locale and switch language pack * @param locale - Target locale * @returns true if successful */ setLocale(locale: SupportedLocale): boolean { if (!locales[locale]) { console.error(`Locale "${locale}" not found, keeping current locale: ${this.currentLocale}`); return false; } this.currentLocale = locale; this.messages = locales[locale]; return true; } /** * Save language preference to global config * @param locale - Locale to save */ async saveLanguagePreference(locale: SupportedLocale): Promise<void> { try { // Ensure directory exists if (!fs.existsSync(GLOBAL_CONFIG_DIR)) { fs.mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true }); } // Load or create global config let globalConfig: any = { version: '1.0', giteaServers: [], recentProjects: [], settings: { language: 'en', autoSave: true, autoDetectFromGit: true, }, }; if (fs.existsSync(GLOBAL_CONFIG_FILE)) { const content = fs.readFileSync(GLOBAL_CONFIG_FILE, 'utf-8'); globalConfig = JSON.parse(content); } // Update language setting if (!globalConfig.settings) { globalConfig.settings = {}; } globalConfig.settings.language = locale; // Save to file fs.writeFileSync( GLOBAL_CONFIG_FILE, JSON.stringify(globalConfig, null, 2), 'utf-8' ); } catch (error) { throw new Error(`Failed to save language preference: ${error}`); } } /** * Get list of supported locales */ getSupportedLocales(): SupportedLocale[] { return Object.keys(locales) as SupportedLocale[]; } /** * Get localized language name * @param locale - Locale to get name for * @returns Localized language name */ getLanguageName(locale: SupportedLocale): string { return this.messages.language.names[locale] || locale; } /** * Translate a message key with parameter interpolation * @param key - Message key in dot notation (e.g., 'init.title') * @param params - Parameters for interpolation (e.g., {url: 'https://...'}) * @returns Translated message with parameters replaced */ t(key: string, params?: Record<string, string | number>): string { // Navigate the nested message structure using dot notation const keys = key.split('.'); let message: any = this.messages; for (const k of keys) { if (message && typeof message === 'object' && k in message) { message = message[k]; } else { // Key not found, return the key itself as fallback console.warn(`Translation key not found: ${key}`); return key; } } // Ensure we got a string if (typeof message !== 'string') { console.warn(`Translation key "${key}" does not resolve to a string`); return key; } // Replace parameters in the message if (params) { return this.interpolate(message, params); } return message; } /** * Replace parameters in a message string * @param message - Message template with {param} placeholders * @param params - Parameter values * @returns Message with parameters replaced */ private interpolate(message: string, params: Record<string, string | number>): string { return message.replace(/\{(\w+)\}/g, (match, key) => { if (key in params) { return String(params[key]); } return match; // Keep placeholder if parameter not found }); } } /** * Singleton instance */ export const i18n = new I18n(); /** * Helper: Translate function */ export const t: TranslationFunction = (key, params) => i18n.t(key, params); /** * Helper: Get current locale */ export const getLocale = () => i18n.getLocale(); /** * Helper: Set locale */ export const setLocale = (locale: SupportedLocale) => i18n.setLocale(locale); /** * Helper: Save language preference */ export const saveLanguagePreference = async (locale: SupportedLocale) => { await i18n.saveLanguagePreference(locale); }; /** * Helper: Get supported locales */ export const getSupportedLocales = () => i18n.getSupportedLocales(); /** * Helper: Get language name */ export const getLanguageName = (locale: SupportedLocale) => i18n.getLanguageName(locale); /** * Initialize i18n system * Should be called at application startup */ export const initI18n = async () => { await i18n.init(); };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SupenBysz/gitea-mcp-tool'

If you have feedback or need assistance with the MCP directory API, please join our Discord server