Skip to main content
Glama

Weblate MCP Server

by mmntm
statistics.tool.ts15.5 kB
import { Injectable, Logger } from '@nestjs/common'; import { Tool } from '@rekog/mcp-nest'; import { z } from 'zod'; import { WeblateApiService } from '../services'; import { WeblateStatisticsService } from '../services/weblate/statistics.service'; @Injectable() export class WeblateStatisticsTool { private readonly logger = new Logger(WeblateStatisticsTool.name); constructor( private apiService: WeblateApiService, private statisticsService: WeblateStatisticsService, ) {} @Tool({ name: 'getProjectStatistics', description: 'Get comprehensive statistics for a project including completion rates and string counts', parameters: z.object({ projectSlug: z.string().describe('The slug of the project'), }), }) async getProjectStatistics({ projectSlug }: { projectSlug: string }) { try { const stats = await this.statisticsService.getProjectStatistics(projectSlug); return { content: [ { type: 'text', text: this.formatProjectStatistics(projectSlug, stats), }, ], }; } catch (error) { this.logger.error(`Failed to get project statistics for ${projectSlug}`, error); return { content: [ { type: 'text', text: `Error getting project statistics: ${error.message}`, }, ], isError: true, }; } } @Tool({ name: 'getComponentStatistics', description: 'Get detailed statistics for a specific component', parameters: z.object({ projectSlug: z.string().describe('The slug of the project'), componentSlug: z.string().describe('The slug of the component'), }), }) async getComponentStatistics({ projectSlug, componentSlug, }: { projectSlug: string; componentSlug: string; }) { try { const stats = await this.statisticsService.getComponentStatistics(projectSlug, componentSlug); return { content: [ { type: 'text', text: this.formatComponentStatistics(projectSlug, componentSlug, stats), }, ], }; } catch (error) { this.logger.error(`Failed to get component statistics for ${projectSlug}/${componentSlug}`, error); return { content: [ { type: 'text', text: `Error getting component statistics: ${error.message}`, }, ], isError: true, }; } } @Tool({ name: 'getProjectDashboard', description: 'Get a comprehensive dashboard overview for a project with all component statistics', parameters: z.object({ projectSlug: z.string().describe('The slug of the project'), }), }) async getProjectDashboard({ projectSlug }: { projectSlug: string }) { try { const dashboard = await this.statisticsService.getProjectDashboard(projectSlug); return { content: [ { type: 'text', text: this.formatProjectDashboard(projectSlug, dashboard), }, ], }; } catch (error) { this.logger.error(`Failed to get project dashboard for ${projectSlug}`, error); return { content: [ { type: 'text', text: `Error getting project dashboard: ${error.message}`, }, ], isError: true, }; } } @Tool({ name: 'getTranslationStatistics', description: 'Get statistics for a specific translation (project/component/language combination)', parameters: z.object({ projectSlug: z.string().describe('The slug of the project'), componentSlug: z.string().describe('The slug of the component'), languageCode: z.string().describe('The language code (e.g., en, es, fr)'), }), }) async getTranslationStatistics({ projectSlug, componentSlug, languageCode, }: { projectSlug: string; componentSlug: string; languageCode: string; }) { try { const stats = await this.statisticsService.getTranslationStatistics( projectSlug, componentSlug, languageCode, ); return { content: [ { type: 'text', text: this.formatTranslationStatistics(projectSlug, componentSlug, languageCode, stats), }, ], }; } catch (error) { this.logger.error( `Failed to get translation statistics for ${projectSlug}/${componentSlug}/${languageCode}`, error, ); return { content: [ { type: 'text', text: `Error getting translation statistics: ${error.message}`, }, ], isError: true, }; } } @Tool({ name: 'getComponentLanguageProgress', description: 'Get translation progress for all languages in a component', parameters: z.object({ projectSlug: z.string().describe('The slug of the project'), componentSlug: z.string().describe('The slug of the component'), }), }) async getComponentLanguageProgress({ projectSlug, componentSlug, }: { projectSlug: string; componentSlug: string; }) { try { const progress = await this.statisticsService.getComponentLanguageProgress( projectSlug, componentSlug, ); return { content: [ { type: 'text', text: this.formatComponentLanguageProgress(projectSlug, componentSlug, progress), }, ], }; } catch (error) { this.logger.error( `Failed to get component language progress for ${projectSlug}/${componentSlug}`, error, ); return { content: [ { type: 'text', text: `Error getting component language progress: ${error.message}`, }, ], isError: true, }; } } @Tool({ name: 'getLanguageStatistics', description: 'Get statistics for a specific language across all projects', parameters: z.object({ languageCode: z.string().describe('The language code (e.g., en, es, fr)'), }), }) async getLanguageStatistics({ languageCode }: { languageCode: string }) { try { const stats = await this.statisticsService.getLanguageStatistics(languageCode); return { content: [ { type: 'text', text: this.formatLanguageStatistics(languageCode, stats), }, ], }; } catch (error) { this.logger.error(`Failed to get language statistics for ${languageCode}`, error); return { content: [ { type: 'text', text: `Error getting language statistics: ${error.message}`, }, ], isError: true, }; } } @Tool({ name: 'getUserStatistics', description: 'Get contribution statistics for a specific user', parameters: z.object({ username: z.string().describe('The username to get statistics for'), }), }) async getUserStatistics({ username }: { username: string }) { try { const stats = await this.statisticsService.getUserStatistics(username); return { content: [ { type: 'text', text: this.formatUserStatistics(username, stats), }, ], }; } catch (error) { this.logger.error(`Failed to get user statistics for ${username}`, error); return { content: [ { type: 'text', text: `Error getting user statistics: ${error.message}`, }, ], isError: true, }; } } private formatProjectStatistics(projectSlug: string, stats: any): string { const getStatValue = (key: string, defaultValue = 'N/A') => { return stats?.[key] !== undefined ? stats[key] : defaultValue; }; const formatPercent = (value: any) => { return typeof value === 'number' ? `${value.toFixed(1)}%` : 'N/A'; }; return `## 📊 Project Statistics: ${stats?.name || projectSlug} **Overall Progress:** - 🎯 Translation Progress: ${formatPercent(getStatValue('translated_percent'))} - ✅ Approved: ${formatPercent(getStatValue('approved_percent'))} - 🔍 Needs Review: ${formatPercent(getStatValue('readonly_percent'))} - ❌ Untranslated: ${formatPercent(getStatValue('nottranslated_percent'))} **String Counts:** - 📝 Total Strings: ${getStatValue('total')} - ✅ Translated: ${getStatValue('translated')} - 🎯 Approved: ${getStatValue('approved')} - ❌ Untranslated: ${getStatValue('nottranslated')} - 🔍 Read-only: ${getStatValue('readonly')} **Project Details:** - 🌐 URL: ${stats?.web_url || 'N/A'} - 🔗 Repository: ${stats?.repository_url || 'N/A'}`; } private formatComponentStatistics(projectSlug: string, componentSlug: string, stats: any): string { const getStatValue = (key: string, defaultValue = 'N/A') => { return stats?.[key] !== undefined ? stats[key] : defaultValue; }; const formatPercent = (value: any) => { return typeof value === 'number' ? `${value.toFixed(1)}%` : 'N/A'; }; return `## 📊 Component Statistics: ${stats?.name || componentSlug} **Project:** ${projectSlug} **Component:** ${componentSlug} **Translation Progress:** - 🎯 Translated: ${formatPercent(getStatValue('translated_percent'))} - ✅ Approved: ${formatPercent(getStatValue('approved_percent'))} - 🔍 Needs Review: ${formatPercent(getStatValue('readonly_percent'))} - ❌ Untranslated: ${formatPercent(getStatValue('nottranslated_percent'))} **String Counts:** - 📝 Total: ${getStatValue('total')} - ✅ Translated: ${getStatValue('translated')} - 🎯 Approved: ${getStatValue('approved')} - ❌ Untranslated: ${getStatValue('nottranslated')} **Component Details:** - 🌐 URL: ${stats?.web_url || 'N/A'} - 📁 Source Language: ${stats?.source_language?.name || 'N/A'} (${stats?.source_language?.code || 'N/A'})`; } private formatTranslationStatistics( projectSlug: string, componentSlug: string, languageCode: string, stats: any, ): string { const getStatValue = (key: string, defaultValue = 'N/A') => { return stats?.[key] !== undefined ? stats[key] : defaultValue; }; const formatPercent = (value: any) => { return typeof value === 'number' ? `${value.toFixed(1)}%` : 'N/A'; }; return `## 📊 Translation Statistics **Translation:** ${projectSlug}/${componentSlug}/${languageCode} **Progress:** - 🎯 Translated: ${formatPercent(getStatValue('translated_percent'))} - ✅ Approved: ${formatPercent(getStatValue('approved_percent'))} - 🔍 Needs Review: ${formatPercent(getStatValue('readonly_percent'))} - ❌ Untranslated: ${formatPercent(getStatValue('nottranslated_percent'))} **String Details:** - 📝 Total Strings: ${getStatValue('total')} - ✅ Translated: ${getStatValue('translated')} - 🎯 Approved: ${getStatValue('approved')} - ❌ Untranslated: ${getStatValue('nottranslated')} - 🔍 Readonly: ${getStatValue('readonly')} **Quality Metrics:** - ⚠️ Failing Checks: ${getStatValue('failing_percent', '0')}% - 💡 Suggestions: ${getStatValue('suggestions')} - 💬 Comments: ${getStatValue('comments')}`; } private formatProjectDashboard(projectSlug: string, dashboard: any): string { const project = dashboard.project; const components = dashboard.components || []; let result = this.formatProjectStatistics(projectSlug, project); result += '\n\n## 📋 Component Breakdown\n\n'; components.forEach((comp: any, index: number) => { if (comp.statistics) { const stats = comp.statistics; const formatPercent = (value: any) => { return typeof value === 'number' ? `${value.toFixed(1)}%` : 'N/A'; }; result += `**${index + 1}. ${comp.component}** (${comp.slug}) - 🎯 Progress: ${formatPercent(stats.translated_percent)} - ✅ Approved: ${formatPercent(stats.approved_percent)} - 📝 Total Strings: ${stats.total || 'N/A'} `; } else { result += `**${index + 1}. ${comp.component}** (${comp.slug}) - ❌ Error: ${comp.error || 'Unable to load statistics'} `; } }); return result; } private formatComponentLanguageProgress( projectSlug: string, componentSlug: string, progress: any[], ): string { let result = `## 🌐 Language Progress: ${projectSlug}/${componentSlug}\n\n`; progress.forEach((lang: any, index: number) => { if (lang.statistics) { const stats = lang.statistics; const formatPercent = (value: any) => { return typeof value === 'number' ? `${value.toFixed(1)}%` : 'N/A'; }; const progressBar = this.generateProgressBar(stats.translated_percent || 0); result += `**${index + 1}. ${lang.language}** (${lang.code}) ${progressBar} ${formatPercent(stats.translated_percent)} - ✅ Approved: ${formatPercent(stats.approved_percent)} - 📝 Total: ${stats.total || 'N/A'} | Translated: ${stats.translated || 'N/A'} `; } else { result += `**${index + 1}. ${lang.language}** (${lang.code}) - ❌ Error: ${lang.error || 'Unable to load statistics'} `; } }); return result; } private formatLanguageStatistics(languageCode: string, stats: any): string { const getStatValue = (key: string, defaultValue = 'N/A') => { return stats?.[key] !== undefined ? stats[key] : defaultValue; }; const formatPercent = (value: any) => { return typeof value === 'number' ? `${value.toFixed(1)}%` : 'N/A'; }; return `## 🌐 Language Statistics: ${stats?.name || languageCode} **Language Details:** - 📛 Name: ${getStatValue('name')} - 🔤 Code: ${getStatValue('code')} - 📍 Direction: ${getStatValue('direction', 'ltr')} **Overall Progress:** - 🎯 Translated: ${formatPercent(getStatValue('translated_percent'))} - ✅ Approved: ${formatPercent(getStatValue('approved_percent'))} - ❌ Untranslated: ${formatPercent(getStatValue('nottranslated_percent'))} **String Counts:** - 📝 Total: ${getStatValue('total')} - ✅ Translated: ${getStatValue('translated')} - 🎯 Approved: ${getStatValue('approved')} - ❌ Untranslated: ${getStatValue('nottranslated')}`; } private formatUserStatistics(username: string, stats: any): string { const getStatValue = (key: string, defaultValue = 'N/A') => { return stats?.[key] !== undefined ? stats[key] : defaultValue; }; return `## 👤 User Statistics: ${stats?.full_name || username} **User Details:** - 👤 Username: ${getStatValue('username')} - 📧 Email: ${getStatValue('email')} - 📅 Joined: ${stats?.date_joined ? new Date(stats.date_joined).toLocaleDateString() : 'N/A'} **Contribution Stats:** - ✏️ Translations: ${getStatValue('translated')} - ✅ Approved: ${getStatValue('approved')} - 💡 Suggestions: ${getStatValue('suggestions')} - 💬 Comments: ${getStatValue('comments')} **Activity:** - 📈 Total Changes: ${getStatValue('total_changes')} - 🗓️ Last Activity: ${stats?.last_login ? new Date(stats.last_login).toLocaleDateString() : 'N/A'}`; } private generateProgressBar(percentage: number): string { const width = 20; const filled = Math.max(0, Math.min(width, Math.floor((percentage / 100) * width))); const empty = Math.max(0, width - filled); return `[${'█'.repeat(filled)}${'░'.repeat(empty)}]`; } }

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/mmntm/weblate-mcp'

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