diff --git a/Core/MultiBotEngine.lua b/Core/MultiBotEngine.lua index 19036ce..7cc8985 100644 --- a/Core/MultiBotEngine.lua +++ b/Core/MultiBotEngine.lua @@ -267,13 +267,18 @@ MultiBot.toTip = function(pClass, pLevel, pName) end MultiBot.toPoint = function(pFrame) - -- Mesurer par rapport au parent global stable et arrondir à l’unité. - local uiRight = (UIParent and UIParent:GetRight()) or GetScreenWidth() - local xRight = pFrame:GetRight() or 0 - local yBottom = pFrame:GetBottom() or 0 - -- Offset vers BOTTOMRIGHT (négatif ou nul) - local offX = xRight - uiRight - local offY = yBottom + if not pFrame then + return 0, 0 + end + -- Mesurer par rapport au parent global stable et arrondir à l’unité. + local uiRight = (UIParent and UIParent:GetRight()) or GetScreenWidth() + local getRight = pFrame.GetRight or pFrame.getRight + local getBottom = pFrame.GetBottom or pFrame.getBottom + local xRight = (type(getRight) == "function" and getRight(pFrame)) or 0 + local yBottom = (type(getBottom) == "function" and getBottom(pFrame)) or 0 + -- Offset vers BOTTOMRIGHT (négatif ou nul) + local offX = xRight - uiRight + local offY = yBottom -- Arrondi au plus proche pour éviter la dérive cumulée return math.floor(offX + 0.5), math.floor(offY + 0.5) end @@ -1472,7 +1477,7 @@ function MultiBot.BindShiftRightSwapButtons(host, contextKey, entries) state.previewTarget = nil applySelectionVisuals() if(UIErrorsFrame) then - UIErrorsFrame:AddMessage("Swap source: " .. (entryRec.id or entryRec.name), 1, 0.82, 0, 1) + UIErrorsFrame:AddMessage(MultiBot.L("ui.swap.source_prefix") .. (entryRec.id or entryRec.name), 1, 0.82, 0, 1) end return end @@ -1480,7 +1485,7 @@ function MultiBot.BindShiftRightSwapButtons(host, contextKey, entries) if(state.selected == entryRec) then clearSelectionState() if(UIErrorsFrame) then - UIErrorsFrame:AddMessage("Swap annulé.", 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage(MultiBot.L("ui.swap.cancelled"), 1, 0.25, 0.25, 1) end return end @@ -1506,7 +1511,7 @@ function MultiBot.BindShiftRightSwapButtons(host, contextKey, entries) applySelectionVisuals() if(UIErrorsFrame and state.previewTarget ~= entryRec) then state.previewTarget = entryRec - UIErrorsFrame:AddMessage("Aperçu swap: " .. (state.selected.id or state.selected.name) .. " <-> " .. (entryRec.id or entryRec.name), 1, 1, 0.4, 1) + UIErrorsFrame:AddMessage(MultiBot.L("ui.swap.preview_prefix") .. (state.selected.id or state.selected.name) .. " <-> " .. (entryRec.id or entryRec.name), 1, 1, 0.4, 1) end end end) diff --git a/Core/MultiBotHandler.lua b/Core/MultiBotHandler.lua index 0073d8c..1783d27 100644 --- a/Core/MultiBotHandler.lua +++ b/Core/MultiBotHandler.lua @@ -1531,7 +1531,7 @@ function MultiBot.HandleMultiBotEvent(event, ...) -- On ne traite que les réponses commençant par "Glyphs:" ou "No glyphs" if not rawMsg:match("^[Gg]lyphs:") and not rawMsg:match("^[Nn]o glyphs") then - DEFAULT_CHAT_FRAME:AddMessage("|cff66ccff[ERROR]|r Ignored non-glyphs msg") + DEFAULT_CHAT_FRAME:AddMessage("|cff66ccff[ERROR]|r " .. MultiBot.L("talent.glyphs.error_ignored_non_glyph")) return end diff --git a/Locales/MultiBotAceLocale-deDE.lua b/Locales/MultiBotAceLocale-deDE.lua index 5216c71..71e4de3 100644 --- a/Locales/MultiBotAceLocale-deDE.lua +++ b/Locales/MultiBotAceLocale-deDE.lua @@ -192,6 +192,50 @@ local deDEValues = { ["tips.units.inviteRaid25"] = "25er Raid\n|cffffffffMit dieser Schaltfläche füllt man sein Raid auf.\nDiese Funktion nimmt Einheiten vom aktuellen Roster und ignoriert dabei den Klassen-Filter.\nSie hält am Ende des Rosters oder sobald die Gruppe 25 Mitglieder hat.|r\n\n|cffff0000Linksklicken um Raid-Mitglieder einzuladen|r\n|cff999999(Ausführreihenfolge: System)|r", ["tips.units.inviteRaid40"] = "40er Raid\n|cffffffffMit dieser Schaltfläche füllt man sein Raid auf.\nDiese Funktion nimmt Einheiten vom aktuellen Roster und ignoriert dabei den Klassen-Filter.\nSie hält am Ende des Rosters oder sobald die Gruppe 40 Mitglieder hat.|r\n\n|cffff0000Linksklicken um Raid-Mitglieder einzuladen|r\n|cff999999(Ausführreihenfolge: System)|r", ["tips.units.alliance"] = "Alle PlayerBots ein-/ausloggen\n|cffffffffLoggt alle PlayerBots ein oder aus, auf die du Zugriff hast.\nDiese Funktion kann je nach Gesamtanzahl der PlayerBots einige Zeit benötigen,\num die Buttonleisten für jeden PlayerBot zu laden.\n\n|cffff0000Linksklick, um alle PlayerBots einzuloggen|r\n|cff999999(Ausgeführt von: System)|r\n\n|cffff0000Rechtsklick, um alle PlayerBots auszuloggen|r\n|cff999999(Ausgeführt von: System)|r", + ["options.minimap.explainer"] = "Blendet den MultiBot-Minimap-Button ein oder aus.", + ["options.layout.lock_mainbar"] = "Bewegung der Hauptleiste sperren", + ["options.layout.lock_mainbar_desc"] = "Aktiviert: Strg + Rechtsklick zum Verschieben der Leiste. Deaktiviert: Rechtsklick genügt.", + ["options.layout.owner_import"] = "Spieler-Layout zum Importieren", + ["options.layout.export"] = "Layout exportieren", + ["options.layout.import"] = "Layout importieren", + ["options.layout.delete"] = "Layout löschen", + ["options.layout.refresh"] = "Liste aktualisieren", + ["options.layout.reset"] = "Layout zurücksetzen", + ["options.layout.none"] = "Kein Layout", + ["options.layout.error_no_layout_to_import"] = "Kein gespeichertes Layout zum Importieren.", + ["options.layout.error_no_layout_to_delete"] = "Kein Layout zum Löschen ausgewählt.", + ["options.layout.error_delete_unavailable"] = "Löschen nicht verfügbar.", + ["options.layout.list_refreshed"] = "Layoutliste aktualisiert.", + ["options.layout.error_reset_unavailable"] = "Zurücksetzen des Layouts nicht verfügbar.", + ["options.layout.error_reset_failed"] = "Zurücksetzen des Layouts fehlgeschlagen.", + ["options.layout.saved"] = "Layout gespeichert: %s", + ["options.layout.error_export_failed"] = "Export fehlgeschlagen: %s", + ["options.layout.imported"] = "Layout importiert: %s (%s Einträge)", + ["options.layout.error_import_failed"] = "Import fehlgeschlagen: %s", + ["options.layout.deleted"] = "Layout gelöscht: %s", + ["options.layout.error_delete_failed"] = "Löschen fehlgeschlagen: %s", + ["options.layout.reset_done"] = "Layout zurückgesetzt (%s Schlüssel).", + ["options.tabs.minimap"] = "Minikarte", + ["options.tabs.layout"] = "Layout", + ["options.tabs.strata"] = "Strata", + ["options.tabs.intervals"] = "Intervalle", + ["mainbar.swap.locked"] = "Leiste gesperrt: Halte Strg + Rechtsklick zum Verschieben.", + ["mainbar.swap.source_prefix"] = "Hauptleiste: Quelle = ", + ["mainbar.swap.cancelled"] = "Hauptleiste: Tausch abgebrochen.", + ["mainbar.swap.preview_prefix"] = "Hauptleiste: ", + ["pvp.stats.error_select_bot"] = "Wähle zuerst ein Bot‑Ziel aus.", + ["pvp.stats.error_not_in_group"] = "Du bist nicht in einer Gruppe.", + ["pvp.stats.error_not_in_raid"] = "Du bist nicht in einem Schlachtzug.", + ["ui.swap.source_prefix"] = "Tauschquelle: ", + ["ui.swap.cancelled"] = "Tausch abgebrochen.", + ["ui.swap.preview_prefix"] = "Tauschvorschau: ", + ["talent.glyphs.waiting"] = "Warte auf Glyphen…", + ["talent.glyphs.debug_prefix"] = "Glyphen‑Payload => ", + ["talent.glyphs.error_ignored_non_glyph"] = "Nicht‑Glyphen‑Nachricht ignoriert", + ["inventory.mode.sell"] = "Verkaufen", + ["spellbook.rank"] = "Rang", + ["spellbook.title"] = "Titel", + ["quests.none"] = "Keine Quests", ["tips.sliders.throttleinstalled"] = "MultiBot-Drossel installiert", ["tips.sliders.frametitle"] = "MultiBot — Optionen", ["tips.sliders.actionsinter"] = "Intervalle für automatische Aktionen", diff --git a/Locales/MultiBotAceLocale-enGB.lua b/Locales/MultiBotAceLocale-enGB.lua index 2dc51ad..596bf43 100644 --- a/Locales/MultiBotAceLocale-enGB.lua +++ b/Locales/MultiBotAceLocale-enGB.lua @@ -194,6 +194,50 @@ local enGBValues = { ["tips.units.inviteRaid25"] = "25-Man Raid Group\n|cffffffffFills your Raid Group with up to 25 PlayerBots.\nPlayerBots are added from the selected Roster, ignoring any Class filters.|r\n\n|cffff0000Left-click to activate.|r\n|cff999999(Executed by: System)|r", ["tips.units.inviteRaid40"] = "40-Man Raid Group\n|cffffffffFills your Raid Group with up to 40 PlayerBots.\nPlayerBots are added from the selected Roster, ignoring any Class filters.|r\n\n|cffff0000Left-click to activate.|r\n|cff999999(Executed by: System)|r", ["tips.units.alliance"] = "Log In/Out All PlayerBots\n|cffffffffLog in/Out all PlayerBots you have access to.\nThis function will take time to populate the ButtonBars for each PlayerBot, depending on the number of PlayerBots total.\n\n|cffff0000Left-click to log in all PlayerBots|r\n|cff999999(Executed by: System)|r\n\n|cffff0000Right-click to log out all PlayerBots|r\n|cff999999(Executed by: System)|r", + ["options.minimap.explainer"] = "Hide or show the MultiBot minimap button.", + ["options.layout.lock_mainbar"] = "Lock main bar movement", + ["options.layout.lock_mainbar_desc"] = "Checked: Ctrl + right-click to move the bar. Unchecked: right-click is enough.", + ["options.layout.owner_import"] = "Player layout to import", + ["options.layout.export"] = "Export layout", + ["options.layout.import"] = "Import layout", + ["options.layout.delete"] = "Delete layout", + ["options.layout.refresh"] = "Refresh list", + ["options.layout.reset"] = "Reset layout", + ["options.layout.none"] = "No layout", + ["options.layout.error_no_layout_to_import"] = "No saved layout to import.", + ["options.layout.error_no_layout_to_delete"] = "No layout selected to delete.", + ["options.layout.error_delete_unavailable"] = "Delete unavailable.", + ["options.layout.list_refreshed"] = "Layout list refreshed.", + ["options.layout.error_reset_unavailable"] = "Layout reset unavailable.", + ["options.layout.error_reset_failed"] = "Layout reset failed.", + ["options.layout.saved"] = "Layout saved: %s", + ["options.layout.error_export_failed"] = "Export failed: %s", + ["options.layout.imported"] = "Layout imported: %s (%s entries)", + ["options.layout.error_import_failed"] = "Import failed: %s", + ["options.layout.deleted"] = "Layout deleted: %s", + ["options.layout.error_delete_failed"] = "Delete failed: %s", + ["options.layout.reset_done"] = "Layout reset (%s keys).", + ["options.tabs.minimap"] = "Minimap", + ["options.tabs.layout"] = "Layout", + ["options.tabs.strata"] = "Strata", + ["options.tabs.intervals"] = "Intervals", + ["mainbar.swap.locked"] = "Bar locked: hold Ctrl + right-click to move.", + ["mainbar.swap.source_prefix"] = "MainBar: source = ", + ["mainbar.swap.cancelled"] = "MainBar: swap cancelled.", + ["mainbar.swap.preview_prefix"] = "MainBar: ", + ["pvp.stats.error_select_bot"] = "Select a bot target first.", + ["pvp.stats.error_not_in_group"] = "You are not in a group.", + ["pvp.stats.error_not_in_raid"] = "You are not in a raid.", + ["ui.swap.source_prefix"] = "Swap source: ", + ["ui.swap.cancelled"] = "Swap cancelled.", + ["ui.swap.preview_prefix"] = "Swap preview: ", + ["talent.glyphs.waiting"] = "Waiting for glyphs…", + ["talent.glyphs.debug_prefix"] = "Glyph payload => ", + ["talent.glyphs.error_ignored_non_glyph"] = "Ignored non-glyphs message", + ["inventory.mode.sell"] = "Sell", + ["spellbook.rank"] = "Rank", + ["spellbook.title"] = "Title", + ["quests.none"] = "No quests", ["tips.sliders.throttleinstalled"] = "MultiBot throttle installed", ["tips.sliders.frametitle"] = "MultiBot — Options", ["tips.sliders.actionsinter"] = "Automatic action intervals", diff --git a/Locales/MultiBotAceLocale-enUS.lua b/Locales/MultiBotAceLocale-enUS.lua index 538e103..93d5525 100644 --- a/Locales/MultiBotAceLocale-enUS.lua +++ b/Locales/MultiBotAceLocale-enUS.lua @@ -194,6 +194,50 @@ local enUSValues = { ["tips.units.inviteRaid25"] = "25-Man Raid Group\n|cffffffffFills your Raid Group with up to 25 PlayerBots.\nPlayerBots are added from the selected Roster, ignoring any Class filters.|r\n\n|cffff0000Left-click to activate.|r\n|cff999999(Executed by: System)|r", ["tips.units.inviteRaid40"] = "40-Man Raid Group\n|cffffffffFills your Raid Group with up to 40 PlayerBots.\nPlayerBots are added from the selected Roster, ignoring any Class filters.|r\n\n|cffff0000Left-click to activate.|r\n|cff999999(Executed by: System)|r", ["tips.units.alliance"] = "Log In/Out All PlayerBots\n|cffffffffLog in/Out all PlayerBots you have access to.\nThis function will take time to populate the ButtonBars for each PlayerBot, depending on the number of PlayerBots total.\n\n|cffff0000Left-click to log in all PlayerBots|r\n|cff999999(Executed by: System)|r\n\n|cffff0000Right-click to log out all PlayerBots|r\n|cff999999(Executed by: System)|r", + ["options.minimap.explainer"] = "Hide or show the MultiBot minimap button.", + ["options.layout.lock_mainbar"] = "Lock main bar movement", + ["options.layout.lock_mainbar_desc"] = "Checked: Ctrl + right-click to move the bar. Unchecked: right-click is enough.", + ["options.layout.owner_import"] = "Player layout to import", + ["options.layout.export"] = "Export layout", + ["options.layout.import"] = "Import layout", + ["options.layout.delete"] = "Delete layout", + ["options.layout.refresh"] = "Refresh list", + ["options.layout.reset"] = "Reset layout", + ["options.layout.none"] = "No layout", + ["options.layout.error_no_layout_to_import"] = "No saved layout to import.", + ["options.layout.error_no_layout_to_delete"] = "No layout selected to delete.", + ["options.layout.error_delete_unavailable"] = "Delete unavailable.", + ["options.layout.list_refreshed"] = "Layout list refreshed.", + ["options.layout.error_reset_unavailable"] = "Layout reset unavailable.", + ["options.layout.error_reset_failed"] = "Layout reset failed.", + ["options.layout.saved"] = "Layout saved: %s", + ["options.layout.error_export_failed"] = "Export failed: %s", + ["options.layout.imported"] = "Layout imported: %s (%s entries)", + ["options.layout.error_import_failed"] = "Import failed: %s", + ["options.layout.deleted"] = "Layout deleted: %s", + ["options.layout.error_delete_failed"] = "Delete failed: %s", + ["options.layout.reset_done"] = "Layout reset (%s keys).", + ["options.tabs.minimap"] = "Minimap", + ["options.tabs.layout"] = "Layout", + ["options.tabs.strata"] = "Strata", + ["options.tabs.intervals"] = "Intervals", + ["mainbar.swap.locked"] = "Bar locked: hold Ctrl + right-click to move.", + ["mainbar.swap.source_prefix"] = "MainBar: source = ", + ["mainbar.swap.cancelled"] = "MainBar: swap cancelled.", + ["mainbar.swap.preview_prefix"] = "MainBar: ", + ["pvp.stats.error_select_bot"] = "Select a bot target first.", + ["pvp.stats.error_not_in_group"] = "You are not in a group.", + ["pvp.stats.error_not_in_raid"] = "You are not in a raid.", + ["ui.swap.source_prefix"] = "Swap source: ", + ["ui.swap.cancelled"] = "Swap cancelled.", + ["ui.swap.preview_prefix"] = "Swap preview: ", + ["talent.glyphs.waiting"] = "Waiting for glyphs…", + ["talent.glyphs.debug_prefix"] = "Glyph payload => ", + ["talent.glyphs.error_ignored_non_glyph"] = "Ignored non-glyphs message", + ["inventory.mode.sell"] = "Sell", + ["spellbook.rank"] = "Rank", + ["spellbook.title"] = "Title", + ["quests.none"] = "No quests", ["tips.sliders.throttleinstalled"] = "MultiBot throttle installed", ["tips.sliders.frametitle"] = "MultiBot — Options", ["tips.sliders.actionsinter"] = "Automatic action intervals", diff --git a/Locales/MultiBotAceLocale-esES.lua b/Locales/MultiBotAceLocale-esES.lua index a24810f..c978d71 100644 --- a/Locales/MultiBotAceLocale-esES.lua +++ b/Locales/MultiBotAceLocale-esES.lua @@ -192,6 +192,50 @@ local esESValues = { ["tips.units.inviteRaid25"] = "Raid of Twenty-Five\n|cffffffffCon este botón puedes llenar tu banda de hasta 25 integrantes.\nEsta función toma unidades de la lista seleccionada, ignorando el filtro de clase.\nSe detiene al final de la lista o cuando la banda llega a 25 miembros.|r\n\n|cffff0000Clic izquierdo para invitar miembros de la banda|r\n|cff999999(Orden de ejecución: Sistema)|r", ["tips.units.inviteRaid40"] = "Raid of Forty\n|cffffffffCon este botón puedes llenar tu banda de hasta 40 integrantes.\nEsta función toma unidades de la lista seleccionada, ignorando el filtro de clase.\nSe detiene al final de la lista o cuando la banda llega a 40 miembros.|r\n\n|cffff0000Clic izquierdo para invitar miembros de la banda|r\n|cff999999(Orden de ejecución: Sistema)|r", ["tips.units.alliance"] = "Iniciar/Cerrar sesión de todos los PlayerBots\n|cffffffffInicia o cierra la sesión de todos los PlayerBots a los que tengas acceso.\nEsta función puede tardar en llenar las barras de botones de cada PlayerBot,\ndependiendo del número total de PlayerBots.\n\n|cffff0000Clic izquierdo para iniciar sesión en todos los PlayerBots|r\n|cff999999(Ejecutado por: Sistema)|r\n\n|cffff0000Clic derecho para cerrar sesión en todos los PlayerBots|r\n|cff999999(Ejecutado por: Sistema)|r", + ["options.minimap.explainer"] = "Muestra u oculta el botón del minimapa de MultiBot.", + ["options.layout.lock_mainbar"] = "Bloquear movimiento de la barra principal", + ["options.layout.lock_mainbar_desc"] = "Marcado: Ctrl + clic derecho para mover la barra. Desmarcado: clic derecho suficiente.", + ["options.layout.owner_import"] = "Layout de jugador para importar", + ["options.layout.export"] = "Exportar diseño", + ["options.layout.import"] = "Importar diseño", + ["options.layout.delete"] = "Eliminar diseño", + ["options.layout.refresh"] = "Actualizar lista", + ["options.layout.reset"] = "Restablecer diseño", + ["options.layout.none"] = "Sin diseño", + ["options.layout.error_no_layout_to_import"] = "No hay ningún diseño guardado para importar.", + ["options.layout.error_no_layout_to_delete"] = "No se ha seleccionado ningún diseño para eliminar.", + ["options.layout.error_delete_unavailable"] = "Eliminación no disponible.", + ["options.layout.list_refreshed"] = "Lista de diseños actualizada.", + ["options.layout.error_reset_unavailable"] = "Restablecimiento del diseño no disponible.", + ["options.layout.error_reset_failed"] = "Error al restablecer el diseño.", + ["options.layout.saved"] = "Diseño guardado: %s", + ["options.layout.error_export_failed"] = "Error al exportar: %s", + ["options.layout.imported"] = "Diseño importado: %s (%s entradas)", + ["options.layout.error_import_failed"] = "Error al importar: %s", + ["options.layout.deleted"] = "Diseño eliminado: %s", + ["options.layout.error_delete_failed"] = "Error al eliminar: %s", + ["options.layout.reset_done"] = "Diseño restablecido (%s claves).", + ["options.tabs.minimap"] = "Minimapa", + ["options.tabs.layout"] = "Diseño", + ["options.tabs.strata"] = "Estratos", + ["options.tabs.intervals"] = "Intervalos", + ["mainbar.swap.locked"] = "Barra bloqueada: mantén Ctrl + clic derecho para mover.", + ["mainbar.swap.source_prefix"] = "Barra principal: origen = ", + ["mainbar.swap.cancelled"] = "Barra principal: intercambio cancelado.", + ["mainbar.swap.preview_prefix"] = "Barra principal: ", + ["pvp.stats.error_select_bot"] = "Selecciona primero un bot como objetivo.", + ["pvp.stats.error_not_in_group"] = "No estás en un grupo.", + ["pvp.stats.error_not_in_raid"] = "No estás en una banda.", + ["ui.swap.source_prefix"] = "Origen del intercambio: ", + ["ui.swap.cancelled"] = "Intercambio cancelado.", + ["ui.swap.preview_prefix"] = "Vista previa del intercambio: ", + ["talent.glyphs.waiting"] = "Esperando glifos…", + ["talent.glyphs.debug_prefix"] = "Carga de glifos => ", + ["talent.glyphs.error_ignored_non_glyph"] = "Mensaje no relacionado con glifos ignorado", + ["inventory.mode.sell"] = "Vender", + ["spellbook.rank"] = "Rango", + ["spellbook.title"] = "Título", + ["quests.none"] = "Sin misiones", ["tips.sliders.throttleinstalled"] = "Limitador de MultiBot instalado", ["tips.sliders.frametitle"] = "MultiBot — Opciones", ["tips.sliders.actionsinter"] = "Intervalos de acciones automáticas", diff --git a/Locales/MultiBotAceLocale-frFR.lua b/Locales/MultiBotAceLocale-frFR.lua index b731e0f..7f58e86 100644 --- a/Locales/MultiBotAceLocale-frFR.lua +++ b/Locales/MultiBotAceLocale-frFR.lua @@ -192,6 +192,50 @@ local frFRValues = { ["tips.units.inviteRaid25"] = "Raid de Vingt-Cinq\n|cffffffffAvec ce bouton, vous pouvez remplir votre Raid.\nCette fonctionnalité prend les unités du Rôle sélectionné en ignorant le filtre de classe.\nElle s'arrête à la fin du Rôle ou jusqu'à ce que le groupe atteigne 25 membres.|r\n\n|cffff0000Clic gauche pour inviter des membres du Raid|r\n|cff999999(Ordre d'exécution : Système)|r", ["tips.units.inviteRaid40"] = "Raid de Quarante\n|cffffffffAvec ce bouton, vous pouvez remplir votre Raid.\nCette fonctionnalité prend les unités du Rôle sélectionné en ignorant le filtre de classe.\nElle s'arrête à la fin du Rôle ou jusqu'à ce que le groupe atteigne 40 membres.|r\n\n|cffff0000Clic gauche pour inviter des membres du Raid|r\n|cff999999(Ordre d'exécution : Système)|r", ["tips.units.alliance"] = "Connexion/Déconnexion de tous les PlayerBots\n|cffffffffConnecte ou déconnecte tous les PlayerBots auxquels vous avez accès.\nCette fonction peut prendre du temps pour remplir les barres de boutons de chaque PlayerBot,\nen fonction du nombre total de PlayerBots.\n\n|cffff0000Clic gauche pour connecter tous les PlayerBots|r\n|cff999999(Exécuté par : Système)|r\n\n|cffff0000Clic droit pour déconnecter tous les PlayerBots|r\n|cff999999(Exécuté par : Système)|r", + ["options.minimap.explainer"] = "Affiche ou masque le bouton minimap de MultiBot.", + ["options.layout.lock_mainbar"] = "Verrouiller déplacement barre principale", + ["options.layout.lock_mainbar_desc"] = "Coché : Ctrl + clic droit pour déplacer la barre. Décoché : clic droit suffit.", + ["options.layout.owner_import"] = "Layout joueur à importer", + ["options.layout.export"] = "Exporter le layout", + ["options.layout.import"] = "Importer le layout", + ["options.layout.delete"] = "Supprimer le layout", + ["options.layout.refresh"] = "Rafraîchir la liste", + ["options.layout.reset"] = "Réinitialiser le layout", + ["options.layout.none"] = "Aucun layout", + ["options.layout.error_no_layout_to_import"] = "Aucun layout enregistré à importer.", + ["options.layout.error_no_layout_to_delete"] = "Aucun layout sélectionné pour la suppression.", + ["options.layout.error_delete_unavailable"] = "Suppression indisponible.", + ["options.layout.list_refreshed"] = "Liste des layouts rafraîchie.", + ["options.layout.error_reset_unavailable"] = "Réinitialisation du layout indisponible.", + ["options.layout.error_reset_failed"] = "Échec de la réinitialisation du layout.", + ["options.layout.saved"] = "Layout enregistré : %s", + ["options.layout.error_export_failed"] = "Échec de l’export : %s", + ["options.layout.imported"] = "Layout importé : %s (%s entrées)", + ["options.layout.error_import_failed"] = "Échec de l’import : %s", + ["options.layout.deleted"] = "Layout supprimé : %s", + ["options.layout.error_delete_failed"] = "Échec de la suppression : %s", + ["options.layout.reset_done"] = "Layout réinitialisé (%s clés).", + ["options.tabs.minimap"] = "Minicarte", + ["options.tabs.layout"] = "Layout", + ["options.tabs.strata"] = "Strates", + ["options.tabs.intervals"] = "Intervalles", + ["mainbar.swap.locked"] = "Barre verrouillée : maintenez Ctrl + clic droit pour déplacer.", + ["mainbar.swap.source_prefix"] = "Barre principale : source = ", + ["mainbar.swap.cancelled"] = "Barre principale : échange annulé.", + ["mainbar.swap.preview_prefix"] = "Barre principale : ", + ["pvp.stats.error_select_bot"] = "Sélectionnez d’abord une cible bot.", + ["pvp.stats.error_not_in_group"] = "Vous n’êtes pas dans un groupe.", + ["pvp.stats.error_not_in_raid"] = "Vous n’êtes pas dans un raid.", + ["ui.swap.source_prefix"] = "Source de l’échange : ", + ["ui.swap.cancelled"] = "Échange annulé.", + ["ui.swap.preview_prefix"] = "Aperçu de l’échange : ", + ["talent.glyphs.waiting"] = "En attente des glyphes…", + ["talent.glyphs.debug_prefix"] = "Payload des glyphes => ", + ["talent.glyphs.error_ignored_non_glyph"] = "Message non‑glyphe ignoré", + ["inventory.mode.sell"] = "Vendre", + ["spellbook.rank"] = "Rang", + ["spellbook.title"] = "Titre", + ["quests.none"] = "Aucune quête", ["tips.sliders.throttleinstalled"] = "Limitation MultiBot installée", ["tips.sliders.frametitle"] = "MultiBot — Options", ["tips.sliders.actionsinter"] = "Intervalles des actions automatiques", diff --git a/Locales/MultiBotAceLocale-koKR.lua b/Locales/MultiBotAceLocale-koKR.lua index 5d43c0e..149ae47 100644 --- a/Locales/MultiBotAceLocale-koKR.lua +++ b/Locales/MultiBotAceLocale-koKR.lua @@ -191,6 +191,50 @@ local koKRValues = { ["tips.units.inviteRaid10"] = "10인 팀\n|cffffffff이 버튼을 사용하여 팀을 구성하세요.\n이 기능은 클래스 필터를 무시하고 선택한 팀 목록에서 유닛을 가져옵니다.\n팀 목록이 끝나거나 팀원이 10명이 되면 멈춥니다.|r\n\n|cffff0000팀원을 초대하려면 왼쪽 클릭|r\n|cff999999(명령 실행: 시스템)|r", ["tips.units.inviteRaid25"] = "25명으로 구성된 팀\n|cffffffff이 버튼을 사용하여 팀을 구성하세요.\n이 기능은 클래스 필터를 무시하고 선택한 팀 목록에서 유닛을 가져옵니다.\n팀 목록이 끝나거나 팀 구성원이 25명이 되면 중단됩니다.|r\n\n|cffff0000팀원을 초대하려면 왼쪽 클릭|r\n|cff999999(명령 실행: 시스템)|r", ["tips.units.alliance"] = "얼라이언스 / 호드\n|cffffffff이 버튼을 사용하면 모든 파티원을 온라인 또는 오프라인 상태로 전환할 수 있습니다.\nMultiBot이 충분히 빠르게 반응하지 못해 모든 봇바를 표시하지 못할 수도 있습니다.\n\n|cffff0000왼쪽 클릭: 모든 파티원을 온라인 상태로 전환|r\n|cff999999(실행 순서: 시스템)|r\n\n|cffff0000오른쪽 클릭: 모든 파티원을 오프라인 상태로 전환|r\n|cff999999(실행 순서: 시스템)|r", + ["options.minimap.explainer"] = "MultiBot 미니맵 버튼을 숨기거나 표시합니다.", + ["options.layout.lock_mainbar"] = "기본 바 이동 잠금", + ["options.layout.lock_mainbar_desc"] = "체크: Ctrl + 우클릭으로 바 이동. 해제: 우클릭만으로 이동.", + ["options.layout.owner_import"] = "가져올 플레이어 레이아웃", + ["options.layout.export"] = "레이아웃 내보내기", + ["options.layout.import"] = "레이아웃 가져오기", + ["options.layout.delete"] = "레이아웃 삭제", + ["options.layout.refresh"] = "목록 새로고침", + ["options.layout.reset"] = "레이아웃 초기화", + ["options.layout.none"] = "레이아웃 없음", + ["options.layout.error_no_layout_to_import"] = "가져올 저장된 레이아웃이 없습니다.", + ["options.layout.error_no_layout_to_delete"] = "삭제할 레이아웃이 선택되지 않았습니다.", + ["options.layout.error_delete_unavailable"] = "삭제할 수 없습니다.", + ["options.layout.list_refreshed"] = "레이아웃 목록이 새로고침되었습니다.", + ["options.layout.error_reset_unavailable"] = "레이아웃 초기화를 사용할 수 없습니다.", + ["options.layout.error_reset_failed"] = "레이아웃 초기화 실패.", + ["options.layout.saved"] = "레이아웃 저장됨: %s", + ["options.layout.error_export_failed"] = "내보내기 실패: %s", + ["options.layout.imported"] = "레이아웃 가져옴: %s (%s개 항목)", + ["options.layout.error_import_failed"] = "가져오기 실패: %s", + ["options.layout.deleted"] = "레이아웃 삭제됨: %s", + ["options.layout.error_delete_failed"] = "삭제 실패: %s", + ["options.layout.reset_done"] = "레이아웃 초기화 완료 (%s개 키).", + ["options.tabs.minimap"] = "미니맵", + ["options.tabs.layout"] = "레이아웃", + ["options.tabs.strata"] = "스트라타", + ["options.tabs.intervals"] = "간격", + ["mainbar.swap.locked"] = "바 잠김: 이동하려면 Ctrl + 오른쪽 클릭을 누르세요.", + ["mainbar.swap.source_prefix"] = "메인바: 소스 = ", + ["mainbar.swap.cancelled"] = "메인바: 교체 취소됨.", + ["mainbar.swap.preview_prefix"] = "메인바: ", + ["pvp.stats.error_select_bot"] = "먼저 봇 대상을 선택하세요.", + ["pvp.stats.error_not_in_group"] = "파티에 속해 있지 않습니다.", + ["pvp.stats.error_not_in_raid"] = "공격대에 속해 있지 않습니다.", + ["ui.swap.source_prefix"] = "교체 소스: ", + ["ui.swap.cancelled"] = "교체 취소됨.", + ["ui.swap.preview_prefix"] = "교체 미리보기: ", + ["talent.glyphs.waiting"] = "문양을 기다리는 중…", + ["talent.glyphs.debug_prefix"] = "문양 페이로드 => ", + ["talent.glyphs.error_ignored_non_glyph"] = "문양이 아닌 메시지 무시됨", + ["inventory.mode.sell"] = "판매", + ["spellbook.rank"] = "등급", + ["spellbook.title"] = "제목", + ["quests.none"] = "퀘스트 없음", ["tips.sliders.throttleinstalled"] = "MultiBot 제한이 설치되었습니다", ["tips.sliders.frametitle"] = "MultiBot — 옵션", ["tips.sliders.actionsinter"] = "자동 작업 간격", diff --git a/Locales/MultiBotAceLocale-ruRU.lua b/Locales/MultiBotAceLocale-ruRU.lua index 954d0d7..b635266 100644 --- a/Locales/MultiBotAceLocale-ruRU.lua +++ b/Locales/MultiBotAceLocale-ruRU.lua @@ -192,6 +192,50 @@ local ruRUValues = { ["tips.units.inviteRaid25"] = "Рейд из 25\n|cffffffffЭтой кнопкой можно заполнить рейд.\nИспользует юнитов из выбранного списка, игнорируя фильтр классов.\nОстанавливается в конце списка или при достижении 25 участников.|r\n\n|cffff0000Левый клик - пригласить участников рейда|r\n|cff999999(Порядок выполнения: Система)|r", ["tips.units.inviteRaid40"] = "Рейд из 40\n|cffffffffЭтой кнопкой можно заполнить рейд.\nИспользует юнитов из выбранного списка, игнорируя фильтр классов.\nОстанавливается в конце списка или при достижении 40 участников.|r\n\n|cffff0000Левый клик - пригласить участников рейда|r\n|cff999999(Порядок выполнения: Система)|r", ["tips.units.alliance"] = "Iniciar/Cerrar sesión de todos los PlayerBots\n|cffffffffInicia o cierra la sesión de todos los PlayerBots a los que tengas acceso.\nEsta función puede tardar en llenar las barras de botones de cada PlayerBot,\ndependiendo del número total de PlayerBots.\n\n|cffff0000Clic izquierdo para iniciar sesión en todos los PlayerBots|r\n|cff999999(Ejecutado por: Sistema)|r\n\n|cffff0000Clic derecho para cerrar sesión en todos los PlayerBots|r\n|cff999999(Ejecutado por: Sistema)|r", + ["options.minimap.explainer"] = "Скрывает или показывает кнопку миникарты MultiBot.", + ["options.layout.lock_mainbar"] = "Заблокировать перемещение главной панели", + ["options.layout.lock_mainbar_desc"] = "Включено: Ctrl + ПКМ для перемещения панели. Выключено: достаточно ПКМ.", + ["options.layout.owner_import"] = "Макет игрока для импорта", + ["options.layout.export"] = "Экспорт макета", + ["options.layout.import"] = "Импорт макета", + ["options.layout.delete"] = "Удалить макет", + ["options.layout.refresh"] = "Обновить список", + ["options.layout.reset"] = "Сбросить макет", + ["options.layout.none"] = "Нет макета", + ["options.layout.error_no_layout_to_import"] = "Нет сохранённого макета для импорта.", + ["options.layout.error_no_layout_to_delete"] = "Не выбран макет для удаления.", + ["options.layout.error_delete_unavailable"] = "Удаление недоступно.", + ["options.layout.list_refreshed"] = "Список макетов обновлён.", + ["options.layout.error_reset_unavailable"] = "Сброс макета недоступен.", + ["options.layout.error_reset_failed"] = "Сбой при сбросе макета.", + ["options.layout.saved"] = "Макет сохранён: %s", + ["options.layout.error_export_failed"] = "Ошибка экспорта: %s", + ["options.layout.imported"] = "Макет импортирован: %s (%s записей)", + ["options.layout.error_import_failed"] = "Ошибка импорта: %s", + ["options.layout.deleted"] = "Макет удалён: %s", + ["options.layout.error_delete_failed"] = "Ошибка удаления: %s", + ["options.layout.reset_done"] = "Макет сброшен (%s ключей).", + ["options.tabs.minimap"] = "Миникарта", + ["options.tabs.layout"] = "Макет", + ["options.tabs.strata"] = "Слои", + ["options.tabs.intervals"] = "Интервалы", + ["mainbar.swap.locked"] = "Панель заблокирована: удерживайте Ctrl + ПКМ для перемещения.", + ["mainbar.swap.source_prefix"] = "Главная панель: источник = ", + ["mainbar.swap.cancelled"] = "Главная панель: обмен отменён.", + ["mainbar.swap.preview_prefix"] = "Главная панель: ", + ["pvp.stats.error_select_bot"] = "Сначала выберите цель‑бота.", + ["pvp.stats.error_not_in_group"] = "Вы не в группе.", + ["pvp.stats.error_not_in_raid"] = "Вы не в рейде.", + ["ui.swap.source_prefix"] = "Источник обмена: ", + ["ui.swap.cancelled"] = "Обмен отменён.", + ["ui.swap.preview_prefix"] = "Предпросмотр обмена: ", + ["talent.glyphs.waiting"] = "Ожидание глифов…", + ["talent.glyphs.debug_prefix"] = "Пакет глифов => ", + ["talent.glyphs.error_ignored_non_glyph"] = "Сообщение не‑глифа проигнорировано", + ["inventory.mode.sell"] = "Продать", + ["spellbook.rank"] = "Уровень", + ["spellbook.title"] = "Название", + ["quests.none"] = "Нет заданий", ["tips.sliders.throttleinstalled"] = "Ограничитель MultiBot установлен", ["tips.sliders.frametitle"] = "MultiBot — Опции", ["tips.sliders.actionsinter"] = "Интервалы автоматических действий", diff --git a/Locales/MultiBotAceLocale-zhCN.lua b/Locales/MultiBotAceLocale-zhCN.lua index cf00cbf..9100c27 100644 --- a/Locales/MultiBotAceLocale-zhCN.lua +++ b/Locales/MultiBotAceLocale-zhCN.lua @@ -192,6 +192,50 @@ local zhCNValues = { ["tips.units.inviteRaid25"] = "二十五人团队\n|cffffffff使用此按钮可以填充你的团队。\n此功能会从选定的团队列表中获取单位,忽略职业筛选器。\n它会在团队列表结束处或队伍达到 25 名成员时停止。|r\n\n|cffff0000左键单击邀请团队成员|r\n|cff999999(执行命令: 系统)|r", ["tips.units.inviteRaid40"] = "四十人团队\n|cffffffff使用此按钮可以填充你的团队。\n此功能会从选定的团队列表中获取单位,忽略职业筛选器。\n它会在团队列表结束处或队伍达到 40 名成员时停止。|r\n\n|cffff0000左键单击邀请团队成员|r\n|cff999999(执行命令: 系统)|r", ["tips.units.alliance"] = "登录/登出所有 PlayerBot\n|cffffffff登录或登出你有权限访问的所有 PlayerBot。\n根据 PlayerBot 的总数量,此功能可能需要一些时间来填充每个 PlayerBot 的按钮栏。\n\n|cffff0000左键点击:登录所有 PlayerBot|r\n|cff999999(执行者:系统)|r\n\n|cffff0000右键点击:登出所有 PlayerBot|r\n|cff999999(执行者:系统)|r", + ["options.minimap.explainer"] = "显示或隐藏 MultiBot 小地图按钮。", + ["options.layout.lock_mainbar"] = "锁定主动作条移动", + ["options.layout.lock_mainbar_desc"] = "勾选:Ctrl + 右键拖动动作条。取消勾选:仅右键即可。", + ["options.layout.owner_import"] = "要导入的玩家布局", + ["options.layout.export"] = "导出布局", + ["options.layout.import"] = "导入布局", + ["options.layout.delete"] = "删除布局", + ["options.layout.refresh"] = "刷新列表", + ["options.layout.reset"] = "重置布局", + ["options.layout.none"] = "无布局", + ["options.layout.error_no_layout_to_import"] = "没有可导入的已保存布局。", + ["options.layout.error_no_layout_to_delete"] = "未选择要删除的布局。", + ["options.layout.error_delete_unavailable"] = "无法删除。", + ["options.layout.list_refreshed"] = "布局列表已刷新。", + ["options.layout.error_reset_unavailable"] = "无法重置布局。", + ["options.layout.error_reset_failed"] = "布局重置失败。", + ["options.layout.saved"] = "布局已保存:%s", + ["options.layout.error_export_failed"] = "导出失败:%s", + ["options.layout.imported"] = "布局已导入:%s(%s 条目)", + ["options.layout.error_import_failed"] = "导入失败:%s", + ["options.layout.deleted"] = "布局已删除:%s", + ["options.layout.error_delete_failed"] = "删除失败:%s", + ["options.layout.reset_done"] = "布局已重置(%s 个键)。", + ["options.tabs.minimap"] = "小地图", + ["options.tabs.layout"] = "布局", + ["options.tabs.strata"] = "层级", + ["options.tabs.intervals"] = "间隔", + ["mainbar.swap.locked"] = "栏已锁定:按住 Ctrl + 右键可移动。", + ["mainbar.swap.source_prefix"] = "主栏:来源 = ", + ["mainbar.swap.cancelled"] = "主栏:交换已取消。", + ["mainbar.swap.preview_prefix"] = "主栏:", + ["pvp.stats.error_select_bot"] = "请先选择一个机器人目标。", + ["pvp.stats.error_not_in_group"] = "你不在队伍中。", + ["pvp.stats.error_not_in_raid"] = "你不在团队中。", + ["ui.swap.source_prefix"] = "交换来源:", + ["ui.swap.cancelled"] = "交换已取消。", + ["ui.swap.preview_prefix"] = "交换预览:", + ["talent.glyphs.waiting"] = "正在等待雕文…", + ["talent.glyphs.debug_prefix"] = "雕文载荷 => ", + ["talent.glyphs.error_ignored_non_glyph"] = "忽略了非雕文消息", + ["inventory.mode.sell"] = "出售", + ["spellbook.rank"] = "等级", + ["spellbook.title"] = "标题", + ["quests.none"] = "无任务", ["tips.sliders.throttleinstalled"] = "已安装 MultiBot 限速", ["tips.sliders.frametitle"] = "MultiBot — 选项", ["tips.sliders.actionsinter"] = "自动操作间隔", diff --git a/UI/MultiBotInventoryFrame.lua b/UI/MultiBotInventoryFrame.lua index 3f575bf..f5bb8ec 100644 --- a/UI/MultiBotInventoryFrame.lua +++ b/UI/MultiBotInventoryFrame.lua @@ -741,7 +741,7 @@ local function createInventoryContent(window) modeValueLabel:SetJustifyH("LEFT") modeValueLabel:SetJustifyV("TOP") modeValueLabel:SetHeight(INVENTORY_WINDOW_DEFAULTS.modeValueHeight) - modeValueLabel:SetText("Sell") + modeValueLabel:SetText(MultiBot.L("inventory.mode.sell")) local helperText = leftPanel:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge") helperText:SetPoint("TOPLEFT", modeValueLabel, "BOTTOMLEFT", 0, -INVENTORY_WINDOW_DEFAULTS.helperTextOffsetY) diff --git a/UI/MultiBotMainUI.lua b/UI/MultiBotMainUI.lua index 526d538..318916b 100644 --- a/UI/MultiBotMainUI.lua +++ b/UI/MultiBotMainUI.lua @@ -413,7 +413,7 @@ function MultiBot.InitializeMainUI(tMultiBar) mainButton:SetScript("OnDragStart", function() if not isMainBarMoveAllowed() then if UIErrorsFrame then - UIErrorsFrame:AddMessage("Barre verrouillée: maintiens Ctrl + clic droit pour déplacer.", 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage(MultiBot.L("mainbar.swap.locked"), 1, 0.25, 0.25, 1) end return end @@ -456,13 +456,13 @@ function MultiBot.InitializeMainUI(tMultiBar) if not selectedSwapButtonName then selectedSwapButtonName = buttonName - UIErrorsFrame:AddMessage("MainBar: source = " .. buttonName, 1, 0.82, 0, 1) + UIErrorsFrame:AddMessage(MultiBot.L("mainbar.swap.source_prefix") .. buttonName, 1, 0.82, 0, 1) return end if selectedSwapButtonName == buttonName then selectedSwapButtonName = nil - UIErrorsFrame:AddMessage("MainBar: permutation annulée.", 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage(MultiBot.L("mainbar.swap.cancelled"), 1, 0.25, 0.25, 1) return end @@ -479,7 +479,7 @@ function MultiBot.InitializeMainUI(tMultiBar) applyMainButtonOrder(mainFrame, currentMainButtonOrder) saveMainButtonOrder(currentMainButtonOrder) - UIErrorsFrame:AddMessage("MainBar: " .. selectedSwapButtonName .. " <-> " .. buttonName, 0.25, 1, 0.25, 1) + UIErrorsFrame:AddMessage(MultiBot.L("mainbar.swap.preview_prefix") .. selectedSwapButtonName .. " <-> " .. buttonName, 0.25, 1, 0.25, 1) selectedSwapButtonName = nil end diff --git a/UI/MultiBotOptions.lua b/UI/MultiBotOptions.lua index 8c86416..ac191a9 100644 --- a/UI/MultiBotOptions.lua +++ b/UI/MultiBotOptions.lua @@ -178,8 +178,8 @@ local function buildLegacyOptionsContent(panel) local chkMainBarMoveLocked = CreateFrame("CheckButton", "MultiBot_MainBarMoveLockedCheck", scrollChild, "InterfaceOptionsCheckButtonTemplate") chkMainBarMoveLocked:SetPoint("TOPLEFT", chkMinimapHide, "BOTTOMLEFT", 0, -8) - _G[chkMainBarMoveLocked:GetName() .. "Text"]:SetText("Verrouiller déplacement barre principale") - chkMainBarMoveLocked.tooltipText = "Cochée: Ctrl + clic droit pour déplacer la barre.\nDécochée: clic droit suffit." + _G[chkMainBarMoveLocked:GetName() .. "Text"]:SetText(optL("options.layout.lock_mainbar")) + chkMainBarMoveLocked.tooltipText = optL("options.layout.lock_mainbar_desc") chkMainBarMoveLocked:SetChecked(mainBarMoveLocked and true or false) chkMainBarMoveLocked:SetScript("OnClick", function(btn) if MultiBot.SetMainBarMoveLocked then @@ -196,19 +196,19 @@ local function buildLegacyOptionsContent(panel) local exportBtn = CreateFrame("Button", nil, scrollChild, "UIPanelButtonTemplate") exportBtn:SetSize(110, 22) exportBtn:SetPoint("TOPLEFT", chkMainBarMoveLocked, "BOTTOMLEFT", 0, -14) - exportBtn:SetText("Export layout") + exportBtn:SetText(optL("options.layout.export")) exportBtn:SetScript("OnClick", function() if MultiBot.SaveMainBarLayoutForCurrentPlayer then local ok, ownerKey, payloadOrError = MultiBot.SaveMainBarLayoutForCurrentPlayer() if UIErrorsFrame then if ok then - UIErrorsFrame:AddMessage(("Layout sauvegardé: %s"):format(ownerKey), 0.25, 1, 0.25, 1) + UIErrorsFrame:AddMessage((optL("options.layout.saved")):format(ownerKey), 0.25, 1, 0.25, 1) selectedOwnerKey = ownerKey if refreshOwnerDropdown then refreshOwnerDropdown() end else - UIErrorsFrame:AddMessage(("Export échoué: %s"):format(tostring(ownerKey or payloadOrError)), 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage((optL("options.layout.error_export_failed")):format(tostring(ownerKey or payloadOrError)), 1, 0.25, 0.25, 1) end end end @@ -217,39 +217,39 @@ local function buildLegacyOptionsContent(panel) local importBtn = CreateFrame("Button", nil, scrollChild, "UIPanelButtonTemplate") importBtn:SetSize(110, 22) importBtn:SetPoint("LEFT", exportBtn, "RIGHT", 8, 0) - importBtn:SetText("Import layout") + importBtn:SetText(optL("options.layout.import")) importBtn:SetScript("OnClick", function() if selectedOwnerKey then local ok, detail = importLayoutOwner(selectedOwnerKey) if UIErrorsFrame then if ok then - UIErrorsFrame:AddMessage(("Layout importé: %s (%s entrées)"):format(selectedOwnerKey, tostring(detail)), 0.25, 1, 0.25, 1) + UIErrorsFrame:AddMessage((optL("options.layout.imported")):format(selectedOwnerKey, tostring(detail)), 0.25, 1, 0.25, 1) else - UIErrorsFrame:AddMessage(("Import échoué: %s"):format(tostring(detail)), 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage((optL("options.layout.error_import_failed")):format(tostring(detail)), 1, 0.25, 0.25, 1) end end return end if UIErrorsFrame then - UIErrorsFrame:AddMessage("Aucun layout enregistré à importer.", 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage(optL("options.layout.error_no_layout_to_import"), 1, 0.25, 0.25, 1) end end) local deleteBtn = CreateFrame("Button", nil, scrollChild, "UIPanelButtonTemplate") deleteBtn:SetSize(110, 22) deleteBtn:SetPoint("TOPLEFT", importBtn, "BOTTOMLEFT", 0, -6) - deleteBtn:SetText("Suppr layout") + deleteBtn:SetText(optL("options.layout.delete")) deleteBtn:SetScript("OnClick", function() if not selectedOwnerKey then if UIErrorsFrame then - UIErrorsFrame:AddMessage("Aucun layout sélectionné à supprimer.", 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage(optL("options.layout.error_no_layout_to_delete"), 1, 0.25, 0.25, 1) end return end if not MultiBot.DeleteSavedMainBarLayout then if UIErrorsFrame then - UIErrorsFrame:AddMessage("Suppression indisponible.", 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage(optL("options.layout.error_delete_unavailable"), 1, 0.25, 0.25, 1) end return end @@ -257,9 +257,9 @@ local function buildLegacyOptionsContent(panel) local ok, detail = MultiBot.DeleteSavedMainBarLayout(selectedOwnerKey) if UIErrorsFrame then if ok then - UIErrorsFrame:AddMessage(("Layout supprimé: %s"):format(selectedOwnerKey), 1, 0.82, 0, 1) + UIErrorsFrame:AddMessage((optL("options.layout.deleted")):format(selectedOwnerKey), 1, 0.82, 0, 1) else - UIErrorsFrame:AddMessage(("Suppression échouée: %s"):format(tostring(detail)), 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage((optL("options.layout.error_delete_failed")):format(tostring(detail)), 1, 0.25, 0.25, 1) end end refreshOwnerDropdown() @@ -268,31 +268,31 @@ local function buildLegacyOptionsContent(panel) local refreshBtn = CreateFrame("Button", nil, scrollChild, "UIPanelButtonTemplate") refreshBtn:SetSize(110, 22) refreshBtn:SetPoint("LEFT", deleteBtn, "RIGHT", 8, 0) - refreshBtn:SetText("Refresh liste") + refreshBtn:SetText(optL("options.layout.refresh")) refreshBtn:SetScript("OnClick", function() refreshOwnerDropdown() if UIErrorsFrame then - UIErrorsFrame:AddMessage("Liste layouts rafraîchie.", 0.25, 1, 0.25, 1) + UIErrorsFrame:AddMessage(optL("options.layout.list_refreshed"), 0.25, 1, 0.25, 1) end end) local resetBtn = CreateFrame("Button", nil, scrollChild, "UIPanelButtonTemplate") resetBtn:SetSize(110, 22) resetBtn:SetPoint("TOPLEFT", refreshBtn, "BOTTOMLEFT", 0, -6) - resetBtn:SetText("Reset layout") + resetBtn:SetText(optL("options.layout.reset")) resetBtn:SetScript("OnClick", function() if not MultiBot.ResetMainBarLayoutState then if UIErrorsFrame then - UIErrorsFrame:AddMessage("Reset layout indisponible.", 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage(optL("options.layout.error_reset_unavailable"), 1, 0.25, 0.25, 1) end return end local ok, removed = MultiBot.ResetMainBarLayoutState() if UIErrorsFrame then if ok then - UIErrorsFrame:AddMessage(("Layout reset (%s clés)."):format(tostring(removed)), 1, 0.82, 0, 1) + UIErrorsFrame:AddMessage((optL("options.layout.reset_done")):format(tostring(removed)), 1, 0.82, 0, 1) else - UIErrorsFrame:AddMessage("Reset layout échoué.", 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage(optL("options.layout.error_reset_failed"), 1, 0.25, 0.25, 1) end end refreshOwnerDropdown() @@ -302,7 +302,7 @@ local function buildLegacyOptionsContent(panel) ownerDropDown:SetPoint("TOPLEFT", exportBtn, "BOTTOMLEFT", -14, -8) local ownerLabel = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormal") ownerLabel:SetPoint("BOTTOMLEFT", ownerDropDown, "TOPLEFT", 16, 3) - ownerLabel:SetText("Layout joueur à importer") + ownerLabel:SetText(optL("options.layout.owner_import")) refreshOwnerDropdown = function() local owners = getSavedLayoutOwners() @@ -331,7 +331,7 @@ local function buildLegacyOptionsContent(panel) UIDropDownMenu_SetText(ownerDropDown, selectedOwnerKey) else selectedOwnerKey = nil - UIDropDownMenu_SetText(ownerDropDown, "Aucun layout") + UIDropDownMenu_SetText(ownerDropDown, optL("options.layout.none")) end end refreshOwnerDropdown() @@ -464,282 +464,373 @@ function MultiBot.BuildOptionsPanel() root.frame:SetPoint("BOTTOMRIGHT", -8, 8) self._aceRoot = root - local scroll = AceGUI:Create("ScrollFrame") - scroll:SetLayout("List") - root:AddChild(scroll) - - local minimapConfig = MultiBot.GetMinimapConfig and MultiBot.GetMinimapConfig() or { hide = false } - local mainBarMoveLocked = MultiBot.GetMainBarMoveLocked and MultiBot.GetMainBarMoveLocked() or true - local chkMinimapHide = AceGUI:Create("CheckBox") - chkMinimapHide:SetLabel(optL("info.buttonoptionshide")) - chkMinimapHide:SetValue(minimapConfig.hide and true or false) - chkMinimapHide:SetFullWidth(true) - chkMinimapHide:SetCallback("OnValueChanged", function(_, _, hide) - if MultiBot.SetMinimapConfig then - MultiBot.SetMinimapConfig("hide", hide and true or false) - end - if MultiBot.Minimap_Refresh then - MultiBot.Minimap_Refresh() - else - local b = _G["MultiBot_MinimapButton"] or MultiBot.MinimapButton - if b then - if hide then b:Hide() else b:Show() end - end - end - end) - scroll:AddChild(chkMinimapHide) - panel.chkMinimapHide = chkMinimapHide - - local chkMainBarMoveLocked = AceGUI:Create("CheckBox") - chkMainBarMoveLocked:SetLabel("Verrouiller déplacement barre principale") - if chkMainBarMoveLocked.SetDescription then - chkMainBarMoveLocked:SetDescription("Cochée: Ctrl + clic droit pour déplacer la barre. Décochée: clic droit suffit.") + local selectedOwnerKey = nil + local strataLevels = { "BACKGROUND", "LOW", "MEDIUM", "HIGH", "DIALOG", "TOOLTIP" } + local minimapHelpText = optL("options.minimap.explainer") + local mainBarMoveLockLabel = optL("options.layout.lock_mainbar") + local mainBarMoveLockDesc = optL("options.layout.lock_mainbar_desc") + local layoutOwnerLabel = optL("options.layout.owner_import") + + local function addTabScroll(tabGroup) + local scroll = AceGUI:Create("ScrollFrame") + scroll:SetLayout("List") + tabGroup:AddChild(scroll) + return scroll end - chkMainBarMoveLocked:SetValue(mainBarMoveLocked and true or false) - chkMainBarMoveLocked:SetFullWidth(true) - chkMainBarMoveLocked:SetCallback("OnValueChanged", function(_, _, value) - if MultiBot.SetMainBarMoveLocked then - MultiBot.SetMainBarMoveLocked(value and true or false) - end - end) - scroll:AddChild(chkMainBarMoveLocked) - panel.chkMainBarMoveLocked = chkMainBarMoveLocked - local layoutActions = AceGUI:Create("SimpleGroup") - layoutActions:SetLayout("Flow") - layoutActions:SetFullWidth(true) + local function buildMinimapTab(tabGroup) + local scroll = addTabScroll(tabGroup) + local minimapConfig = MultiBot.GetMinimapConfig and MultiBot.GetMinimapConfig() or { hide = false } + + local explainer = AceGUI:Create("Label") + explainer:SetFullWidth(true) + explainer:SetText(minimapHelpText) + scroll:AddChild(explainer) + + local explainerSpacer = AceGUI:Create("Label") + explainerSpacer:SetFullWidth(true) + explainerSpacer:SetText(" ") + scroll:AddChild(explainerSpacer) + + local chkMinimapHide = AceGUI:Create("CheckBox") + chkMinimapHide:SetLabel(optL("info.buttonoptionshide")) + chkMinimapHide:SetValue(minimapConfig.hide and true or false) + chkMinimapHide:SetFullWidth(true) + chkMinimapHide:SetCallback("OnValueChanged", function(_, _, hide) + if MultiBot.SetMinimapConfig then + MultiBot.SetMinimapConfig("hide", hide and true or false) + end + if MultiBot.Minimap_Refresh then + MultiBot.Minimap_Refresh() + else + local b = _G["MultiBot_MinimapButton"] or MultiBot.MinimapButton + if b then + if hide then b:Hide() else b:Show() end + end + end + end) + scroll:AddChild(chkMinimapHide) + panel.chkMinimapHide = chkMinimapHide + end - local selectedOwnerKey = nil + local function buildLayoutTab(tabGroup) + local scroll = addTabScroll(tabGroup) + local mainBarMoveLocked = MultiBot.GetMainBarMoveLocked and MultiBot.GetMainBarMoveLocked() or true - local ownerDropDown = AceGUI:Create("Dropdown") - ownerDropDown:SetLabel("Layout joueur à importer") - ownerDropDown:SetWidth(280) - local function refreshOwnerList() - local owners = getSavedLayoutOwners() - local options = {} - for _, owner in ipairs(owners) do - options[owner] = owner + local chkMainBarMoveLocked = AceGUI:Create("CheckBox") + chkMainBarMoveLocked:SetLabel(mainBarMoveLockLabel) + if chkMainBarMoveLocked.SetDescription then + chkMainBarMoveLocked:SetDescription(mainBarMoveLockDesc) end - ownerDropDown:SetList(options) - if #owners > 0 then - selectedOwnerKey = selectedOwnerKey or owners[1] + chkMainBarMoveLocked:SetValue(mainBarMoveLocked and true or false) + chkMainBarMoveLocked:SetFullWidth(true) + chkMainBarMoveLocked:SetCallback("OnValueChanged", function(_, _, value) + if MultiBot.SetMainBarMoveLocked then + MultiBot.SetMainBarMoveLocked(value and true or false) + end + end) + scroll:AddChild(chkMainBarMoveLocked) + panel.chkMainBarMoveLocked = chkMainBarMoveLocked + + local ownerTitle = AceGUI:Create("Label") + ownerTitle:SetFullWidth(true) + ownerTitle:SetText(layoutOwnerLabel) + scroll:AddChild(ownerTitle) + + local ownerTopSpacer = AceGUI:Create("Label") + ownerTopSpacer:SetFullWidth(true) + ownerTopSpacer:SetText(" ") + scroll:AddChild(ownerTopSpacer) + + local ownerDropDown = AceGUI:Create("Dropdown") + ownerDropDown:SetLabel(" ") + ownerDropDown:SetWidth(320) + ownerDropDown:SetCallback("OnValueChanged", function(_, _, value) + selectedOwnerKey = value + end) + + local function refreshOwnerList() + local owners = getSavedLayoutOwners() + local options = {} + for _, owner in ipairs(owners) do + options[owner] = owner + end + ownerDropDown:SetList(options) + if #owners == 0 then + selectedOwnerKey = nil + ownerDropDown:SetValue(nil) + return + end + if not selectedOwnerKey or not options[selectedOwnerKey] then + selectedOwnerKey = owners[1] + end ownerDropDown:SetValue(selectedOwnerKey) - else - selectedOwnerKey = nil - ownerDropDown:SetValue(nil) end - end - ownerDropDown:SetCallback("OnValueChanged", function(_, _, value) - selectedOwnerKey = value - end) - refreshOwnerList() - layoutActions:AddChild(ownerDropDown) - - local exportBtn = AceGUI:Create("Button") - exportBtn:SetText("Export layout") - exportBtn:SetWidth(150) - exportBtn:SetCallback("OnClick", function() - if MultiBot.SaveMainBarLayoutForCurrentPlayer then - local ok, ownerKey = MultiBot.SaveMainBarLayoutForCurrentPlayer() + + refreshOwnerList() + scroll:AddChild(ownerDropDown) + + local ownerBottomSpacer = AceGUI:Create("Label") + ownerBottomSpacer:SetFullWidth(true) + ownerBottomSpacer:SetText(" ") + scroll:AddChild(ownerBottomSpacer) + + local layoutActions = AceGUI:Create("SimpleGroup") + layoutActions:SetLayout("Flow") + layoutActions:SetFullWidth(true) + + local exportBtn = AceGUI:Create("Button") + exportBtn:SetText(optL("options.layout.export")) + exportBtn:SetWidth(150) + exportBtn:SetCallback("OnClick", function() + if MultiBot.SaveMainBarLayoutForCurrentPlayer then + local ok, ownerKey = MultiBot.SaveMainBarLayoutForCurrentPlayer() + if UIErrorsFrame then + if ok then + UIErrorsFrame:AddMessage((optL("options.layout.saved")):format(ownerKey), 0.25, 1, 0.25, 1) + else + UIErrorsFrame:AddMessage((optL("options.layout.error_export_failed")):format(tostring(ownerKey)), 1, 0.25, 0.25, 1) + end + end + end + refreshOwnerList() + end) + layoutActions:AddChild(exportBtn) + + local importBtn = AceGUI:Create("Button") + importBtn:SetText(optL("options.layout.import")) + importBtn:SetWidth(150) + importBtn:SetCallback("OnClick", function() + if selectedOwnerKey then + local ok, detail = importLayoutOwner(selectedOwnerKey) + if UIErrorsFrame then + if ok then + UIErrorsFrame:AddMessage((optL("options.layout.imported")):format(selectedOwnerKey, tostring(detail)), 0.25, 1, 0.25, 1) + else + UIErrorsFrame:AddMessage((optL("options.layout.error_import_failed")):format(tostring(detail)), 1, 0.25, 0.25, 1) + end + end + return + end if UIErrorsFrame then - if ok then - UIErrorsFrame:AddMessage(("Layout sauvegardé: %s"):format(ownerKey), 0.25, 1, 0.25, 1) - else - UIErrorsFrame:AddMessage(("Export échoué: %s"):format(tostring(ownerKey)), 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage(optL("options.layout.error_no_layout_to_import"), 1, 0.25, 0.25, 1) + end + end) + layoutActions:AddChild(importBtn) + + local deleteBtn = AceGUI:Create("Button") + deleteBtn:SetText(optL("options.layout.delete")) + deleteBtn:SetWidth(150) + deleteBtn:SetCallback("OnClick", function() + if not selectedOwnerKey then + if UIErrorsFrame then + UIErrorsFrame:AddMessage(optL("options.layout.error_no_layout_to_delete"), 1, 0.25, 0.25, 1) end + return end - end - refreshOwnerList() - end) - layoutActions:AddChild(exportBtn) - - local importBtn = AceGUI:Create("Button") - importBtn:SetText("Import layout") - importBtn:SetWidth(150) - importBtn:SetCallback("OnClick", function() - if selectedOwnerKey then - local ok, detail = importLayoutOwner(selectedOwnerKey) + if not MultiBot.DeleteSavedMainBarLayout then + if UIErrorsFrame then + UIErrorsFrame:AddMessage(optL("options.layout.error_delete_unavailable"), 1, 0.25, 0.25, 1) + end + return + end + local ok, detail = MultiBot.DeleteSavedMainBarLayout(selectedOwnerKey) if UIErrorsFrame then if ok then - UIErrorsFrame:AddMessage(("Layout importé: %s (%s entrées)"):format(selectedOwnerKey, tostring(detail)), 0.25, 1, 0.25, 1) + UIErrorsFrame:AddMessage((optL("options.layout.deleted")):format(selectedOwnerKey), 1, 0.82, 0, 1) else - UIErrorsFrame:AddMessage(("Import échoué: %s"):format(tostring(detail)), 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage((optL("options.layout.error_delete_failed")):format(tostring(detail)), 1, 0.25, 0.25, 1) end end - return - end - if UIErrorsFrame then - UIErrorsFrame:AddMessage("Aucun layout enregistré à importer.", 1, 0.25, 0.25, 1) - end - end) - layoutActions:AddChild(importBtn) + refreshOwnerList() + end) + layoutActions:AddChild(deleteBtn) - local deleteBtn = AceGUI:Create("Button") - deleteBtn:SetText("Suppr layout") - deleteBtn:SetWidth(150) - deleteBtn:SetCallback("OnClick", function() - if not selectedOwnerKey then + local refreshBtn = AceGUI:Create("Button") + refreshBtn:SetText(optL("options.layout.refresh")) + refreshBtn:SetWidth(150) + refreshBtn:SetCallback("OnClick", function() + refreshOwnerList() if UIErrorsFrame then - UIErrorsFrame:AddMessage("Aucun layout sélectionné à supprimer.", 1, 0.25, 0.25, 1) + UIErrorsFrame:AddMessage(optL("options.layout.list_refreshed"), 0.25, 1, 0.25, 1) end - return - end - if not MultiBot.DeleteSavedMainBarLayout then + end) + layoutActions:AddChild(refreshBtn) + + local resetBtn = AceGUI:Create("Button") + resetBtn:SetText(optL("options.layout.reset")) + resetBtn:SetWidth(150) + resetBtn:SetCallback("OnClick", function() + if not MultiBot.ResetMainBarLayoutState then + if UIErrorsFrame then + UIErrorsFrame:AddMessage(optL("options.layout.error_reset_unavailable"), 1, 0.25, 0.25, 1) + end + return + end + local ok, removed = MultiBot.ResetMainBarLayoutState() if UIErrorsFrame then - UIErrorsFrame:AddMessage("Suppression indisponible.", 1, 0.25, 0.25, 1) + if ok then + UIErrorsFrame:AddMessage((optL("options.layout.reset_done")):format(tostring(removed)), 1, 0.82, 0, 1) + else + UIErrorsFrame:AddMessage(optL("options.layout.error_reset_failed"), 1, 0.25, 0.25, 1) + end end - return + refreshOwnerList() + end) + layoutActions:AddChild(resetBtn) + scroll:AddChild(layoutActions) + end + + local function buildStrataTab(tabGroup) + local scroll = addTabScroll(tabGroup) + local strataTitle = AceGUI:Create("Label") + strataTitle:SetFullWidth(true) + strataTitle:SetText(MultiBot.L("options.frame_strata")) + scroll:AddChild(strataTitle) + + local strataSpacer = AceGUI:Create("Label") + strataSpacer:SetFullWidth(true) + strataSpacer:SetText(" ") + scroll:AddChild(strataSpacer) + + local strata = AceGUI:Create("Dropdown") + strata:SetLabel(" ") + strata:SetWidth(240) + local strataList = {} + for _, strataLevel in ipairs(strataLevels) do + strataList[strataLevel] = strataLevel end - local ok, detail = MultiBot.DeleteSavedMainBarLayout(selectedOwnerKey) - if UIErrorsFrame then - if ok then - UIErrorsFrame:AddMessage(("Layout supprimé: %s"):format(selectedOwnerKey), 1, 0.82, 0, 1) - else - UIErrorsFrame:AddMessage(("Suppression échouée: %s"):format(tostring(detail)), 1, 0.25, 0.25, 1) + strata:SetList(strataList) + strata:SetValue((MultiBot.GetGlobalStrataLevel and MultiBot.GetGlobalStrataLevel()) or "HIGH") + strata:SetCallback("OnValueChanged", function(_, _, value) + if MultiBot.SetGlobalStrataLevel then + MultiBot.SetGlobalStrataLevel(value) end - end - refreshOwnerList() - end) - layoutActions:AddChild(deleteBtn) + if MultiBot.ApplyGlobalStrata then + MultiBot.ApplyGlobalStrata() + end + end) + scroll:AddChild(strata) + end - local refreshBtn = AceGUI:Create("Button") - refreshBtn:SetText("Refresh liste") - refreshBtn:SetWidth(150) - refreshBtn:SetCallback("OnClick", function() - refreshOwnerList() - if UIErrorsFrame then - UIErrorsFrame:AddMessage("Liste layouts rafraîchie.", 0.25, 1, 0.25, 1) + local function buildIntervalsTab(tabGroup) + local scroll = addTabScroll(tabGroup) + + local sub = AceGUI:Create("Label") + sub:SetText(optL("tips.sliders.actionsinter")) + sub:SetFullWidth(true) + scroll:AddChild(sub) + + local intervalsTopSpacer = AceGUI:Create("Label") + intervalsTopSpacer:SetFullWidth(true) + intervalsTopSpacer:SetText(" ") + scroll:AddChild(intervalsTopSpacer) + + local sliderRefs = {} + + local function buildTimerSlider(key, label, minV, maxV, step) + local slider = AceGUI:Create("Slider") + slider:SetFullWidth(true) + slider:SetSliderValues(minV, maxV, step) + slider:SetLabel(label) + slider:SetCallback("OnValueChanged", function(widget, _, value) + value = round(value, step) + MultiBot.SetTimer(key, value) + widget:SetLabel(formatSliderLabel(label, secondsLabel(value))) + widget:SetValue(value) + end) + slider._refresh = function() + local value = MultiBot.GetTimer(key) + slider:SetValue(value) + slider:SetLabel(formatSliderLabel(label, secondsLabel(value))) + end + sliderRefs[#sliderRefs + 1] = slider + scroll:AddChild(slider) + return slider end - end) - layoutActions:AddChild(refreshBtn) - local resetBtn = AceGUI:Create("Button") - resetBtn:SetText("Reset layout") - resetBtn:SetWidth(150) - resetBtn:SetCallback("OnClick", function() - if not MultiBot.ResetMainBarLayoutState then - if UIErrorsFrame then - UIErrorsFrame:AddMessage("Reset layout indisponible.", 1, 0.25, 0.25, 1) + local function buildThrottleSlider(key, label, minV, maxV, step) + local getValue = (key == "thr_rate") and MultiBot.GetThrottleRate or MultiBot.GetThrottleBurst + local setValue = (key == "thr_rate") and MultiBot.SetThrottleRate or MultiBot.SetThrottleBurst + local slider = AceGUI:Create("Slider") + slider:SetFullWidth(true) + slider:SetSliderValues(minV, maxV, step) + slider:SetLabel(label) + slider:SetCallback("OnValueChanged", function(widget, _, value) + value = round(value, step) + setValue(value) + widget:SetLabel(formatSliderLabel(label, tostring(value))) + widget:SetValue(value) + end) + slider._refresh = function() + local value = getValue() + slider:SetValue(value) + slider:SetLabel(formatSliderLabel(label, tostring(value))) end - return + sliderRefs[#sliderRefs + 1] = slider + scroll:AddChild(slider) + return slider end - local ok, removed = MultiBot.ResetMainBarLayoutState() - if UIErrorsFrame then - if ok then - UIErrorsFrame:AddMessage(("Layout reset (%s clés)."):format(tostring(removed)), 1, 0.82, 0, 1) - else - UIErrorsFrame:AddMessage("Reset layout échoué.", 1, 0.25, 0.25, 1) + + local s_stats = buildTimerSlider("stats", optL("tips.sliders.statsinter"), 5, 300, 1) + local s_talent = buildTimerSlider("talent", optL("tips.sliders.talentsinter"), 1, 30, 0.5) + local s_invite = buildTimerSlider("invite", optL("tips.sliders.invitsinter"), 1, 60, 1) + local s_sort = buildTimerSlider("sort", optL("tips.sliders.sortinter"), 0.2, 10, 0.2) + local s_thr_rate = buildThrottleSlider("thr_rate", optL("tips.sliders.messpersec"), 1, 20, 1) + local s_thr_burst = buildThrottleSlider("thr_burst", optL("tips.sliders.maxburst"), 1, 50, 1) + + local btn = AceGUI:Create("Button") + btn:SetText(optL("tips.sliders.rstbutn")) + btn:SetWidth(180) + btn:SetCallback("OnClick", function() + MultiBot.SetTimer("stats", 45) + MultiBot.SetTimer("talent", 3) + MultiBot.SetTimer("invite", 5) + MultiBot.SetTimer("sort", 1) + MultiBot.SetThrottleRate(5) + MultiBot.SetThrottleBurst(8) + for _, slider in ipairs(sliderRefs) do + slider._refresh() end - end - refreshOwnerList() - end) - layoutActions:AddChild(resetBtn) + end) - scroll:AddChild(layoutActions) + local resetTopSpacer = AceGUI:Create("Label") + resetTopSpacer:SetFullWidth(true) + resetTopSpacer:SetText(" ") + scroll:AddChild(resetTopSpacer) - local strata = AceGUI:Create("Dropdown") - strata:SetLabel(MultiBot.L("options.frame_strata")) - strata:SetWidth(240) - local strataLevels = { "BACKGROUND", "LOW", "MEDIUM", "HIGH", "DIALOG", "TOOLTIP" } - local strataList = {} - for _, v in ipairs(strataLevels) do strataList[v] = v end - strata:SetList(strataList) - strata:SetValue((MultiBot.GetGlobalStrataLevel and MultiBot.GetGlobalStrataLevel()) or "HIGH") - strata:SetCallback("OnValueChanged", function(_, _, value) - if MultiBot.SetGlobalStrataLevel then - MultiBot.SetGlobalStrataLevel(value) - end - if MultiBot.ApplyGlobalStrata then - MultiBot.ApplyGlobalStrata() - end - end) - scroll:AddChild(strata) - - local spacer = AceGUI:Create("Label") - spacer:SetText(" ") - spacer:SetFullWidth(true) - scroll:AddChild(spacer) - - local sub = AceGUI:Create("Label") - sub:SetText(optL("tips.sliders.actionsinter")) - sub:SetFullWidth(true) - scroll:AddChild(sub) - - local sliderRefs = {} - - local function buildTimerSlider(key, label, minV, maxV, step) - local slider = AceGUI:Create("Slider") - slider:SetFullWidth(true) - slider:SetSliderValues(minV, maxV, step) - slider:SetLabel(label) - slider:SetCallback("OnValueChanged", function(widget, _, value) - value = round(value, step) - MultiBot.SetTimer(key, value) - widget:SetLabel(formatSliderLabel(label, secondsLabel(value))) - widget:SetValue(value) - end) - slider._refresh = function() - local value = MultiBot.GetTimer(key) - slider:SetValue(value) - slider:SetLabel(formatSliderLabel(label, secondsLabel(value))) - end - sliderRefs[#sliderRefs + 1] = slider - scroll:AddChild(slider) - return slider - end + scroll:AddChild(btn) - local function buildThrottleSlider(key, label, minV, maxV, step) - local getValue = (key == "thr_rate") and MultiBot.GetThrottleRate or MultiBot.GetThrottleBurst - local setValue = (key == "thr_rate") and MultiBot.SetThrottleRate or MultiBot.SetThrottleBurst - local slider = AceGUI:Create("Slider") - slider:SetFullWidth(true) - slider:SetSliderValues(minV, maxV, step) - slider:SetLabel(label) - slider:SetCallback("OnValueChanged", function(widget, _, value) - value = round(value, step) - setValue(value) - widget:SetLabel(formatSliderLabel(label, tostring(value))) - widget:SetValue(value) - end) - slider._refresh = function() - local value = getValue() - slider:SetValue(value) - slider:SetLabel(formatSliderLabel(label, tostring(value))) - end - sliderRefs[#sliderRefs + 1] = slider - scroll:AddChild(slider) - return slider + s_stats._refresh() + s_talent._refresh() + s_invite._refresh() + s_sort._refresh() + s_thr_rate._refresh() + s_thr_burst._refresh() end - local s_stats = buildTimerSlider("stats", optL("tips.sliders.statsinter"), 5, 300, 1) - local s_talent = buildTimerSlider("talent", optL("tips.sliders.talentsinter"), 1, 30, 0.5) - local s_invite = buildTimerSlider("invite", optL("tips.sliders.invitsinter"), 1, 60, 1) - local s_sort = buildTimerSlider("sort", optL("tips.sliders.sortinter"), 0.2, 10, 0.2) - local s_thr_rate = buildThrottleSlider("thr_rate", optL("tips.sliders.messpersec"), 1, 20, 1) - local s_thr_burst = buildThrottleSlider("thr_burst", optL("tips.sliders.maxburst"), 1, 50, 1) - - local btn = AceGUI:Create("Button") - btn:SetText(optL("tips.sliders.rstbutn")) - btn:SetWidth(180) - btn:SetCallback("OnClick", function() - MultiBot.SetTimer("stats", 45) - MultiBot.SetTimer("talent", 3) - MultiBot.SetTimer("invite", 5) - MultiBot.SetTimer("sort", 1) - MultiBot.SetThrottleRate(5) - MultiBot.SetThrottleBurst(8) - for _, slider in ipairs(sliderRefs) do - slider._refresh() + local tabGroup = AceGUI:Create("TabGroup") + tabGroup:SetLayout("Fill") + tabGroup:SetTabs({ + { text = optL("options.tabs.minimap"), value = "minimap" }, + { text = optL("options.tabs.layout"), value = "layout" }, + { text = optL("options.tabs.strata"), value = "strata" }, + { text = optL("options.tabs.intervals"), value = "intervals" }, + }) + tabGroup:SetCallback("OnGroupSelected", function(widget, _, group) + widget:ReleaseChildren() + if group == "minimap" then + buildMinimapTab(widget) + elseif group == "layout" then + buildLayoutTab(widget) + elseif group == "strata" then + buildStrataTab(widget) + elseif group == "intervals" then + buildIntervalsTab(widget) end end) - scroll:AddChild(btn) - - s_stats._refresh() - s_talent._refresh() - s_invite._refresh() - s_sort._refresh() - s_thr_rate._refresh() - s_thr_burst._refresh() + root:AddChild(tabGroup) + tabGroup:SelectTab("minimap") end) if type(InterfaceOptions_AddCategory) == "function" then diff --git a/UI/MultiBotQuestAllFrame.lua b/UI/MultiBotQuestAllFrame.lua index d2e10c1..da7631d 100644 --- a/UI/MultiBotQuestAllFrame.lua +++ b/UI/MultiBotQuestAllFrame.lua @@ -75,7 +75,7 @@ end local function createEmptySectionHint(self, text) local hint = self.aceGUI:Create("Label") hint:SetFullWidth(true) - hint:SetText(" " .. (text or MultiBot.L("tips.quests.gobnosearchdata") or "No quests")) + hint:SetText(" " .. (text or MultiBot.L("tips.quests.gobnosearchdata") or MultiBot.L("quests.none"))) self.scroll:AddChild(hint) end diff --git a/UI/MultiBotSpecUI.lua b/UI/MultiBotSpecUI.lua index e707a2b..95e01fb 100644 --- a/UI/MultiBotSpecUI.lua +++ b/UI/MultiBotSpecUI.lua @@ -526,9 +526,8 @@ local function getAceGUI() end local function debugSpecPath(path) - if MultiBot and MultiBot.Debug and type(MultiBot.Debug.Once) == "function" then - --MultiBot.Debug.Once("spec.dropdown.path", "MultiBot Spec: using " .. tostring(path) .. " path", "33ccff") - end + -- Debug volontairement désactivé : helper conservé pour ne pas toucher les callsites. + return path end local function disableSetTalentsToggle(wrapper) diff --git a/UI/MultiBotSpellBookFrame.lua b/UI/MultiBotSpellBookFrame.lua index 6cb5c0e..0857f40 100644 --- a/UI/MultiBotSpellBookFrame.lua +++ b/UI/MultiBotSpellBookFrame.lua @@ -347,13 +347,13 @@ local function createSpellbookContent(window) local rank = textLayer:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall") rank:SetPoint("TOPLEFT", textLayer, "TOPLEFT", tRankX, tRankY) rank:SetDrawLayer("OVERLAY", getSafeTextDrawSubLevel()) - rank:SetText("|cff" .. (getSpellBookUI().RANK_TEXT_COLOR_HEX or "ffcc00") .. "Rank|r") + rank:SetText("|cff" .. (getSpellBookUI().RANK_TEXT_COLOR_HEX or "ffcc00") .. MultiBot.L("spellbook.rank") .. "|r") tOverlay.texts["R" .. tIndex] = rank local titleText = textLayer:CreateFontString(nil, "OVERLAY", "GameFontHighlight") titleText:SetPoint("TOPLEFT", textLayer, "TOPLEFT", tTitleX, tTextY) titleText:SetDrawLayer("OVERLAY", getSafeTextDrawSubLevel()) - titleText:SetText("|cffffcc00Title|r") + titleText:SetText("|cffffcc00" .. MultiBot.L("spellbook.title") .. "|r") titleText:Hide() tOverlay.texts["T" .. tIndex] = titleText diff --git a/UI/MultiBotTalentFrame.lua b/UI/MultiBotTalentFrame.lua index 89a9ce5..a8095de 100644 --- a/UI/MultiBotTalentFrame.lua +++ b/UI/MultiBotTalentFrame.lua @@ -121,7 +121,7 @@ function MultiBot.InitializeTalentFrameModule() return end - if not MultiBot.talent then + if not MultiBot.talent or type(MultiBot.talent.addFrame) ~= "function" then MultiBot.TalentHostContentLayout = MultiBot.TalentHostContentLayout or DEFAULT_TALENT_HOST_CONTENT_LAYOUT local layout = MultiBot.TalentHostContentLayout @@ -996,7 +996,7 @@ function MultiBot.InitializeTalentFrameModule() local rec = MultiBot.receivedGlyphs and MultiBot.receivedGlyphs[botName] if not rec then - DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MultiBot]|r Waiting for glyphs…") + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MultiBot]|r " .. MultiBot.L("talent.glyphs.waiting")) return end @@ -1080,7 +1080,7 @@ function MultiBot.InitializeTalentFrameModule() ids[i] = (socket and socket.item) or 0 end local payload = "glyph equip " .. table.concat(ids, " ") - DEFAULT_CHAT_FRAME:AddMessage("|cff66ccff[DBG]|r " .. + DEFAULT_CHAT_FRAME:AddMessage("|cff66ccff[DBG]|r " .. MultiBot.L("talent.glyphs.debug_prefix") .. (MultiBot.talent.name or "?") .. " : " .. payload) SendChatMessage(payload, "WHISPER", nil, MultiBot.talent.name) end diff --git a/UI/MultiBotUnitsRootUI.lua b/UI/MultiBotUnitsRootUI.lua index b4ceedc..b48475c 100644 --- a/UI/MultiBotUnitsRootUI.lua +++ b/UI/MultiBotUnitsRootUI.lua @@ -489,7 +489,7 @@ local function createPvpStatsControls(controlFrame) whisperButton.doLeft = function() local bot = UnitName("target") if not bot or not UnitIsPlayer("target") then - UIErrorsFrame:AddMessage("Sélectionne un bot (cible) d'abord.", 1, 0.2, 0.2, 1) + UIErrorsFrame:AddMessage(MultiBot.L("pvp.stats.error_select_bot"), 1, 0.2, 0.2, 1) return end SendChatMessage("pvp stats", "WHISPER", nil, bot) @@ -498,7 +498,7 @@ local function createPvpStatsControls(controlFrame) partyButton.doLeft = function() if GetNumPartyMembers() == 0 and GetNumRaidMembers() == 0 then - UIErrorsFrame:AddMessage("Tu n'es pas en groupe.", 1, 0.2, 0.2, 1) + UIErrorsFrame:AddMessage(MultiBot.L("pvp.stats.error_not_in_group"), 1, 0.2, 0.2, 1) return end SendChatMessage("pvp stats", "PARTY") @@ -507,7 +507,7 @@ local function createPvpStatsControls(controlFrame) raidButton.doLeft = function() if GetNumRaidMembers() == 0 then - UIErrorsFrame:AddMessage("Tu n'es pas en raid.", 1, 0.2, 0.2, 1) + UIErrorsFrame:AddMessage(MultiBot.L("pvp.stats.error_not_in_raid"), 1, 0.2, 0.2, 1) return end SendChatMessage("pvp stats", "RAID")