Skip to main content
Glama
comments.formatter.ts9.78 kB
import type { Comment, CommentDeleted, PaginatedResult, } from "@lokalise/node-api"; import { formatBulletList, formatDate, formatEmptyState, formatFooter, formatHeading, formatTable, } from "../../shared/utils/formatter.util.js"; /** * Comments formatter functions for converting API responses to user-friendly Markdown. * Comments are attached to translation keys and allow team collaboration. */ /** * Format a list of comments for a specific key into Markdown. * @param commentsData - API response containing comments list * @param keyId - The key ID for context * @returns Formatted Markdown string */ export function formatKeyCommentsList( commentsData: PaginatedResult<Comment>, keyId: number, ): string { const lines: string[] = []; // Add main heading const totalCount = commentsData.totalResults || commentsData.items?.length || 0; lines.push(formatHeading(`Comments for Key #${keyId} (${totalCount})`, 1)); lines.push(""); if (!commentsData.items || commentsData.items.length === 0) { const suggestions = [ "Add a comment to start the discussion", "Comments help collaborate on translation decisions", "Use comments to provide context for translators", ]; lines.push(formatEmptyState("comments", `key #${keyId}`, suggestions)); lines.push(formatFooter("No comments found")); return lines.join("\n"); } // Add summary section lines.push(formatHeading("Summary", 2)); const summary: Record<string, unknown> = { "Total Comments": totalCount, "Current Page": commentsData.currentPage || 1, "Total Pages": commentsData.totalPages || 1, "Results Per Page": commentsData.resultsPerPage || commentsData.items.length, }; lines.push(formatBulletList(summary)); lines.push(""); // Add comments table lines.push(formatHeading("Comments", 2)); const tableData = commentsData.items.map((comment: Comment) => ({ id: comment.comment_id, author: comment.added_by_email || `User #${comment.added_by}`, comment: comment.comment.length > 50 ? `${comment.comment.substring(0, 50)}...` : comment.comment, added: formatDate(new Date(comment.added_at_timestamp * 1000)), })); const table = formatTable(tableData, [ { key: "id", header: "ID" }, { key: "author", header: "Author" }, { key: "comment", header: "Comment" }, { key: "added", header: "Added" }, ]); lines.push(table); lines.push(""); // Add pagination info if applicable if (commentsData.totalPages > 1) { lines.push(formatHeading("Pagination", 2)); lines.push( `Page ${commentsData.currentPage} of ${commentsData.totalPages}`, ); if (commentsData.currentPage < commentsData.totalPages) { lines.push(`- Next page: ${commentsData.currentPage + 1}`); } if (commentsData.currentPage > 1) { lines.push(`- Previous page: ${commentsData.currentPage - 1}`); } lines.push(""); } // Add footer lines.push(formatFooter("Comments retrieved")); return lines.join("\n"); } /** * Format all project comments into Markdown. * @param commentsData - API response containing all project comments * @returns Formatted Markdown string */ export function formatProjectCommentsList( commentsData: PaginatedResult<Comment>, ): string { const lines: string[] = []; // Add main heading const totalCount = commentsData.totalResults || commentsData.items?.length || 0; lines.push(formatHeading(`All Project Comments (${totalCount})`, 1)); lines.push(""); if (!commentsData.items || commentsData.items.length === 0) { const suggestions = [ "Start adding comments to translation keys", "Comments help track translation decisions", "Use comments for team collaboration", ]; lines.push(formatEmptyState("comments", "this project", suggestions)); lines.push(formatFooter("No comments found")); return lines.join("\n"); } // Add summary section lines.push(formatHeading("Summary", 2)); const summary: Record<string, unknown> = { "Total Comments": totalCount, "Current Page": commentsData.currentPage || 1, "Total Pages": commentsData.totalPages || 1, "Results Per Page": commentsData.resultsPerPage || commentsData.items.length, }; lines.push(formatBulletList(summary)); lines.push(""); // Group comments by key const commentsByKey = new Map<number, Comment[]>(); for (const comment of commentsData.items) { const keyComments = commentsByKey.get(comment.key_id) || []; keyComments.push(comment); commentsByKey.set(comment.key_id, keyComments); } // Add comments grouped by key lines.push(formatHeading("Comments by Key", 2)); for (const [keyId, comments] of commentsByKey) { lines.push( `### Key #${keyId} (${comments.length} comment${comments.length !== 1 ? "s" : ""})`, ); const tableData = comments.map((comment: Comment) => ({ id: comment.comment_id, author: comment.added_by_email || `User #${comment.added_by}`, comment: comment.comment.length > 40 ? `${comment.comment.substring(0, 40)}...` : comment.comment, added: formatDate(new Date(comment.added_at_timestamp * 1000)), })); const table = formatTable(tableData, [ { key: "id", header: "ID" }, { key: "author", header: "Author" }, { key: "comment", header: "Comment" }, { key: "added", header: "Added" }, ]); lines.push(table); lines.push(""); } // Add pagination info if applicable if (commentsData.totalPages > 1) { lines.push(formatHeading("Pagination", 2)); lines.push( `Page ${commentsData.currentPage} of ${commentsData.totalPages}`, ); if (commentsData.currentPage < commentsData.totalPages) { lines.push(`- Next page: ${commentsData.currentPage + 1}`); } if (commentsData.currentPage > 1) { lines.push(`- Previous page: ${commentsData.currentPage - 1}`); } lines.push(""); } // Add footer lines.push(formatFooter("All comments retrieved")); return lines.join("\n"); } /** * Format comment details into Markdown. * @param comment - Comment data from API * @returns Formatted Markdown string */ export function formatCommentDetails(comment: Comment): string { const lines: string[] = []; // Add main heading lines.push(formatHeading(`Comment #${comment.comment_id}`, 1)); lines.push(""); // Add comment content lines.push(formatHeading("Comment", 2)); lines.push("```"); lines.push(comment.comment); lines.push("```"); lines.push(""); // Add metadata lines.push(formatHeading("Details", 2)); const details: Record<string, unknown> = { "Comment ID": comment.comment_id, "Key ID": comment.key_id, Author: comment.added_by_email || `User #${comment.added_by}`, "Author ID": comment.added_by, Added: formatDate(new Date(comment.added_at_timestamp * 1000)), Timestamp: comment.added_at_timestamp, }; lines.push(formatBulletList(details)); lines.push(""); // Add footer lines.push(formatFooter("Comment retrieved")); return lines.join("\n"); } /** * Format comments creation result into Markdown. * @param comments - The created comments data * @param keyId - The key ID for context * @returns Formatted Markdown string */ export function formatCreateCommentsResult( comments: Comment[], keyId: number, ): string { const lines: string[] = []; // Add main heading const count = comments.length; lines.push( formatHeading( `${count} Comment${count !== 1 ? "s" : ""} Created Successfully`, 1, ), ); lines.push(""); // Add creation summary lines.push(formatHeading("Creation Summary", 2)); const summary: Record<string, unknown> = { "Key ID": keyId, "Comments Created": count, "Created At": formatDate(new Date()), }; lines.push(formatBulletList(summary)); lines.push(""); // Add created comments details lines.push(formatHeading("Created Comments", 2)); const tableData = comments.map((comment: Comment) => ({ id: comment.comment_id, author: comment.added_by_email || `User #${comment.added_by}`, comment: comment.comment.length > 50 ? `${comment.comment.substring(0, 50)}...` : comment.comment, added: formatDate(new Date(comment.added_at_timestamp * 1000)), })); const table = formatTable(tableData, [ { key: "id", header: "ID" }, { key: "author", header: "Author" }, { key: "comment", header: "Comment" }, { key: "added", header: "Added" }, ]); lines.push(table); lines.push(""); // Add next steps lines.push(formatHeading("Next Steps", 2)); const nextSteps = [ "View the comments in the Lokalise dashboard", "Add replies or additional comments as needed", "Use comments to guide translation decisions", ]; for (const step of nextSteps) { lines.push(`- ${step}`); } lines.push(""); // Add footer lines.push(formatFooter("Comments created")); return lines.join("\n"); } /** * Format comment deletion result into Markdown. * @param result - The deletion result from API * @returns Formatted Markdown string */ export function formatDeleteCommentResult(result: CommentDeleted): string { const lines: string[] = []; // Add main heading lines.push(formatHeading("Comment Deleted Successfully", 1)); lines.push(""); // Add deletion details lines.push(formatHeading("Deletion Details", 2)); const details: Record<string, unknown> = { "Project ID": result.project_id, "Deletion Status": result.comment_deleted ? "Success" : "Failed", "Deletion Time": formatDate(new Date()), }; if (result.branch) { details.Branch = result.branch; } lines.push(formatBulletList(details)); lines.push(""); // Add warning lines.push(formatHeading("⚠️ Important", 2)); lines.push("- This action cannot be undone"); lines.push("- The comment has been permanently removed"); lines.push("- Other comments on the same key remain unaffected"); lines.push(""); // Add footer lines.push(formatFooter("Comment deleted")); return lines.join("\n"); }

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/AbdallahAHO/lokalise-mcp'

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