diff --git a/locale/de.js b/locale/de.js index 8f56395d..2ac11d65 100644 --- a/locale/de.js +++ b/locale/de.js @@ -1082,6 +1082,8 @@ export default { select_button_ui: 'Objekt auswählen', duplicate_button_ui: 'Duplizieren', delete_button_ui: 'Löschen', + delete_block_button_ui: 'Block löschen', + duplicate_block_button_ui: 'Block duplizieren', camera_button_ui: 'Kamera-Steuerung', eye_button_ui: 'Um ausgewähltes Objekt kreisen', info_panel_link_ui: 'Flock XR Webseite (neuer Tab)', @@ -1109,6 +1111,12 @@ export default { context_expand_all_option: 'Alle ausklappen', context_disable_option: 'Deaktivieren', context_enable_option: 'Aktivieren', + add_comment: 'Kommentar hinzufügen', + delete_comment: 'Kommentar löschen', + view_in_canvas: 'Im Canvas anzeigen', + exit_canvas_view: 'Umkreisen stoppen', + detach_block_option: 'Trennen', + view_in_canvas_option: 'Im Canvas anzeigen', context_copy_option: 'Kopieren', context_paste_option: 'Einfügen', context_cut_option: 'Ausschneiden', diff --git a/locale/en.js b/locale/en.js index 63480c98..ecfa03c3 100644 --- a/locale/en.js +++ b/locale/en.js @@ -1140,6 +1140,8 @@ export default { select_button_ui: 'Select object', duplicate_button_ui: 'Duplicate selected object', delete_button_ui: 'Delete selected object', + delete_block_button_ui: 'Delete block', + duplicate_block_button_ui: 'Duplicate block', camera_button_ui: 'Camera controls', eye_button_ui: 'Orbit around selected object', @@ -1185,6 +1187,12 @@ export default { context_expand_all_option: 'Expand all', context_disable_option: 'Disable', context_enable_option: 'Enable', + add_comment: 'Add comment', + delete_comment: 'Delete comment', + view_in_canvas: 'View in canvas', + exit_canvas_view: 'Stop orbiting object', + detach_block_option: 'Detach', + view_in_canvas_option: 'View in canvas', context_copy_option: 'Copy', context_paste_option: 'Paste', context_cut_option: 'Cut', diff --git a/locale/es.js b/locale/es.js index aa9a06de..46827731 100644 --- a/locale/es.js +++ b/locale/es.js @@ -1132,6 +1132,8 @@ export default { select_button_ui: 'Seleccionar objeto', // human duplicate_button_ui: 'Duplicar objeto seleccionado', // human delete_button_ui: 'Eliminar objeto seleccionado', // human + delete_block_button_ui: 'Eliminar bloque', + duplicate_block_button_ui: 'Duplicar bloque', camera_button_ui: 'Controles de cámara', // human eye_button_ui: 'Orbitar alrededor del objeto seleccionado', @@ -1178,6 +1180,12 @@ export default { context_expand_all_option: 'Expandir todo', context_disable_option: 'Deshabilitar', context_enable_option: 'Habilitar', + add_comment: 'Añadir comentario', + delete_comment: 'Eliminar comentario', + view_in_canvas: 'Ver en el lienzo', + exit_canvas_view: 'Dejar de orbitar el objeto', + detach_block_option: 'Desconectar', + view_in_canvas_option: 'Ver en el lienzo', context_copy_option: 'Copiar', // human context_paste_option: 'Pegar', // human context_cut_option: 'Cortar', // human diff --git a/locale/fr.js b/locale/fr.js index 75f7ade4..3a5f3172 100644 --- a/locale/fr.js +++ b/locale/fr.js @@ -1075,6 +1075,8 @@ export default { select_button_ui: "Sélectionner l'objet", duplicate_button_ui: "Dupliquer l'objet sélectionné", delete_button_ui: "Supprimer l'objet sélectionné", + delete_block_button_ui: 'Supprimer le bloc', + duplicate_block_button_ui: 'Dupliquer le bloc', camera_button_ui: 'Contrôles de la caméra', eye_button_ui: "Orbiter autour de l'objet sélectionné", @@ -1121,6 +1123,12 @@ export default { context_expand_all_option: 'Tout développer', context_disable_option: 'Désactiver', context_enable_option: 'Activer', + add_comment: 'Ajouter un commentaire', + delete_comment: 'Supprimer le commentaire', + view_in_canvas: 'Afficher dans le canvas', + exit_canvas_view: "Arrêter l'orbite", + detach_block_option: 'Détacher', + view_in_canvas_option: 'Afficher dans le canvas', context_copy_option: 'Copier', context_paste_option: 'Coller', context_cut_option: 'Couper', diff --git a/locale/it.js b/locale/it.js index e10c2e54..af38d9e6 100644 --- a/locale/it.js +++ b/locale/it.js @@ -1084,6 +1084,8 @@ export default { select_button_ui: 'Seleziona oggetto', duplicate_button_ui: 'Duplica oggetto selezionato', delete_button_ui: 'Elimina oggetto selezionato', + delete_block_button_ui: 'Elimina blocco', + duplicate_block_button_ui: 'Duplica blocco', camera_button_ui: 'Controlli camera', eye_button_ui: "Orbita attorno all'oggetto selezionato", @@ -1130,6 +1132,12 @@ export default { context_expand_all_option: 'Espandi tutto', context_disable_option: 'Disabilita', context_enable_option: 'Abilita', + add_comment: 'Aggiungi commento', + delete_comment: 'Elimina commento', + view_in_canvas: 'Visualizza nel canvas', + exit_canvas_view: "Interrompi l'orbita", + detach_block_option: 'Stacca', + view_in_canvas_option: 'Visualizza nel canvas', context_copy_option: 'Copia', context_paste_option: 'Incolla', context_cut_option: 'Taglia', diff --git a/locale/pl.js b/locale/pl.js index 6db2d64c..8b72a050 100644 --- a/locale/pl.js +++ b/locale/pl.js @@ -1073,6 +1073,8 @@ export default { select_button_ui: 'Wybierz obiekt', duplicate_button_ui: 'Duplikuj wybrany obiekt', delete_button_ui: 'Usuń wybrany obiekt', + delete_block_button_ui: 'Usuń blok', + duplicate_block_button_ui: 'Duplikuj blok', camera_button_ui: 'Kontrola kamery', eye_button_ui: 'Obracaj wokół wybranego obiektu', @@ -1118,6 +1120,12 @@ export default { context_expand_all_option: 'Rozwiń wszystko', context_disable_option: 'Wyłącz', context_enable_option: 'Włącz', + add_comment: 'Dodaj komentarz', + delete_comment: 'Usuń komentarz', + view_in_canvas: 'Wyświetl na płótnie', + exit_canvas_view: 'Zatrzymaj krążenie wokół obiektu', + detach_block_option: 'Odłącz', + view_in_canvas_option: 'Wyświetl na płótnie', context_copy_option: 'Kopiuj', context_paste_option: 'Wklej', context_cut_option: 'Wytnij', diff --git a/locale/pt.js b/locale/pt.js index 229979cf..0feff7cc 100644 --- a/locale/pt.js +++ b/locale/pt.js @@ -1083,6 +1083,8 @@ export default { select_button_ui: 'Selecionar objeto', duplicate_button_ui: 'Duplicar o objeto selecionado', delete_button_ui: 'Eliminar o objeto selecionado', + delete_block_button_ui: 'Excluir bloco', + duplicate_block_button_ui: 'Duplicar bloco', camera_button_ui: 'Controlos da câmara', eye_button_ui: 'Orbitar ao redor do objeto selecionado', @@ -1129,6 +1131,12 @@ export default { context_expand_all_option: 'Expandir tudo', context_disable_option: 'Desativar', context_enable_option: 'Ativar', + add_comment: 'Adicionar comentário', + delete_comment: 'Excluir comentário', + view_in_canvas: 'Ver na tela', + exit_canvas_view: 'Parar de orbitar o objeto', + detach_block_option: 'Desconectar', + view_in_canvas_option: 'Ver na tela', context_copy_option: 'Copiar', context_paste_option: 'Colar', context_cut_option: 'Cortar', diff --git a/locale/sv.js b/locale/sv.js index 45a43551..ad06b928 100644 --- a/locale/sv.js +++ b/locale/sv.js @@ -1061,6 +1061,8 @@ export default { select_button_ui: 'Välj objekt', duplicate_button_ui: 'Duplicera valt objekt', delete_button_ui: 'Radera valt objekt', + delete_block_button_ui: 'Radera block', + duplicate_block_button_ui: 'Duplicera block', camera_button_ui: 'Kamerakontroller', eye_button_ui: 'Kretsa runt valt objekt', @@ -1106,6 +1108,12 @@ export default { context_expand_all_option: 'Expandera alla', context_disable_option: 'Inaktivera', context_enable_option: 'Aktivera', + add_comment: 'Lägg till kommentar', + delete_comment: 'Ta bort kommentar', + view_in_canvas: 'Visa i canvas', + exit_canvas_view: 'Sluta kretsa runt objektet', + detach_block_option: 'Koppla loss', + view_in_canvas_option: 'Visa i canvas', context_copy_option: 'Kopiera', context_paste_option: 'Klistra in', context_cut_option: 'Klipp ut', diff --git a/ui/contextmenu.js b/ui/contextmenu.js index d6b4353e..35624f21 100644 --- a/ui/contextmenu.js +++ b/ui/contextmenu.js @@ -680,10 +680,16 @@ export function initContextMenus(workspace) { return result === key ? fallback : result; }; + // Helper: set both the a11y label and the native hover tooltip together, + // so toolbar buttons always show hover text like the rest of the app. + const setToolbarLabel = (el, label) => { + el.setAttribute('aria-label', label); + el.title = label; + }; + const duplicateBtn = document.createElement('button'); duplicateBtn.type = 'button'; duplicateBtn.className = 'fc-block-toolbar-btn'; - duplicateBtn.setAttribute('aria-label', getToolbarLabel('duplicate_button_ui', 'Duplicate')); // fa-copy duplicateBtn.innerHTML = mkFaSvg( '' @@ -692,7 +698,6 @@ export function initContextMenus(workspace) { const deleteBtn = document.createElement('button'); deleteBtn.type = 'button'; deleteBtn.className = 'fc-block-toolbar-btn fc-block-toolbar-btn--delete'; - deleteBtn.setAttribute('aria-label', getToolbarLabel('delete_button_ui', 'Delete')); // fa-trash deleteBtn.innerHTML = mkFaSvg( '' @@ -701,13 +706,21 @@ export function initContextMenus(workspace) { const detachBtn = document.createElement('button'); detachBtn.type = 'button'; detachBtn.className = 'fc-block-toolbar-btn'; - detachBtn.setAttribute('aria-label', getToolbarLabel('shortcut_detach_block', 'Detach')); // fa-link-slash detachBtn.innerHTML = mkFaSvg( '', '0 0 640 512' ); + // duplicate/delete/detach labels don't change per-block, so nothing else + // re-applies them after creation — without this, switching language after + // the toolbar first renders would leave them stuck in the old language. + function refreshStaticToolbarLabels() { + setToolbarLabel(duplicateBtn, getToolbarLabel('duplicate_block_button_ui', 'Duplicate block')); + setToolbarLabel(deleteBtn, getToolbarLabel('delete_block_button_ui', 'Delete block')); + setToolbarLabel(detachBtn, getToolbarLabel('shortcut_detach_block', 'Detach')); + } + // Passive "press M to move" hint. Looks like a toolbar button but is inert // (pointer-events: none): it exists purely so keyboard users see the move // icon with an M badge when they land on a loose block. @@ -723,7 +736,7 @@ export function initContextMenus(workspace) { const commentBtn = document.createElement('button'); commentBtn.type = 'button'; commentBtn.className = 'fc-block-toolbar-btn'; - commentBtn.setAttribute('aria-label', getToolbarLabel('add_comment', 'Add comment')); + setToolbarLabel(commentBtn, getToolbarLabel('add_comment', 'Add comment')); // fa-comment (pre-6.7 path revision; same rendered glyph as current fa-comment) const commentAddSvg = mkFaSvg( '', @@ -739,12 +752,12 @@ export function initContextMenus(workspace) { const enableBtn = document.createElement('button'); enableBtn.type = 'button'; enableBtn.className = 'fc-block-toolbar-btn'; - // fa-ban — shown when the block is currently disabled. + // fa-ban — shown when the block is currently enabled (click to disable). const blockDisableSvg = mkFaSvg( '', '0 0 512 512' ); - // fa-circle-check — shown when the block is currently enabled. + // fa-circle-check — shown when the block is currently disabled (click to enable). const blockEnableSvg = mkFaSvg( '', '0 0 512 512' @@ -764,7 +777,7 @@ export function initContextMenus(workspace) { const viewBtn = document.createElement('button'); viewBtn.type = 'button'; viewBtn.className = 'fc-block-toolbar-btn'; - viewBtn.setAttribute('aria-label', getToolbarLabel('view_in_canvas', 'View in canvas')); + setToolbarLabel(viewBtn, getToolbarLabel('view_in_canvas', 'View in canvas')); viewBtn.innerHTML = viewEnterSvg; blockToolbar.append(duplicateBtn, detachBtn, moveHint, commentBtn, enableBtn, viewBtn, deleteBtn); @@ -1021,8 +1034,11 @@ export function initContextMenus(workspace) { const block = toolbarBlock; if (!block) return; const simplified = isLooseAndMovable(block); - duplicateBtn.style.display = simplified ? 'none' : ''; - commentBtn.style.display = isBlockLocked(block) || simplified ? 'none' : ''; + const locked = isBlockLocked(block); + duplicateBtn.style.display = ''; + commentBtn.style.display = locked ? 'none' : ''; + detachBtn.style.display = locked || !isDetachable(block) ? 'none' : ''; + enableBtn.style.display = locked || simplified ? 'none' : ''; moveHint.style.display = toolbarKeyboardMode && simplified ? '' : 'none'; // Loose blocks never show View, regardless of mesh state — no point // waiting on a mesh check for a block that isn't placed anywhere yet. @@ -1043,8 +1059,8 @@ export function initContextMenus(workspace) { // crossed-out "delete" icon when it does, plain "add" icon when it doesn't. function updateCommentButton(block) { const hasComment = block.getCommentText() !== null; - commentBtn.setAttribute( - 'aria-label', + setToolbarLabel( + commentBtn, hasComment ? getToolbarLabel('delete_comment', 'Delete comment') : getToolbarLabel('add_comment', 'Add comment') @@ -1054,11 +1070,11 @@ export function initContextMenus(workspace) { function updateEnableButton(block) { const disabled = block.hasDisabledReason('MANUALLY_DISABLED'); - enableBtn.setAttribute( - 'aria-label', + setToolbarLabel( + enableBtn, disabled ? getToolbarLabel('context_enable_option', 'Enable') : getToolbarLabel('context_disable_option', 'Disable') ); - enableBtn.innerHTML = disabled ? blockDisableSvg : blockEnableSvg; + enableBtn.innerHTML = disabled ? blockEnableSvg : blockDisableSvg; } function showBlockToolbar(block, { keyboard = false } = {}) { @@ -1068,9 +1084,8 @@ export function initContextMenus(workspace) { // Locked blocks can't be edited: hide the mutating buttons (detach, // comment, enable/disable, delete), leaving duplicate and view-in-canvas available. const locked = isBlockLocked(block); - detachBtn.style.display = !locked && isDetachable(block) ? '' : 'none'; deleteBtn.style.display = locked ? 'none' : ''; - enableBtn.style.display = locked ? 'none' : ''; + refreshStaticToolbarLabels(); updateSimplifiedToolbar(); updateCommentButton(block); updateEnableButton(block); @@ -1086,10 +1101,10 @@ export function initContextMenus(workspace) { !!window.orbitViewActive && (window.orbitBlock === block || (meshRoot && window.orbitMesh === meshRoot)); viewBtn.innerHTML = exitMode ? viewExitSvg : viewEnterSvg; - viewBtn.setAttribute( - 'aria-label', + setToolbarLabel( + viewBtn, exitMode - ? getToolbarLabel('exit_canvas_view', 'Exit canvas view') + ? getToolbarLabel('exit_canvas_view', 'Stop orbiting object') : getToolbarLabel('view_in_canvas', 'View in canvas') ); blockToolbar.classList.add('visible');