Files
DoGaMa-serv/internal/web/i18n.go
T
codex 368ae918a8
CI / validate (pull_request) Successful in 26m27s
feat(runtime): add configurable game server uid gid
2026-08-25 22:57:23 +02:00

143 lines
28 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package web
import (
"sort"
"strconv"
"strings"
)
const defaultLanguage = "en"
var messages = map[string]map[string]string{
"en": {
"brand.control": "Docker Gameserver Manager", "nav.primary": "Primary navigation", "nav.dashboard": "Dashboard", "nav.instances": "Instances", "nav.catalog": "Catalog", "nav.backups": "Backups", "nav.audit": "Audit", "nav.settings": "Settings", "nav.administration": "Administration", "nav.planned": "This page is planned for a future milestone",
"setup.title": "Create administrator", "setup.heading": "Welcome", "setup.introduction": "Create the first administrator to finish setup.", "setup.submit": "Create administrator",
"login.title": "Sign in", "login.heading": "Sign in", "login.introduction": "Manage your game servers from one secure place.", "login.submit": "Sign in", "logout.submit": "Sign out",
"dashboard.title": "Dashboard", "dashboard.eyebrow": "Dashboard", "dashboard.heading": "Game servers", "dashboard.introduction": "Overview of all your game server instances.", "dashboard.search": "Search instances", "dashboard.search_placeholder": "Search instance", "dashboard.summary": "Instance summary", "dashboard.total": "Total instances", "dashboard.running": "Running", "dashboard.stopped": "Stopped", "dashboard.updating": "Updating", "dashboard.error": "Error", "dashboard.no_match": "No matching instance", "dashboard.empty_heading": "No instances deployed", "dashboard.empty_copy": "Your game servers will appear here when they are added from the Catalog.", "dashboard.instances_eyebrow": "Servers", "dashboard.instances": "Your instances", "dashboard.activity_eyebrow": "Operations", "dashboard.recent_activity": "Recent activity", "dashboard.no_activity": "No recent activity", "dashboard.system_eyebrow": "Health", "dashboard.system_status": "System status", "dashboard.agent": "Docker agent", "dashboard.database": "Database", "dashboard.storage": "Storage", "dashboard.backups": "Backups", "dashboard.audit_log": "Audit log", "dashboard.online": "Online", "dashboard.offline": "Offline", "dashboard.healthy": "Healthy", "dashboard.unavailable": "Unavailable", "dashboard.last_backup": "Last backup", "dashboard.no_backup": "No backup", "dashboard.retention_days": "days retention", "dashboard.unlimited": "Unlimited retention", "dashboard.by": "by",
"catalog.title": "Catalog", "catalog.eyebrow": "Game library", "catalog.heading": "Catalog", "catalog.search": "Search games", "catalog.search_placeholder": "Search a game", "catalog.scan": "Scan", "catalog.found": "templates found", "catalog.valid": "valid", "catalog.invalid": "invalid", "catalog.no_match": "No matching game", "catalog.empty": "No game available", "catalog.empty_admin": "Add templates to /var/lib/dogama/templates, then use Scan.", "catalog.back": "Back to catalog", "catalog.minimum": "Minimum", "catalog.recommended": "Recommended", "catalog.memory": "Memory", "catalog.storage": "Storage", "catalog.other": "Other", "catalog.deploy": "Deploy", "catalog.deploy_unavailable": "Deployment will be available in the next milestone.",
"deployment.title": "Deploy", "deployment.eyebrow": "New instance", "deployment.heading": "Configure your server", "deployment.name": "Instance name", "deployment.description": "Instance description", "deployment.parameters": "Game parameters", "deployment.backup": "Import an external save", "deployment.backup_help": "ZIP, TAR, TAR.GZ or TAR.ZST archives are validated before use.", "deployment.go": "Go",
"settings.game_runtime": "Game server execution", "settings.game_runtime_help": "These defaults apply only to game-server containers.", "settings.game_runtime_uid": "Default UID", "settings.game_runtime_gid": "Default GID", "settings.game_runtime_policy_help": "Templates that allow an administered identity use these values. Templates using the image's native user ignore them.", "settings.save_game_runtime": "Save game server identity",
"settings.title": "Settings", "settings.eyebrow": "Administration", "settings.introduction": "Configure product services without crowding the server overview.", "settings.tabs": "Settings sections", "settings.notifications": "Notifications", "settings.audit": "Audit", "settings.containers": "Game containers", "settings.web_access": "Web access", "settings.require_https": "Require HTTPS", "settings.canonical_url": "Canonical base URL", "settings.web_help": "Configure HTTPS enforcement and the public origin. Configure and verify your HTTPS reverse proxy before enabling this lock.", "settings.save_web": "Save web access", "settings.canonical_help": "Verify the canonical URL works before saving it.", "settings.https_help": "When enabled, unsafe HTTP requests are refused and safe navigation is redirected to HTTPS.",
"settings.channels": "Notification channels", "settings.channels_help": "Secrets remain encrypted and are never displayed after saving.", "settings.no_channels": "No channel configured.", "settings.send_test": "Send test", "settings.delete": "Delete", "settings.add_channel": "Add channel", "settings.name": "Name", "settings.type": "Type", "settings.enabled": "enabled", "settings.disabled": "disabled", "settings.events": "Events (space separated)", "settings.audit_retention": "Audit retention", "settings.audit_help": "Control history size and perform explicit bounded purges.", "settings.view_audit": "View audit events", "settings.retention_days": "Retention days", "settings.maximum_entries": "Maximum entries", "settings.zero_unlimited": "Zero means unlimited and may grow the database indefinitely.", "settings.save_retention": "Save retention", "settings.delete_before": "Delete events before", "settings.confirm_purge": "Confirm bounded audit purge", "settings.purge": "Purge audit events", "settings.container_labels": "Game-container labels", "settings.container_labels_help": "These labels apply only to game-server containers.", "settings.global_labels": "Global labels", "settings.apply": "Application", "settings.next_start": "Apply on next start", "settings.immediate": "Apply immediately", "settings.disconnection": "Immediate application stops and recreates affected containers.", "settings.confirm_disconnection": "I understand the immediate-disconnection warning", "settings.save_labels": "Save game-container labels",
"audit.title": "Audit", "audit.eyebrow": "Administration", "audit.introduction": "Review significant authentication and mutation events.", "audit.actor": "Actor ID", "audit.instance": "Instance ID", "audit.action": "Action", "audit.outcome": "Outcome", "audit.any": "Any", "audit.allowed": "Allowed", "audit.denied": "Denied", "audit.failed": "Failed", "audit.filter": "Filter audit", "audit.time": "Time", "audit.actor_column": "Actor", "audit.instance_column": "Instance", "audit.empty": "No audit event matches these filters.",
"field.username": "Username", "field.password": "Password", "language": "Language", "language.english": "English", "language.french": "French", "language.save": "Save language", "account.signed_in": "Signed in as",
"error.csrf": "Request verification failed.", "error.form": "Invalid form submission.", "error.internal": "The request could not be completed.", "error.credentials": "Invalid username or password.", "error.web_policy": "Web access policy rejected the request.",
"instance.title": "Instance", "instance.back": "Back to dashboard", "instance.start": "Start", "instance.stop": "Stop", "instance.backup": "Back up", "instance.update": "Update", "instance.update_unknown": "No verified update is available.", "instance.players": "Players", "instance.password": "Server password", "instance.unavailable": "Unavailable", "instance.backups": "Backups", "instance.no_backups": "No backups", "instance.restore": "Restore", "instance.restore_confirm": "Restore this backup? The current server data will be replaced.", "instance.server_functions": "Server functions", "instance.module_unavailable": "No active module exposes server functions for this instance.", "instance.action_done.instance.start": "Instance started.", "instance.action_done.instance.stop": "Instance stopped.", "instance.action_done.backup.create": "Backup created.", "instance.action_done.backup.restore": "Backup restored.", "instance.action_failed.instance.start": "Unable to start the instance.", "instance.action_failed.instance.stop": "Unable to stop the instance.", "instance.action_failed.backup.create": "Unable to create the backup.", "instance.action_failed.backup.restore": "Unable to restore the backup.",
},
"fr": {
"brand.control": "Docker Gameserver Manager", "nav.primary": "Navigation principale", "nav.dashboard": "Tableau de bord", "nav.instances": "Instances", "nav.catalog": "Catalogue", "nav.backups": "Sauvegardes", "nav.audit": "Audit", "nav.settings": "Paramètres", "nav.administration": "Administration", "nav.planned": "Cette page est prévue pour un futur jalon",
"setup.title": "Créer l'administrateur", "setup.heading": "Bienvenue", "setup.introduction": "Créez le premier administrateur pour terminer la configuration.", "setup.submit": "Créer l'administrateur",
"login.title": "Connexion", "login.heading": "Se connecter", "login.introduction": "Gérez vos serveurs de jeux depuis un espace sécurisé.", "login.submit": "Se connecter", "logout.submit": "Se déconnecter",
"dashboard.title": "Tableau de bord", "dashboard.eyebrow": "Tableau de bord", "dashboard.heading": "Serveurs de jeux", "dashboard.introduction": "Vue d'ensemble de toutes vos instances de serveurs de jeux.", "dashboard.search": "Rechercher des instances", "dashboard.search_placeholder": "Rechercher une instance", "dashboard.summary": "Résumé des instances", "dashboard.total": "Instances totales", "dashboard.running": "En cours", "dashboard.stopped": "Arrêtées", "dashboard.updating": "Mise à jour", "dashboard.error": "Erreur", "dashboard.no_match": "Aucune instance correspondante", "dashboard.empty_heading": "Aucune instance déployée", "dashboard.empty_copy": "Vos serveurs de jeux apparaîtront ici lorsqu'ils seront ajoutés depuis le Catalogue.", "dashboard.instances_eyebrow": "Serveurs", "dashboard.instances": "Vos instances", "dashboard.activity_eyebrow": "Opérations", "dashboard.recent_activity": "Activité récente", "dashboard.no_activity": "Aucune activité récente", "dashboard.system_eyebrow": "Santé", "dashboard.system_status": "État du système", "dashboard.agent": "Agent Docker", "dashboard.database": "Base de données", "dashboard.storage": "Stockage", "dashboard.backups": "Sauvegardes", "dashboard.audit_log": "Journal d'audit", "dashboard.online": "En ligne", "dashboard.offline": "Hors ligne", "dashboard.healthy": "Saine", "dashboard.unavailable": "Indisponible", "dashboard.last_backup": "Dernière sauvegarde", "dashboard.no_backup": "Aucune sauvegarde", "dashboard.retention_days": "jours de rétention", "dashboard.unlimited": "Rétention illimitée", "dashboard.by": "par",
"catalog.title": "Catalogue", "catalog.eyebrow": "Bibliothèque de jeux", "catalog.heading": "Catalogue", "catalog.search": "Rechercher des jeux", "catalog.search_placeholder": "Rechercher un jeu", "catalog.scan": "Scanner", "catalog.found": "templates trouvés", "catalog.valid": "valides", "catalog.invalid": "invalides", "catalog.no_match": "Aucun jeu correspondant", "catalog.empty": "Aucun jeu disponible", "catalog.empty_admin": "Ajoutez des templates dans /var/lib/dogama/templates, puis utilisez Scanner.", "catalog.back": "Retour au catalogue", "catalog.minimum": "Minimum", "catalog.recommended": "Recommandé", "catalog.memory": "Mémoire", "catalog.storage": "Stockage", "catalog.other": "Autre", "catalog.deploy": "Déployer", "catalog.deploy_unavailable": "Le déploiement sera disponible au prochain bloc.",
"deployment.title": "Déployer", "deployment.eyebrow": "Nouvelle instance", "deployment.heading": "Configurer votre serveur", "deployment.name": "Nom de l’instance", "deployment.description": "Description de l’instance", "deployment.parameters": "Paramètres du jeu", "deployment.backup": "Importer une sauvegarde externe", "deployment.backup_help": "Les archives ZIP, TAR, TAR.GZ et TAR.ZST sont validées avant utilisation.", "deployment.go": "Go",
"settings.title": "Paramètres", "settings.eyebrow": "Administration", "settings.introduction": "Configurez les services du produit sans encombrer la vue des serveurs.", "settings.tabs": "Sections des paramètres", "settings.notifications": "Notifications", "settings.audit": "Audit", "settings.containers": "Conteneurs de jeux", "settings.web_access": "Accès Web", "settings.require_https": "Exiger HTTPS", "settings.canonical_url": "URL de base canonique", "settings.web_help": "Configurez l'obligation HTTPS et l'origine publique. Configurez et vérifiez votre proxy inverse HTTPS avant d'activer ce verrouillage.", "settings.save_web": "Enregistrer l'accès Web", "settings.canonical_help": "Vérifiez que l'URL canonique fonctionne avant de l'enregistrer.", "settings.https_help": "Une fois activé, les requêtes HTTP non sûres sont refusées et les navigations sûres sont redirigées vers HTTPS.",
"settings.channels": "Canaux de notification", "settings.channels_help": "Les secrets restent chiffrés et ne sont jamais affichés après enregistrement.", "settings.no_channels": "Aucun canal configuré.", "settings.send_test": "Envoyer un test", "settings.delete": "Supprimer", "settings.add_channel": "Ajouter un canal", "settings.name": "Nom", "settings.type": "Type", "settings.enabled": "activé", "settings.disabled": "désactivé", "settings.events": "Événements (séparés par des espaces)", "settings.audit_retention": "Rétention de l'audit", "settings.audit_help": "Contrôlez la taille de l'historique et effectuez des purges limitées explicites.", "settings.view_audit": "Voir les événements d'audit", "settings.retention_days": "Jours de rétention", "settings.maximum_entries": "Nombre maximal d'entrées", "settings.zero_unlimited": "Zéro signifie illimité et peut faire croître la base indéfiniment.", "settings.save_retention": "Enregistrer la rétention", "settings.delete_before": "Supprimer les événements antérieurs au", "settings.confirm_purge": "Confirmer la purge limitée de l'audit", "settings.purge": "Purger les événements d'audit", "settings.container_labels": "Étiquettes des conteneurs de jeux", "settings.container_labels_help": "Ces étiquettes s'appliquent uniquement aux conteneurs de serveurs de jeux.", "settings.global_labels": "Étiquettes globales", "settings.apply": "Application", "settings.next_start": "Appliquer au prochain démarrage", "settings.immediate": "Appliquer immédiatement", "settings.disconnection": "L'application immédiate arrête et recrée les conteneurs concernés.", "settings.confirm_disconnection": "Je comprends l'avertissement de déconnexion immédiate", "settings.save_labels": "Enregistrer les étiquettes des conteneurs",
"audit.title": "Audit", "audit.eyebrow": "Administration", "audit.introduction": "Consultez les événements importants d'authentification et de modification.", "audit.actor": "ID de l'acteur", "audit.instance": "ID de l'instance", "audit.action": "Action", "audit.outcome": "Résultat", "audit.any": "Tous", "audit.allowed": "Autorisé", "audit.denied": "Refusé", "audit.failed": "Échec", "audit.filter": "Filtrer l'audit", "audit.time": "Heure", "audit.actor_column": "Acteur", "audit.instance_column": "Instance", "audit.empty": "Aucun événement d'audit ne correspond à ces filtres.",
"field.username": "Nom d'utilisateur", "field.password": "Mot de passe", "language": "Langue", "language.english": "Anglais", "language.french": "Français", "language.save": "Enregistrer la langue", "account.signed_in": "Connecté en tant que",
"error.csrf": "La vérification de la requête a échoué.", "error.form": "Envoi de formulaire invalide.", "error.internal": "La requête n'a pas pu être effectuée.", "error.credentials": "Nom d'utilisateur ou mot de passe invalide.", "error.web_policy": "La politique d'accès Web a rejeté la requête.",
"instance.title": "Instance", "instance.back": "Retour au tableau de bord", "instance.start": "Démarrer", "instance.stop": "Arrêter", "instance.backup": "Sauvegarder", "instance.update": "Mettre à jour", "instance.update_unknown": "Aucune mise à jour vérifiée n’est disponible.", "instance.players": "Joueurs", "instance.password": "Mot de passe serveur", "instance.unavailable": "Indisponible", "instance.backups": "Sauvegardes", "instance.no_backups": "Aucune sauvegarde", "instance.restore": "Restaurer", "instance.restore_confirm": "Restaurer cette sauvegarde ? Les données actuelles seront remplacées.", "instance.server_functions": "Fonctions serveur", "instance.module_unavailable": "Aucun module actif n’expose de fonctions serveur pour cette instance.", "instance.action_done.instance.start": "Instance démarrée.", "instance.action_done.instance.stop": "Instance arrêtée.", "instance.action_done.backup.create": "Sauvegarde créée.", "instance.action_done.backup.restore": "Sauvegarde restaurée.", "instance.action_failed.instance.start": "Impossible de démarrer l’instance.", "instance.action_failed.instance.stop": "Impossible d’arrêter l’instance.", "instance.action_failed.backup.create": "Impossible de créer la sauvegarde.", "instance.action_failed.backup.restore": "Impossible de restaurer la sauvegarde.",
},
}
func init() {
for language, values := range map[string]map[string]string{
"en": {"audit.empty": "No audit events yet.", "audit.empty_filtered": "No audit events match these filters.", "audit.events": "events", "audit.timezone": "Times shown in", "audit.filters": "Filters", "audit.reset": "Reset filters", "audit.period": "Period", "audit.today": "Today", "audit.last_7_days": "Last 7 days", "audit.last_30_days": "Last 30 days", "audit.custom": "Custom", "audit.from": "From", "audit.to": "To", "audit.search": "Search", "audit.results": "Results", "audit.details": "Details", "audit.pagination": "Audit pagination", "audit.previous": "Previous", "audit.next": "Next", "audit.action.auth.login": "Sign in", "audit.action.auth.login.blocked": "Blocked sign in", "audit.action.instance.deploy": "Instance deployment", "audit.action.instance.start": "Instance start", "audit.action.instance.stop": "Instance stop", "audit.action.instance.restart": "Instance restart", "audit.action.instance.update": "Instance update", "audit.action.backup.create": "Backup creation", "audit.action.backup.restore": "Backup restore", "audit.action.players.unban": "Player unban", "audit.action.audit.policy.update": "Audit retention update", "audit.action.audit.purge": "Audit purge", "audit.action.notification.channel.update": "Notification channel update", "audit.action.notification.channel.delete": "Notification channel deletion", "audit.action.notification.channel.test": "Notification channel test"},
"fr": {"audit.empty": "Aucun événement d’audit.", "audit.empty_filtered": "Aucun événement ne correspond aux filtres.", "audit.events": "événements", "audit.timezone": "Heures affichées en", "audit.filters": "Filtres", "audit.reset": "Réinitialiser les filtres", "audit.period": "Période", "audit.today": "Aujourd’hui", "audit.last_7_days": "7 derniers jours", "audit.last_30_days": "30 derniers jours", "audit.custom": "Personnalisée", "audit.from": "Du", "audit.to": "Au", "audit.search": "Rechercher", "audit.results": "Résultats", "audit.details": "Détails", "audit.pagination": "Pagination de l’audit", "audit.previous": "Précédent", "audit.next": "Suivant", "audit.action.auth.login": "Connexion", "audit.action.auth.login.blocked": "Connexion bloquée", "audit.action.instance.deploy": "Déploiement d’instance", "audit.action.instance.start": "Démarrage d’instance", "audit.action.instance.stop": "Arrêt d’instance", "audit.action.instance.restart": "Redémarrage d’instance", "audit.action.instance.update": "Mise à jour d’instance", "audit.action.backup.create": "Création de sauvegarde", "audit.action.backup.restore": "Restauration de sauvegarde", "audit.action.players.unban": "Débannissement de joueur", "audit.action.audit.policy.update": "Mise à jour de la rétention", "audit.action.audit.purge": "Purge de l’audit", "audit.action.notification.channel.update": "Mise à jour de canal de notification", "audit.action.notification.channel.delete": "Suppression de canal de notification", "audit.action.notification.channel.test": "Test de canal de notification"},
} {
for key, value := range values {
messages[language][key] = value
}
}
for language, values := range map[string]map[string]string{
"en": {"instance.update_unknown": "Update verification is unavailable.", "instance.update_available": "A verified update is available.", "instance.update_current": "This instance is up to date.", "instance.banned_players": "Banned players", "instance.no_banned_players": "No banned players.", "instance.player_identifier": "Player identifier", "instance.bans_not_supported": "The banned-player list is unavailable for this server. Enter the identifier expected by the game API.", "instance.action_done.instance.update": "Instance updated.", "instance.action_failed.instance.update": "Unable to update the instance."},
"fr": {"instance.update_unknown": "La vérification de mise à jour est indisponible.", "instance.update_available": "Une mise à jour vérifiée est disponible.", "instance.update_current": "Cette instance est à jour.", "instance.banned_players": "Joueurs bannis", "instance.no_banned_players": "Aucun joueur banni.", "instance.player_identifier": "Identifiant du joueur", "instance.bans_not_supported": "La liste des joueurs bannis n’est pas disponible pour ce serveur. Saisissez l’identifiant attendu par l’API du jeu.", "instance.action_done.instance.update": "Instance mise à jour.", "instance.action_failed.instance.update": "Impossible de mettre l’instance à jour."},
} {
for key, value := range values {
messages[language][key] = value
}
}
for language, values := range map[string]map[string]string{
"en": {"template_repositories.title": "Template repositories", "template_repositories.heading": "Template repositories", "template_repositories.introduction": "Configure future template sources. Remote synchronization is not implemented.", "template_repositories.users": "Users", "template_repositories.new": "New repository", "template_repositories.name": "Name", "template_repositories.url": "URL", "template_repositories.add": "Add", "template_repositories.delete": "Delete", "template_repositories.delete_confirm": "Delete this template repository?", "template_repositories.not_implemented": "Feature not implemented", "template_repositories.empty": "No template repositories configured.", "template_repositories.validation_error": "Enter a name and a valid HTTP or HTTPS URL.", "template_repositories.duplicate_error": "This template repository URL is already configured.", "template_repositories.not_found": "Template repository not found."},
"fr": {"template_repositories.title": "Dépôts de templates", "template_repositories.heading": "Dépôts de templates", "template_repositories.introduction": "Configurez de futures sources de templates. La synchronisation distante n’est pas implémentée.", "template_repositories.users": "Utilisateurs", "template_repositories.new": "Nouveau dépôt", "template_repositories.name": "Nom", "template_repositories.url": "URL", "template_repositories.add": "Ajouter", "template_repositories.delete": "Supprimer", "template_repositories.delete_confirm": "Supprimer ce dépôt de templates ?", "template_repositories.not_implemented": "Fonctionnalité non implémentée", "template_repositories.empty": "Aucun dépôt de templates configuré.", "template_repositories.validation_error": "Saisissez un nom et une URL HTTP ou HTTPS valide.", "template_repositories.duplicate_error": "Cette URL de dépôt de templates est déjà configurée.", "template_repositories.not_found": "Dépôt de templates introuvable."},
} {
for key, value := range values {
messages[language][key] = value
}
}
for language, values := range map[string]map[string]string{
"en": {"settings.notification_general": "General", "settings.notification_language_help": "Choose the language used in delivered notifications.", "settings.notification_email": "Email / SMTP", "settings.notification_discord": "Discord", "settings.notification_gotify": "Gotify", "settings.sender_name": "Sender name", "settings.sender_email": "Sender email", "settings.smtp_server": "SMTP server", "settings.port": "Port", "settings.username": "Username", "settings.password": "Password", "settings.password_retain": "leave empty to retain on edit", "settings.tls_mode": "TLS mode", "settings.test_recipient": "Test recipient (optional)", "settings.save_smtp": "Save SMTP", "settings.webhook_url": "Webhook URL", "settings.save_discord": "Save Discord", "settings.gotify_help": "Create an application in Gotify, then enter its server URL and application token here.", "settings.server_url": "Server URL", "settings.application_token": "Application token", "settings.save_gotify": "Save Gotify", "settings.configured": "configured", "settings.not_configured": "not configured"},
"fr": {"settings.notification_general": "Général", "settings.notification_language_help": "Choisissez la langue utilisée pour les notifications envoyées.", "settings.notification_email": "E-mail / SMTP", "settings.notification_discord": "Discord", "settings.notification_gotify": "Gotify", "settings.sender_name": "Nom de l'expéditeur", "settings.sender_email": "E-mail de l'expéditeur", "settings.smtp_server": "Serveur SMTP", "settings.port": "Port", "settings.username": "Nom d'utilisateur", "settings.password": "Mot de passe", "settings.password_retain": "laissez vide pour le conserver lors d'une modification", "settings.tls_mode": "Mode TLS", "settings.test_recipient": "Destinataire de test (facultatif)", "settings.save_smtp": "Enregistrer SMTP", "settings.webhook_url": "URL du webhook", "settings.save_discord": "Enregistrer Discord", "settings.gotify_help": "Créez une application dans Gotify, puis saisissez ici l'URL du serveur et le jeton de l'application.", "settings.server_url": "URL du serveur", "settings.application_token": "Jeton d'application", "settings.save_gotify": "Enregistrer Gotify", "settings.configured": "configuré", "settings.not_configured": "non configuré"},
} {
for key, value := range values {
messages[language][key] = value
}
}
}
type languageOption struct {
Code string
Display string
}
var languageOptions = []languageOption{
{Code: "fr", Display: "🇫🇷 Français"},
{Code: "en", Display: "🇬🇧 English"},
}
func supportedLanguageOptions() []languageOption { return languageOptions }
func validLanguage(value string) bool {
for _, option := range languageOptions {
if option.Code == value {
return true
}
}
return false
}
func negotiateLanguage(header string) string {
type candidate struct {
language string
quality float64
order int
}
var candidates []candidate
for order, part := range strings.Split(header, ",") {
fields := strings.Split(strings.TrimSpace(part), ";")
language := strings.ToLower(strings.Split(strings.TrimSpace(fields[0]), "-")[0])
quality := 1.0
for _, field := range fields[1:] {
field = strings.TrimSpace(field)
if strings.HasPrefix(field, "q=") {
if value, err := strconv.ParseFloat(strings.TrimPrefix(field, "q="), 64); err == nil {
quality = value
}
}
}
if validLanguage(language) && quality > 0 {
candidates = append(candidates, candidate{language, quality, order})
}
}
sort.SliceStable(candidates, func(i, j int) bool { return candidates[i].quality > candidates[j].quality })
if len(candidates) > 0 {
return candidates[0].language
}
return defaultLanguage
}
func localized(language, key string) string {
if !validLanguage(language) {
language = defaultLanguage
}
if value := messages[language][key]; value != "" {
return value
}
if value := messages[defaultLanguage][key]; value != "" {
return value
}
return key
}
func (d pageData) Msg(key string) string { return localized(d.Language, key) }