Skip to main content
Glama
keys.cli.ts12.1 kB
import type { SupportedPlatforms } from "@lokalise/node-api"; import type { Command } from "commander"; import type { DomainCli, DomainMeta } from "../../shared/types/domain.types.js"; import { handleCliError } from "../../shared/utils/error.util.js"; import { Logger } from "../../shared/utils/logger.util.js"; import keysController from "./keys.controller.js"; const logger = Logger.forContext("cli/keys.cli.ts"); /** * Register Keys CLI commands * @param program The Commander program instance */ function register(program: Command) { const methodLogger = logger.forMethod("register"); methodLogger.debug("Registering Keys CLI commands..."); // List Keys Command program .command("list-keys") .description( "Lists translation keys from a Lokalise project with optional filtering and pagination support.", ) .requiredOption("-p, --project-id <projectId>", "Lokalise project ID") .option( "-l, --limit <number>", "Number of keys to return (1-5000, default: 100)", (value) => { const parsed = Number.parseInt(value, 10); if (Number.isNaN(parsed) || parsed < 1 || parsed > 5000) { throw new Error("Limit must be a number between 1 and 5000"); } return parsed; }, ) .option( "--page <number>", "Page number for pagination (default: 1)", (value) => { const parsed = Number.parseInt(value, 10); if (Number.isNaN(parsed) || parsed < 1) { throw new Error("Page must be a number greater than 0"); } return parsed; }, ) .option("--include-translations", "Include translation data for each key") .option( "--filter-keys <keys>", "Filter by specific key names (comma-separated)", (value) => value.split(",").map((k) => k.trim()), ) .option( "--filter-platforms <platforms>", "Filter by platforms: ios, android, web, other (comma-separated)", (value) => { const platforms = value.split(",").map((p) => p.trim()); const validPlatforms = ["ios", "android", "web", "other"]; for (const platform of platforms) { if (!validPlatforms.includes(platform)) { throw new Error( `Invalid platform: ${platform}. Valid platforms: ${validPlatforms.join(", ")}`, ); } } return platforms; }, ) .action(async (options) => { const actionLogger = logger.forMethod("action:list-keys"); actionLogger.debug("Executing list-keys command", options); try { const result = await keysController.listKeys({ projectId: options.projectId, limit: options.limit, page: options.page, includeTranslations: options.includeTranslations || false, filterKeys: options.filterKeys, filterPlatforms: options.filterPlatforms, }); console.log(result.content); } catch (error) { handleCliError({ message: "Failed to list keys", error, command: "list-keys", projectId: options.projectId, limit: options.limit, }); } }); // Create Keys Command program .command("create-keys") .description( "Creates multiple translation keys in a Lokalise project with optional initial translations.", ) .requiredOption("-p, --project-id <projectId>", "Lokalise project ID") .requiredOption( "-k, --keys <keys>", "JSON string or file path containing keys data (array of key objects)", ) .action(async (options) => { const actionLogger = logger.forMethod("action:create-keys"); actionLogger.debug("Executing create-keys command", options); try { let keysData: unknown; // Try to parse as JSON first, then check if it's a file path try { keysData = JSON.parse(options.keys); } catch { // If JSON parsing fails, treat as file path const fs = await import("node:fs"); if (fs.existsSync(options.keys)) { const fileContent = fs.readFileSync(options.keys, "utf-8"); keysData = JSON.parse(fileContent); } else { throw new Error("Invalid JSON and file does not exist"); } } if (!Array.isArray(keysData)) { throw new Error("Keys data must be an array of key objects"); } const result = await keysController.createKeys({ projectId: options.projectId, keys: keysData, }); console.log(result.content); } catch (error) { handleCliError({ message: "Failed to create keys", error, command: "create-keys", projectId: options.projectId, keys: options.keys, }); } }); // Get Key Command program .command("get-key") .description( "Retrieves detailed information about a specific translation key from a Lokalise project.", ) .requiredOption("-p, --project-id <projectId>", "Lokalise project ID") .requiredOption("-k, --key-id <keyId>", "Key ID to retrieve", (value) => { const parsed = Number.parseInt(value, 10); if (Number.isNaN(parsed)) { throw new Error("Key ID must be a number"); } return parsed; }) .action(async (options) => { const actionLogger = logger.forMethod("action:get-key"); actionLogger.debug("Executing get-key command", options); try { const result = await keysController.getKey({ projectId: options.projectId, keyId: options.keyId, }); console.log(result.content); } catch (error) { handleCliError({ message: "Failed to get key", error, command: "get-key", projectId: options.projectId, keyId: options.keyId, }); } }); // Update Key Command program .command("update-key") .description( "Updates the metadata (description, platforms, tags) of a translation key in a Lokalise project.", ) .requiredOption("-p, --project-id <projectId>", "Lokalise project ID") .requiredOption("-k, --key-id <keyId>", "Key ID to update", (value) => { const parsed = Number.parseInt(value, 10); if (Number.isNaN(parsed)) { throw new Error("Key ID must be a number"); } return parsed; }) .option("-d, --description <description>", "New description for the key") .option( "--platforms <platforms>", "New platforms for the key: ios, android, web, other (comma-separated)", (value) => { const platforms = value.split(",").map((p) => p.trim()); const validPlatforms = ["ios", "android", "web", "other"]; for (const platform of platforms) { if (!validPlatforms.includes(platform)) { throw new Error( `Invalid platform: ${platform}. Valid platforms: ${validPlatforms.join(", ")}`, ); } } return platforms; }, ) .option( "-t, --tags <tags>", "New tags for the key (comma-separated)", (value) => value.split(",").map((t) => t.trim()), ) .action(async (options) => { const actionLogger = logger.forMethod("action:update-key"); actionLogger.debug("Executing update-key command", options); try { const keyData: { description?: string; platforms?: SupportedPlatforms[]; tags?: string[]; } = {}; if (options.description !== undefined) { keyData.description = options.description; } if (options.platforms !== undefined) { keyData.platforms = options.platforms; } if (options.tags !== undefined) { keyData.tags = options.tags; } const result = await keysController.updateKey({ projectId: options.projectId, keyId: options.keyId, keyData, }); console.log(result.content); } catch (error) { handleCliError({ message: "Failed to update key", error, command: "update-key", projectId: options.projectId, keyId: options.keyId, }); } }); // Delete Key Command program .command("delete-key") .description( "Deletes a translation key and all its translations from a Lokalise project permanently.", ) .requiredOption("-p, --project-id <projectId>", "Lokalise project ID") .requiredOption("-k, --key-id <keyId>", "Key ID to delete", (value) => { const parsed = Number.parseInt(value, 10); if (Number.isNaN(parsed)) { throw new Error("Key ID must be a number"); } return parsed; }) .option("--confirm", "Confirm deletion (required for safety)") .action(async (options) => { const actionLogger = logger.forMethod("action:delete-key"); actionLogger.debug("Executing delete-key command", options); try { if (!options.confirm) { console.error("Error: Deletion requires --confirm flag for safety"); process.exit(1); } const result = await keysController.deleteKey({ projectId: options.projectId, keyId: options.keyId, }); console.log(result.content); } catch (error) { handleCliError({ message: "Failed to delete key", error, command: "delete-key", projectId: options.projectId, keyId: options.keyId, }); } }); // Bulk Update Keys Command program .command("bulk-update-keys") .description( "Updates multiple translation keys in a Lokalise project in a single operation.", ) .requiredOption("-p, --project-id <projectId>", "Lokalise project ID") .requiredOption( "-k, --keys <keys>", "JSON string or file path containing keys update data (array of key update objects)", ) .action(async (options) => { const actionLogger = logger.forMethod("action:bulk-update-keys"); actionLogger.debug("Executing bulk-update-keys command", options); try { let keysData: unknown; // Try to parse as JSON first, then check if it's a file path try { keysData = JSON.parse(options.keys); } catch { // If JSON parsing fails, treat as file path const fs = await import("node:fs"); if (fs.existsSync(options.keys)) { const fileContent = fs.readFileSync(options.keys, "utf-8"); keysData = JSON.parse(fileContent); } else { throw new Error("Invalid JSON and file does not exist"); } } if (!Array.isArray(keysData)) { throw new Error("Keys data must be an array of key update objects"); } const result = await keysController.bulkUpdateKeys({ projectId: options.projectId, keys: keysData, }); console.log(result.content); } catch (error) { handleCliError({ message: "Failed to bulk update keys", error, command: "bulk-update-keys", projectId: options.projectId, keys: options.keys, }); } }); // Bulk Delete Keys Command program .command("bulk-delete-keys") .description( "Deletes multiple translation keys and all their translations from a Lokalise project permanently.", ) .requiredOption("-p, --project-id <projectId>", "Lokalise project ID") .requiredOption( "-k, --key-ids <keyIds>", "Comma-separated list of key IDs to delete", (value) => { const keyIds = value.split(",").map((id) => { const parsed = Number.parseInt(id.trim(), 10); if (Number.isNaN(parsed)) { throw new Error(`Invalid key ID: ${id.trim()}`); } return parsed; }); if (keyIds.length === 0) { throw new Error("At least one key ID is required"); } return keyIds; }, ) .option("--confirm", "Confirm deletion (required for safety)") .action(async (options) => { const actionLogger = logger.forMethod("action:bulk-delete-keys"); actionLogger.debug("Executing bulk-delete-keys command", options); try { if (!options.confirm) { console.error( "Error: Bulk deletion requires --confirm flag for safety", ); process.exit(1); } const result = await keysController.bulkDeleteKeys({ projectId: options.projectId, keyIds: options.keyIds, }); console.log(result.content); } catch (error) { handleCliError({ message: "Failed to bulk delete keys", error, command: "bulk-delete-keys", projectId: options.projectId, keyIds: options.keyIds, }); } }); methodLogger.debug("Keys CLI commands registered successfully"); } const keysCli: DomainCli = { register, getMeta(): DomainMeta { return { name: "keys", description: "Translation keys management CLI commands", version: "1.0.0", cliCommandsCount: 7, }; }, }; export default keysCli;

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