package main import ( "fmt" "strings" ) // Message key constants for i18n. const ( MsgCommandEmpty = "command.empty" MsgCommandUnknown = "command.unknown" MsgNotConfigured = "requestbook.not_configured" MsgUsage = "requestbook.usage" MsgSearchFailed = "requestbook.search_failed" MsgNoBooksFound = "requestbook.no_books_found" MsgQueueFailed = "requestbook.queue_failed" MsgBookFound = "requestbook.book_found" MsgLanguageInfo = "requestbook.language_info" MsgDownloadTimeout = "job.download_timeout" MsgNoReleases = "job.no_releases" MsgNoReleasesFound = "job.no_releases_found" MsgDownloadStartFailed = "job.download_start_failed" MsgShelfmarkError = "job.shelfmark_error" MsgTrackingLost = "job.tracking_lost" MsgDownloadStatus = "job.download_status" MsgDownloadFailed = "job.download_failed" MsgPostFailed = "job.post_failed" MsgUploadFailed = "job.upload_failed" MsgAttachFailed = "job.attach_failed" MsgTeamNotAllowed = "command.team_not_allowed" MsgChannelNotFound = "config.channel_not_found" MsgShelfmarkUnreachable = "config.shelfmark_unreachable" ) // messages maps locale → key → format string. var messages = map[string]map[string]string{ "en": { MsgCommandEmpty: "Empty command received.", MsgCommandUnknown: "Unknown command: %s", MsgNotConfigured: "Plugin is not configured: %s", MsgUsage: "Usage: `/requestbook [--language ] `\nExample: `/requestbook The Hitchhiker's Guide to the Galaxy`\nExample: `/requestbook --language es Don Quixote`", MsgSearchFailed: "Failed to search for books. Please check the plugin configuration and try again.", MsgNoBooksFound: "No books found for \"%s\".", MsgQueueFailed: "Failed to queue the book request. Please try again.", MsgBookFound: "Book \"%s\" found%s. The download has been queued and will be posted when ready.", MsgLanguageInfo: " (language: %s)", MsgDownloadTimeout: "Download for \"%s\" timed out. Please try again.", MsgNoReleases: "Could not find any downloadable files for \"%s\".", MsgNoReleasesFound: "No downloadable files were found for \"%s\".", MsgDownloadStartFailed: "Failed to start the download for \"%s\". Please try again.", MsgShelfmarkError: "Shelfmark could not process the download for \"%s\".", MsgTrackingLost: "The download for \"%s\" could not be tracked on Shelfmark.", MsgDownloadStatus: "The download for \"%s\" has %s on Shelfmark.", MsgDownloadFailed: "Failed to download \"%s\" from Shelfmark.", MsgPostFailed: "Failed to create the book post for \"%s\".", MsgUploadFailed: "Failed to upload the file for \"%s\".", MsgAttachFailed: "Failed to attach the file for \"%s\".", MsgTeamNotAllowed: "This plugin is not available on this team.", MsgChannelNotFound: "The configured channel ID does not exist.", MsgShelfmarkUnreachable: "Cannot reach the Shelfmark server.", }, "es": { MsgCommandEmpty: "Comando vacío recibido.", MsgCommandUnknown: "Comando desconocido: %s", MsgNotConfigured: "El plugin no está configurado: %s", MsgUsage: "Uso: `/requestbook [--language ] `\nEjemplo: `/requestbook The Hitchhiker's Guide to the Galaxy`\nEjemplo: `/requestbook --language es Don Quixote`", MsgSearchFailed: "Error al buscar libros. Por favor, compruebe la configuración del plugin e inténtelo de nuevo.", MsgNoBooksFound: "No se encontraron libros para \"%s\".", MsgQueueFailed: "Error al encolar la solicitud del libro. Por favor, inténtelo de nuevo.", MsgBookFound: "Libro \"%s\" encontrado%s. La descarga ha sido añadida a la cola y se publicará cuando esté lista.", MsgLanguageInfo: " (idioma: %s)", MsgDownloadTimeout: "La descarga de \"%s\" ha expirado. Por favor, inténtelo de nuevo.", MsgNoReleases: "No se encontraron archivos descargables para \"%s\".", MsgNoReleasesFound: "No se encontraron archivos descargables para \"%s\".", MsgDownloadStartFailed: "Error al iniciar la descarga de \"%s\". Por favor, inténtelo de nuevo.", MsgShelfmarkError: "Shelfmark no pudo procesar la descarga de \"%s\".", MsgTrackingLost: "No se pudo rastrear la descarga de \"%s\" en Shelfmark.", MsgDownloadStatus: "La descarga de \"%s\" tiene estado %s en Shelfmark.", MsgDownloadFailed: "Error al descargar \"%s\" desde Shelfmark.", MsgPostFailed: "Error al crear la publicación del libro \"%s\".", MsgUploadFailed: "Error al subir el archivo de \"%s\".", MsgAttachFailed: "Error al adjuntar el archivo de \"%s\".", MsgTeamNotAllowed: "Este plugin no está disponible en este equipo.", MsgChannelNotFound: "El canal configurado no existe.", MsgShelfmarkUnreachable: "No se puede conectar con el servidor Shelfmark.", }, } // T returns a localized, formatted message. Falls back to English if the locale // or key is not found. func T(locale, key string, args ...any) string { format := localizedMessage(locale, key) if len(args) == 0 { return format } return fmt.Sprintf(format, args...) } // localizedMessage resolves the format string for a given locale and key. // It normalizes locales like "es-ES" → "es" and falls back to "en". func localizedMessage(locale, key string) string { normalized := normalizeLocale(locale) if msgs, ok := messages[normalized]; ok { if format, ok := msgs[key]; ok { return format } } // Fallback to English. if msgs, ok := messages["en"]; ok { if format, ok := msgs[key]; ok { return format } } return key } // normalizeLocale extracts the base language from a locale string (e.g., "es-ES" → "es"). func normalizeLocale(locale string) string { if locale == "" { return "en" } parts := strings.SplitN(locale, "-", 2) return strings.ToLower(parts[0]) }