Add early validation in OnConfigurationChange to verify the configured channel exists in Mattermost and the Shelfmark server is reachable, giving admins immediate feedback instead of discovering issues at runtime. - Add Ping() method to shelfmark.Client for lightweight reachability check - Add validateConfiguration() on Plugin that checks channel + Shelfmark - Add i18n messages for channel not found and Shelfmark unreachable - Add unit tests for Ping and validateConfiguration Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
127 lines
6 KiB
Go
127 lines
6 KiB
Go
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 <code>] <search query>`\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 <código>] <búsqueda>`\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])
|
|
}
|