From 110d26caec39b68a620fe4478523e52e049494eb Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Fri, 12 Jun 2026 05:52:54 -0400 Subject: [PATCH 1/5] Update menu radio groups to remove prefixes from values --- ts/a11y/aria.ts | 29 +------- ts/a11y/explorer.ts | 8 +- ts/ui/menu/MJContextMenu.ts | 25 ++++--- ts/ui/menu/Menu.ts | 131 +++++++++++++++++---------------- ts/ui/menu/MenuUtil.ts | 2 +- ts/ui/menu/__locales__/en.json | 6 +- 6 files changed, 91 insertions(+), 110 deletions(-) diff --git a/ts/a11y/aria.ts b/ts/a11y/aria.ts index 7b6f1bdbc..409afeb19 100644 --- a/ts/a11y/aria.ts +++ b/ts/a11y/aria.ts @@ -24,37 +24,16 @@ import { localize } from './__locales__/Component.js'; /** - * Localization keys for ARIA role descriptions used by MathJax. Values are the - * strings expected in the locale files. + * Prefix to use for roleDescription localization */ -export const AriaRoleDescription: Record = { - EXPRESSION: 'Aria/RoleDescription/MathJax expression', - MATHJAX: 'Aria/RoleDescription/MathJax', - MATH: 'Aria/RoleDescription/math', - CLICKABLE: 'Aria/RoleDescription/clickable math', - EXPLORABLE: 'Aria/RoleDescription/explorable math', - NONE: 'Aria/RoleDescription/none', -}; +export const AriaPrefix = 'Aria/RoleDescription'; /** * Returns the localized string for a known ARIA role description ID. * * @param {string} id - The aria localization key to resolve. - * @returns {string | null} Localized string or null if id has no Aria prefix. + * @returns {string} Localized string; */ export function localizeAria(id: string): string { - if (!hasAriaPrefix(id)) { - return null; - } - return localize(id); -} - -/** - * Checks whether the given ID starts with the `Aria/` prefix. - * - * @param {string} id - The string to test. - * @returns {boolean} True if `id` begins with `Aria/`. - */ -function hasAriaPrefix(id: string): boolean { - return id.startsWith('Aria/'); + return localize(`${AriaPrefix}/${id}`); } diff --git a/ts/a11y/explorer.ts b/ts/a11y/explorer.ts index 8d8a2003e..688d5f22b 100644 --- a/ts/a11y/explorer.ts +++ b/ts/a11y/explorer.ts @@ -34,7 +34,7 @@ import { StyleJson } from '../util/StyleJson.js'; import { context } from '../util/context.js'; import { ExplorerPool, RegionPool } from './explorer/ExplorerPool.js'; -import { AriaRoleDescription, localizeAria } from './aria.js'; +import { localizeAria } from './aria.js'; import * as Sre from './sre.js'; @@ -136,7 +136,7 @@ export function ExplorerMathItemMixin>( /** * The aria-roleDescription to use for the math */ - protected static roleDescription: string = AriaRoleDescription.MATH; + protected static roleDescription: string = 'math'; /** * Decription to use when set to none @@ -155,9 +155,9 @@ export function ExplorerMathItemMixin>( public get roleDescription() { const CLASS = this.constructor as typeof BaseClass; - return CLASS.roleDescription === AriaRoleDescription.NONE + return CLASS.roleDescription === 'none' ? CLASS.none - : (localizeAria(CLASS.roleDescription) ?? CLASS.roleDescription); + : localizeAria(CLASS.roleDescription); } public get none() { diff --git a/ts/ui/menu/MJContextMenu.ts b/ts/ui/menu/MJContextMenu.ts index ffc20b00c..c05a29734 100644 --- a/ts/ui/menu/MJContextMenu.ts +++ b/ts/ui/menu/MJContextMenu.ts @@ -137,10 +137,11 @@ export class MJContextMenu extends ContextMenu { public findID(...names: string[]): Item { let menu = this as Menu; let item = null as Item; - for (const name of names) { + for (const fullname of names) { + const name = fullname.match('/') ? fullname : fullname.replace(/.*\//, ''); if (!menu) return null; for (item of menu.items) { - if (item.id === name) { + if (item.id === name || item.id?.replace(/.*\//, '') === name) { menu = item instanceof Submenu ? item.submenu : null; break; } @@ -166,12 +167,12 @@ export class MJContextMenu extends ContextMenu { */ protected getOriginalMenu() { const input = this.mathItem.inputJax.name; - const original = this.findID('Show', 'Show/Original'); + const original = this.findID('Show', 'Original'); original.content = input === 'MathML' ? localize('Show/OriginalMathML') : localize('Show/Commands', input); - const clipboard = this.findID('Copy', 'Show/Original'); + const clipboard = this.findID('Copy', 'Original'); clipboard.content = original.content; } @@ -190,8 +191,8 @@ export class MJContextMenu extends ContextMenu { */ protected getSpeechMenu() { const speech = this.mathItem.outputData.speech; - this.findID('Show', 'Show/SpeechText')[speech ? 'enable' : 'disable'](); - this.findID('Copy', 'Show/SpeechText')[speech ? 'enable' : 'disable'](); + this.findID('Show', 'SpeechText')[speech ? 'enable' : 'disable'](); + this.findID('Copy', 'SpeechText')[speech ? 'enable' : 'disable'](); } /** @@ -199,8 +200,8 @@ export class MJContextMenu extends ContextMenu { */ protected getBrailleMenu() { const braille = this.mathItem.outputData.braille; - this.findID('Show', 'Show/BrailleCode')[braille ? 'enable' : 'disable'](); - this.findID('Copy', 'Show/BrailleCode')[braille ? 'enable' : 'disable'](); + this.findID('Show', 'BrailleCode')[braille ? 'enable' : 'disable'](); + this.findID('Copy', 'BrailleCode')[braille ? 'enable' : 'disable'](); } /** @@ -208,8 +209,8 @@ export class MJContextMenu extends ContextMenu { */ protected getSvgMenu() { const svg = this.jax.SVG; - this.findID('Show', 'Show/SvgImage')[svg ? 'enable' : 'disable'](); - this.findID('Copy', 'Show/SvgImage')[svg ? 'enable' : 'disable'](); + this.findID('Show', 'SvgImage')[svg ? 'enable' : 'disable'](); + this.findID('Copy', 'SvgImage')[svg ? 'enable' : 'disable'](); } /** @@ -226,8 +227,8 @@ export class MJContextMenu extends ContextMenu { '') as string; disable = !this.errorMsg; } - this.findID('Show', 'Show/Error')[disable ? 'disable' : 'enable'](); - this.findID('Copy', 'Show/Error')[disable ? 'disable' : 'enable'](); + this.findID('Show', 'Error')[disable ? 'disable' : 'enable'](); + this.findID('Copy', 'Error')[disable ? 'disable' : 'enable'](); } /*======================================================================*/ diff --git a/ts/ui/menu/Menu.ts b/ts/ui/menu/Menu.ts index 8c964154c..4d9b9bff7 100644 --- a/ts/ui/menu/Menu.ts +++ b/ts/ui/menu/Menu.ts @@ -50,7 +50,7 @@ import { locales } from './locales.js'; import { Parser, Rule, CssStyles, Submenu } from './mj-context-menu.js'; -import { AriaRoleDescription, localizeAria } from '../../a11y/aria.js'; +import { AriaPrefix, localizeAria } from '../../a11y/aria.js'; import { Locale } from '../../util/Locale.js'; import { COMPONENT, localize } from './__locales__/Component.js'; export { COMPONENT }; @@ -173,7 +173,7 @@ export class Menu { brailleSpeech: false, brailleCombine: false, speechRules: 'clearspeak-default', - roleDescription: AriaRoleDescription.MATH, + roleDescription: 'math', inTabOrder: true, tabSelects: 'all', help: true, @@ -707,16 +707,16 @@ export class Menu { this.submenu('Settings', [ this.submenu( 'Renderer', - this.radioGroup('renderer', ['CHTML', 'SVG']) + this.radioGroup('renderer', '.', ['CHTML', 'SVG']) ), this.submenu('Wide/WideExpressions', [ - this.radioGroup('overflow', [ - 'Wide/Overflow', - 'Wide/Scroll', - 'Wide/Linebreak', - 'Wide/Scale', - 'Wide/Truncate', - 'Wide/Elide', + this.radioGroup('overflow', 'Wide', [ + 'Overflow', + 'Scroll', + 'Linebreak', + 'Scale', + 'Truncate', + 'Elide', ]), this.rule(), this.checkbox('Wide/BreakInline', 'breakInline'), @@ -733,14 +733,14 @@ export class Menu { this.submenu('Zoom/ZoomTrigger', [ this.command('Zoom/ZoomNow', () => this.zoom(null, '')), this.rule(), - this.radioGroup('zoom', [ - 'Zoom/Click', - 'Zoom/DoubleClick', - 'Zoom/NoZoom', + this.radioGroup('zoom', 'Zoom', [ + 'Click', + 'DoubleClick', + 'NoZoom', ]), this.rule(), this.label('Zoom/TriggerRequires'), - this.checkbox(MenuUtil.isMac ? 'Option' : 'Zoom/Alt', 'alt'), + this.checkbox(MenuUtil.isMac ? 'Zoom/Option' : 'Zoom/Alt', 'alt'), this.checkbox('Zoom/Command', 'cmd', { hidden: !MenuUtil.isMac, }), @@ -751,7 +751,7 @@ export class Menu { ]), this.submenu( 'Zoom/ZoomFactor', - this.radioGroup('zscale', [ + this.radioGroup('zscale', '.', [ '150%', '175%', '200%', @@ -775,7 +775,7 @@ export class Menu { this.label('Rules'), this.submenu( 'Mathspeak', - this.radioGroup('speechRules', [ + this.radioGroup('speechRules', '', [ 'mathspeak-default', 'mathspeak-brief', 'mathspeak-sbrief', @@ -783,7 +783,7 @@ export class Menu { ), this.submenu( 'Clearspeak', - this.radioGroup('speechRules', ['clearspeak-default']) + this.radioGroup('speechRules', '', ['clearspeak-default']) ), this.rule(), this.submenu('A11yLanguage'), @@ -797,55 +797,55 @@ export class Menu { this.checkbox('BrailleCombine', 'brailleCombine'), this.rule(), this.label('Code'), - this.radioGroup('brailleCode', ['nemeth', 'ueb', 'euro']), + this.radioGroup('brailleCode', '', ['nemeth', 'ueb', 'euro']), ]), this.submenu('Explorer', [ this.submenu('Highlight', [ this.submenu( 'Highlight/Background', - this.radioGroup('backgroundColor', [ - 'Highlight/Blue', - 'Highlight/Red', - 'Highlight/Green', - 'Highlight/Yellow', - 'Highlight/Cyan', - 'Highlight/Magenta', - 'Highlight/White', - 'Highlight/Black', + this.radioGroup('backgroundColor', 'Highlight', [ + 'Blue', + 'Red', + 'Green', + 'Yellow', + 'Cyan', + 'Magenta', + 'White', + 'Black', ]) ), { type: 'slider', variable: 'backgroundOpacity', content: ' ' }, this.submenu( 'Highlight/Foreground', - this.radioGroup('foregroundColor', [ - 'Highlight/Black', - 'Highlight/White', - 'Highlight/Magenta', - 'Highlight/Cyan', - 'Highlight/Yellow', - 'Highlight/Green', - 'Highlight/Red', - 'Highlight/Blue', + this.radioGroup('foregroundColor', 'Highlight', [ + 'Black', + 'White', + 'Magenta', + 'Cyan', + 'Yellow', + 'Green', + 'Red', + 'Blue', ]) ), { type: 'slider', variable: 'foregroundOpacity', content: ' ' }, this.rule(), - this.radioGroup('highlight', [ - 'Highlight/None', - 'Highlight/Hover', - 'Highlight/Flame', + this.radioGroup('highlight', 'Highlight', [ + 'None', + 'Hover', + 'Flame', ]), this.rule(), this.checkbox('Highlight/TreeColoring', 'treeColoring'), ]), this.submenu('Magnification', [ - this.radioGroup('magnification', [ - 'Magnification/None', - 'Magnification/Keyboard', - 'Magnification/Mouse', + this.radioGroup('magnification', 'Magnification', [ + 'None', + 'Keyboard', + 'Mouse', ]), this.rule(), - this.radioGroup('magnify', ['200%', '300%', '400%', '500%']), + this.radioGroup('magnify', '.', ['200%', '300%', '400%', '500%']), ]), this.submenu('Semantic/Info', [ this.checkbox('Semantic/Type', 'infoType'), @@ -854,13 +854,13 @@ export class Menu { ]), this.rule(), this.submenu('RoleDescription', [ - this.radioGroup('roleDescription', [ - AriaRoleDescription.EXPRESSION, - AriaRoleDescription.MATHJAX, - AriaRoleDescription.MATH, - AriaRoleDescription.CLICKABLE, - AriaRoleDescription.EXPLORABLE, - AriaRoleDescription.NONE, + this.radioGroup('roleDescription', AriaPrefix, [ + 'MathJax expression', + 'MathJax', + 'math', + 'clickable math', + 'explorable math', + 'none', ]), ]), this.checkbox('Tabbing/MathHelp', 'help'), @@ -874,7 +874,7 @@ export class Menu { this.rule(), this.checkbox('Tabbing/InTabOrder', 'inTabOrder'), this.submenu('Tabbing/TabSelects', [ - this.radioGroup('tabSelects', ['Tabbing/all', 'Tabbing/last']), + this.radioGroup('tabSelects', 'Tabbing', ['all', 'last']), ]), this.rule(), this.checkbox('AssistiveMml', 'assistiveMml'), @@ -886,7 +886,7 @@ export class Menu { }) as MJContextMenu; const menu = this.menu; menu.settings = this.settings; - menu.findID('Settings', 'Wide/WideExpressions', 'Wide/Elide').disable(); + menu.findID('Settings', 'WideExpressions', 'Elide').disable(); menu.findID('Braille', 'ueb').hide(); menu.setJax(this.jax); this.checkLoadableItems(); @@ -1985,24 +1985,27 @@ export class Menu { * Create JSON for a group of connected radio buttons * * @param {string} variable The (pool) variable to attach to each radio button + * @param {string} prefix The prefix to use for localization, or '' for no localization * @param {string[]} radios An array of [string] or [string, string], giving the id and content * for each radio button (if only one string is given it is used for both) * @returns {object[]} An array of JSON objects for radion buttons */ - public radioGroup(variable: string, radios: string[]): object[] { - return radios.map((item) => this.radio(item, variable)); + public radioGroup(variable: string, prefix: string, radios: string[]): object[] { + return radios.map((item) => this.radio(prefix, item, variable)); } /** * Create JSON for a radio button item * - * @param {string} id The id for the item - * @param {string} variable The (pool) variable to attach to this radio button - * @param {object} other Other values to include in the generated JSON object - * @returns {object} The JSON for the radio button item - */ - public radio(id: string, variable: string, other: object = {}): object { - const content = id.match(/^\d+%$/) ? id : (localizeAria(id) ?? localize(id)); + * @param {string} prefix The prefix to use for localization, or '' for no localization + * @param {string} id The id for the item + * @param {string} variable The (pool) variable to attach to this radio button + * @param {object} other Other values to include in the generated JSON object + * @returns {object} The JSON for the radio button item + */ + public radio(prefix: string, id: string, variable: string, other: object = {}): object { + const key = prefix ? `${prefix}/${id}` : id; + const content = prefix === '.' ? id : prefix === AriaPrefix ? localizeAria(id) : localize(key); return Object.assign({ type: 'radio', id, content, variable }, other); } diff --git a/ts/ui/menu/MenuUtil.ts b/ts/ui/menu/MenuUtil.ts index 0e25d632b..5c3d49a35 100644 --- a/ts/ui/menu/MenuUtil.ts +++ b/ts/ui/menu/MenuUtil.ts @@ -44,7 +44,7 @@ export function copyToClipboard(text: string) { try { document.execCommand('copy'); } catch (error) { - alert(localize('.Warn/CantCopy', error.message)); + alert(localize('Warn/CantCopy', error.message)); } document.body.removeChild(input); } diff --git a/ts/ui/menu/__locales__/en.json b/ts/ui/menu/__locales__/en.json index 235fe465b..6d9eb8716 100644 --- a/ts/ui/menu/__locales__/en.json +++ b/ts/ui/menu/__locales__/en.json @@ -76,9 +76,11 @@ "Wide/WideExpressions": "Wide Expressions", "Zoom/Alt": "Alt", "Zoom/Click": "Click", + "Zoom/Command": "Command", "Zoom/Control": "Control", "Zoom/DoubleClick": "Double-Click", "Zoom/NoZoom": "No Zoom", + "Zoom/Option": "Option", "Zoom/Shift": "Shift", "Zoom/TriggerRequires": "Trigger Requires:", "Zoom/ZoomFactor": "Zoom Factor", @@ -93,11 +95,9 @@ "Braille": "\u00A0 \u00A0 Braille", "BrailleCombine": "Combine with Speech", "BrailleSpeech": "Replace Speech", - "CHTML": "CHTML", "Clearspeak": "Clearspeak", "Code": "Code Format:", "Collapsible": "Collapsible Math", - "Command": "Command", "Enrich": "Semantic Enrichment", "Explorer": "\u00A0 \u00A0 Explorer", "Generate": "Generate", @@ -105,12 +105,10 @@ "Language": "Language", "MathmlIncludes": "MathML/SVG has", "Mathspeak": "Mathspeak", - "Option": "Option", "Options": "\u00A0 \u00A0 Options", "Renderer": "Math Renderer", "Reset": "Reset to defaults", "Rules": "Rules:", - "SVG": "SVG", "ScaleAllMath": "Scale All Math...", "Settings": "Math Settings", "Speech": "\u00A0 \u00A0 Speech", From 360a14625e584be41f9eb75f065736f82458b120 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Fri, 12 Jun 2026 15:27:24 -0400 Subject: [PATCH 2/5] Make fake a11y/aria component for aria role description localization --- components/mjs/a11y/a11y.js | 2 -- components/mjs/a11y/config.json | 19 ---------- components/mjs/a11y/en.js | 5 --- ts/a11y/aria.ts | 39 --------------------- ts/a11y/{ => aria}/__locales__/Component.ts | 15 +++++--- ts/a11y/{ => aria}/__locales__/de.json | 0 ts/a11y/{ => aria}/__locales__/en.json | 0 ts/a11y/explorer.ts | 4 +-- ts/a11y/explorer/KeyExplorer.ts | 2 +- ts/ui/menu/Menu.ts | 6 ++-- 10 files changed, 17 insertions(+), 75 deletions(-) delete mode 100644 components/mjs/a11y/a11y.js delete mode 100644 components/mjs/a11y/config.json delete mode 100644 components/mjs/a11y/en.js delete mode 100644 ts/a11y/aria.ts rename ts/a11y/{ => aria}/__locales__/Component.ts (71%) rename ts/a11y/{ => aria}/__locales__/de.json (100%) rename ts/a11y/{ => aria}/__locales__/en.json (100%) diff --git a/components/mjs/a11y/a11y.js b/components/mjs/a11y/a11y.js deleted file mode 100644 index e895962ab..000000000 --- a/components/mjs/a11y/a11y.js +++ /dev/null @@ -1,2 +0,0 @@ -import './lib/a11y.js'; -import './en.js'; diff --git a/components/mjs/a11y/config.json b/components/mjs/a11y/config.json deleted file mode 100644 index c5538cdac..000000000 --- a/components/mjs/a11y/config.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "build": { - "component": "a11y", - "targets": ["a11y/aria.ts"], - "excludeSubdirs": true - }, - "copy": { - "to": "[bundle]/a11y", - "from": "[ts]/a11y", - "copy": ["__locales__"], - "excludes": ["__locales__/Component.ts"] - }, - "webpack": { - "name": "a11y", - "libs": [ - "components/src/a11y/lib" - ] - } -} diff --git a/components/mjs/a11y/en.js b/components/mjs/a11y/en.js deleted file mode 100644 index f10d4523a..000000000 --- a/components/mjs/a11y/en.js +++ /dev/null @@ -1,5 +0,0 @@ -import { Locale } from '#js/util/Locale.js'; -import { COMPONENT } from '#js/a11y/__locales__/Component.js'; -import data from '#ts/a11y/__locales__/en.json' with {type: 'json'}; - -Locale.registerMessages(COMPONENT, 'en', data); diff --git a/ts/a11y/aria.ts b/ts/a11y/aria.ts deleted file mode 100644 index 409afeb19..000000000 --- a/ts/a11y/aria.ts +++ /dev/null @@ -1,39 +0,0 @@ -/************************************************************* - * - * Copyright (c) 2026 The MathJax Consortium - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @file Localizable ARIA constants. - * - * @author v.sorge@mathjax.org (Volker Sorge) - */ - -import { localize } from './__locales__/Component.js'; - -/** - * Prefix to use for roleDescription localization - */ -export const AriaPrefix = 'Aria/RoleDescription'; - -/** - * Returns the localized string for a known ARIA role description ID. - * - * @param {string} id - The aria localization key to resolve. - * @returns {string} Localized string; - */ -export function localizeAria(id: string): string { - return localize(`${AriaPrefix}/${id}`); -} diff --git a/ts/a11y/__locales__/Component.ts b/ts/a11y/aria/__locales__/Component.ts similarity index 71% rename from ts/a11y/__locales__/Component.ts rename to ts/a11y/aria/__locales__/Component.ts index fe745bf11..5a1c0a2f6 100644 --- a/ts/a11y/__locales__/Component.ts +++ b/ts/a11y/aria/__locales__/Component.ts @@ -16,16 +16,23 @@ */ /** - * @file Locale component registration for a11y/explorer + * @file Locale component registration for a11y/aria * * @author dpvc@mathjax.org (Davide P. Cervone) */ -import { Locale, namedData } from '../../util/Locale.js'; +import { Locale, namedData } from '../../../util/Locale.js'; +import data from '#ts/a11y/aria/__locales__/en.json' with {type: 'json'}; -export const COMPONENT = 'a11y'; +export const COMPONENT = 'a11y/aria'; -Locale.registerLocaleFiles(COMPONENT, '../ts/a11y'); +Locale.registerLocaleFiles(COMPONENT, '../../ts/a11y/aira'); +Locale.registerMessages(COMPONENT, 'en', data); + +/** + * Prefix to use for roleDescription localization + */ +export const roleDescPrefix = 'Aria/RoleDescription'; /** * Get a localized message for this component diff --git a/ts/a11y/__locales__/de.json b/ts/a11y/aria/__locales__/de.json similarity index 100% rename from ts/a11y/__locales__/de.json rename to ts/a11y/aria/__locales__/de.json diff --git a/ts/a11y/__locales__/en.json b/ts/a11y/aria/__locales__/en.json similarity index 100% rename from ts/a11y/__locales__/en.json rename to ts/a11y/aria/__locales__/en.json diff --git a/ts/a11y/explorer.ts b/ts/a11y/explorer.ts index 688d5f22b..ebfa30b68 100644 --- a/ts/a11y/explorer.ts +++ b/ts/a11y/explorer.ts @@ -34,9 +34,9 @@ import { StyleJson } from '../util/StyleJson.js'; import { context } from '../util/context.js'; import { ExplorerPool, RegionPool } from './explorer/ExplorerPool.js'; -import { localizeAria } from './aria.js'; import * as Sre from './sre.js'; +import * as Aria from './aria/__locales__/Component.js'; const isUnix = context.os === 'Unix'; @@ -157,7 +157,7 @@ export function ExplorerMathItemMixin>( const CLASS = this.constructor as typeof BaseClass; return CLASS.roleDescription === 'none' ? CLASS.none - : localizeAria(CLASS.roleDescription); + : Aria.localize(`${Aria.roleDescPrefix}/${CLASS.roleDescription}`); } public get none() { diff --git a/ts/a11y/explorer/KeyExplorer.ts b/ts/a11y/explorer/KeyExplorer.ts index 6c2f57b98..eb34e58fe 100644 --- a/ts/a11y/explorer/KeyExplorer.ts +++ b/ts/a11y/explorer/KeyExplorer.ts @@ -1305,7 +1305,7 @@ export class SpeechExplorer const description = item.roleDescription; const speech = (container.getAttribute(SemAttr.SPEECH) || '') + - (description ? ', ' + description : ''); + (description && description !== this.none ? ', ' + description : ''); this.img?.remove(); this.img = this.document.adaptor.node('mjx-speech', { 'aria-label': speech, diff --git a/ts/ui/menu/Menu.ts b/ts/ui/menu/Menu.ts index 4d9b9bff7..478f0e7ea 100644 --- a/ts/ui/menu/Menu.ts +++ b/ts/ui/menu/Menu.ts @@ -50,7 +50,7 @@ import { locales } from './locales.js'; import { Parser, Rule, CssStyles, Submenu } from './mj-context-menu.js'; -import { AriaPrefix, localizeAria } from '../../a11y/aria.js'; +import * as Aria from '../../a11y/aria/__locales__/Component.js'; import { Locale } from '../../util/Locale.js'; import { COMPONENT, localize } from './__locales__/Component.js'; export { COMPONENT }; @@ -854,7 +854,7 @@ export class Menu { ]), this.rule(), this.submenu('RoleDescription', [ - this.radioGroup('roleDescription', AriaPrefix, [ + this.radioGroup('roleDescription', Aria.roleDescPrefix, [ 'MathJax expression', 'MathJax', 'math', @@ -2005,7 +2005,7 @@ export class Menu { */ public radio(prefix: string, id: string, variable: string, other: object = {}): object { const key = prefix ? `${prefix}/${id}` : id; - const content = prefix === '.' ? id : prefix === AriaPrefix ? localizeAria(id) : localize(key); + const content = prefix === '.' ? id : prefix === Aria.roleDescPrefix ? Aria.localize(key) : localize(key); return Object.assign({ type: 'radio', id, content, variable }, other); } From 5fe1db59ae5ed26b6c63e0a8106a8bdafb0391da Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Fri, 12 Jun 2026 19:21:51 -0400 Subject: [PATCH 3/5] Make new ids compatible with old ones, and add prefixes for more consistency. --- ts/ui/menu/MJContextMenu.ts | 50 ++++++++++++++++---- ts/ui/menu/Menu.ts | 54 +++++++++++----------- ts/ui/menu/__locales__/de.json | 84 +++++++++++++++++----------------- ts/ui/menu/__locales__/en.json | 78 +++++++++++++++---------------- 4 files changed, 149 insertions(+), 117 deletions(-) diff --git a/ts/ui/menu/MJContextMenu.ts b/ts/ui/menu/MJContextMenu.ts index c05a29734..2a18aac48 100644 --- a/ts/ui/menu/MJContextMenu.ts +++ b/ts/ui/menu/MJContextMenu.ts @@ -43,6 +43,27 @@ export type DynamicSubmenu = ( callback: SubmenuCallback ) => void; +/** + * Remap old menu ids to their new localization keys + */ +const RemapIds: {[key: string]: any} = { + Settings: { + Overflow: 'Wide/Expressions', + }, + Speech: { + 'Auto Voicing': 'AutoVoicing', + }, + Braille: { + BrailleSpeech: 'Braille/Speech', + BrailleCombine: 'Braille/Combine', + }, + Explorer: { + 'Semantic Info': 'SemanticInfo', + 'Role Description': 'RoleDescription', + 'Math Help': 'MathHelp', + } +}; + /*==========================================================================*/ /** @@ -132,14 +153,27 @@ export class MJContextMenu extends ContextMenu { * Find an item in the menu (recursively descending into submenus, if needed) * * @param {string[]} names The menu IDs to look for - * @returns {Item} The menu item (or null if not found) + * @returns {Item} The menu item (or null if not found) */ public findID(...names: string[]): Item { + let map = RemapIds; let menu = this as Menu; let item = null as Item; for (const fullname of names) { - const name = fullname.match('/') ? fullname : fullname.replace(/.*\//, ''); if (!menu) return null; + // + // Remap old menu ids to new prefixed ones + // (can be removed in a later version) + // + const remap = map?.[fullname]; + let name = typeof remap === 'string' ? remap : fullname; + if (!name.match('/')) { + name = name.replace(/.*\//, ''); + } + map = typeof remap === 'object' ? remap : null; + // + // Look for the id in the menu list + // for (item of menu.items) { if (item.id === name || item.id?.replace(/.*\//, '') === name) { menu = item instanceof Submenu ? item.submenu : null; @@ -191,8 +225,8 @@ export class MJContextMenu extends ContextMenu { */ protected getSpeechMenu() { const speech = this.mathItem.outputData.speech; - this.findID('Show', 'SpeechText')[speech ? 'enable' : 'disable'](); - this.findID('Copy', 'SpeechText')[speech ? 'enable' : 'disable'](); + this.findID('Show', 'Speech')[speech ? 'enable' : 'disable'](); + this.findID('Copy', 'Speech')[speech ? 'enable' : 'disable'](); } /** @@ -200,8 +234,8 @@ export class MJContextMenu extends ContextMenu { */ protected getBrailleMenu() { const braille = this.mathItem.outputData.braille; - this.findID('Show', 'BrailleCode')[braille ? 'enable' : 'disable'](); - this.findID('Copy', 'BrailleCode')[braille ? 'enable' : 'disable'](); + this.findID('Show', 'Braille')[braille ? 'enable' : 'disable'](); + this.findID('Copy', 'Braille')[braille ? 'enable' : 'disable'](); } /** @@ -209,8 +243,8 @@ export class MJContextMenu extends ContextMenu { */ protected getSvgMenu() { const svg = this.jax.SVG; - this.findID('Show', 'SvgImage')[svg ? 'enable' : 'disable'](); - this.findID('Copy', 'SvgImage')[svg ? 'enable' : 'disable'](); + this.findID('Show', 'SVG')[svg ? 'enable' : 'disable'](); + this.findID('Copy', 'SVG')[svg ? 'enable' : 'disable'](); } /** diff --git a/ts/ui/menu/Menu.ts b/ts/ui/menu/Menu.ts index 478f0e7ea..613fbe077 100644 --- a/ts/ui/menu/Menu.ts +++ b/ts/ui/menu/Menu.ts @@ -669,13 +669,13 @@ export class Menu { this.command('Show/MathMLcode', () => this.mathMLCode()), this.command('Show/Original', () => this.originalText()), this.rule(), - this.command('Show/SpeechText', () => this.speechText(), { + this.command('Show/Speech', () => this.speechText(), { disabled: true, }), - this.command('Show/BrailleCode', () => this.brailleText(), { + this.command('Show/Braille', () => this.brailleText(), { disabled: true, }), - this.command('Show/SvgImage', () => this.svgImage(), { + this.command('Show/SVG', () => this.svgImage(), { disabled: true, }), this.submenu('Show/Annotation'), @@ -688,13 +688,13 @@ export class Menu { this.command('Show/MathMLcode', () => this.copyMathML()), this.command('Show/Original', () => this.copyOriginal()), this.rule(), - this.command('Show/SpeechText', () => this.copySpeechText(), { + this.command('Show/Speech', () => this.copySpeechText(), { disabled: true, }), - this.command('Show/BrailleCode', () => this.copyBrailleText(), { + this.command('Show/Braille', () => this.copyBrailleText(), { disabled: true, }), - this.command('Show/SvgImage', () => this.copySvgImage(), { + this.command('Show/SVG', () => this.copySvgImage(), { disabled: true, }), this.submenu('Copy/Annotation'), @@ -706,10 +706,10 @@ export class Menu { this.rule(), this.submenu('Settings', [ this.submenu( - 'Renderer', + 'Settings/Renderer', this.radioGroup('renderer', '.', ['CHTML', 'SVG']) ), - this.submenu('Wide/WideExpressions', [ + this.submenu('Wide/Expressions', [ this.radioGroup('overflow', 'Wide', [ 'Overflow', 'Scroll', @@ -722,13 +722,13 @@ export class Menu { this.checkbox('Wide/BreakInline', 'breakInline'), ]), this.rule(), - this.submenu('MathmlIncludes', [ - this.checkbox('showSRE', 'showSRE'), - this.checkbox('showTex', 'showTex'), - this.checkbox('texHints', 'texHints'), - this.checkbox('semantics', 'semantics'), + this.submenu('Settings/MathmlIncludes', [ + this.checkbox('MML/showSRE', 'showSRE'), + this.checkbox('MML/showTex', 'showTex'), + this.checkbox('MML/texHints', 'texHints'), + this.checkbox('MML/semantics', 'semantics'), ]), - this.submenu('Language', this.languageSubmenu()), + this.submenu('Settings/Language', this.languageSubmenu()), this.rule(), this.submenu('Zoom/ZoomTrigger', [ this.command('Zoom/ZoomNow', () => this.zoom(null, '')), @@ -761,18 +761,18 @@ export class Menu { ]) ), this.rule(), - this.command('ScaleAllMath', () => this.scaleAllMath()), + this.command('Settings/Scale', () => this.scaleAllMath()), this.rule(), - this.command('Reset', () => this.resetDefaults()), + this.command('Settings/Reset', () => this.resetDefaults()), ]), this.rule(), - this.label('Accessibility'), + this.label('Label/Accessibility'), this.submenu('Speech', [ this.checkbox('Generate', 'speech'), this.checkbox('Subtitles', 'subtitles'), this.checkbox('AutoVoicing', 'voicing'), this.rule(), - this.label('Rules'), + this.label('Label/Rules'), this.submenu( 'Mathspeak', this.radioGroup('speechRules', '', [ @@ -791,13 +791,13 @@ export class Menu { this.submenu('Braille', [ this.checkbox('Generate', 'braille'), this.checkbox('Subtitles', 'viewBraille'), - this.checkbox('BrailleSpeech', 'brailleSpeech', { + this.checkbox('Braille/Speech', 'brailleSpeech', { hidden: true, }), - this.checkbox('BrailleCombine', 'brailleCombine'), + this.checkbox('Braille/Combine', 'brailleCombine'), this.rule(), - this.label('Code'), - this.radioGroup('brailleCode', '', ['nemeth', 'ueb', 'euro']), + this.label('Label/Code'), + this.radioGroup('brailleCode', 'Braille', ['nemeth', 'ueb', 'euro']), ]), this.submenu('Explorer', [ this.submenu('Highlight', [ @@ -866,9 +866,9 @@ export class Menu { this.checkbox('Tabbing/MathHelp', 'help'), ]), this.submenu('Options', [ - this.checkbox('Enrich', 'enrich'), - this.checkbox('Collapsible', 'collapsible'), - this.checkbox('AutoCollapse', 'autocollapse', { + this.checkbox('Options/Enrich', 'enrich'), + this.checkbox('Options/Collapsible', 'collapsible'), + this.checkbox('Options/AutoCollapse', 'autocollapse', { disabled: true, }), this.rule(), @@ -877,7 +877,7 @@ export class Menu { this.radioGroup('tabSelects', 'Tabbing', ['all', 'last']), ]), this.rule(), - this.checkbox('AssistiveMml', 'assistiveMml'), + this.checkbox('Options/AssistiveMml', 'assistiveMml'), ]), this.rule(), this.command('About', () => this.about()), @@ -886,7 +886,7 @@ export class Menu { }) as MJContextMenu; const menu = this.menu; menu.settings = this.settings; - menu.findID('Settings', 'WideExpressions', 'Elide').disable(); + menu.findID('Settings', 'Wide/Expressions', 'Elide').disable(); menu.findID('Braille', 'ueb').hide(); menu.setJax(this.jax); this.checkLoadableItems(); diff --git a/ts/ui/menu/__locales__/de.json b/ts/ui/menu/__locales__/de.json index 605b3f4e2..56ae3a950 100644 --- a/ts/ui/menu/__locales__/de.json +++ b/ts/ui/menu/__locales__/de.json @@ -1,8 +1,18 @@ { + "A11yLanguage": "Sprache", + "About": "Über MathJax", "About/DocType": "Dokumenttyp: %1", "About/InputJax": "Einbabe Jax: %1", "About/Modules": "Geladene Module:", "About/OutputJax": "Ausgabe Jax: %1", + "AutoVoicing": "Automatische Sprachausgabe", + "Braille": "\u00A0 \u00A0 Braille", + "Braille/Combine": "Mit Sprachausgabe kombinieren", + "Braille/Speech": "Sprachausgabe ersetzen", + "Braille/euro": "Euro", + "Braille/nemeth": "Nemeth", + "Braille/ueb": "UEB", + "Clearspeak": "Clearspeak", "Clearspeak/CurrentPrefs": "Aktuelle Einstellungen", "Clearspeak/NoPrefs": "Keine Einstellungen", "Clearspeak/PrefsFor": "Einstellungen für %1", @@ -10,6 +20,9 @@ "Clearspeak/Title": "Clearspeak Einstellungen", "Copy": "In Zwischenablage kopieren", "Copy/Annotation": "Annotationen", + "Explorer": " \u00A0 \u00A0 Explorer", + "Generate": "Erzeugen", + "Help": "MathJax Hilfe", "Help/Message": "

MathJax ist eine JavaScript-Bibliothek, die es Seitenautoren ermöglicht, mathematische Formeln in ihre Webseiten einzubinden. Als Leser müssen Sie nichts tun, um dies zu nutzen.

Browser: MathJax funktioniert in allen modernen Browsern, einschließlich Edge, Firefox, Chrome, Safari, Opera und den meisten mobilen Browsern.

Mathematik-Menü: MathJax fügt den Formeln ein Kontextmenü hinzu. Klicken Sie mit der rechten Maustaste oder bei gedrückter STRG-Taste auf eine beliebige mathematische Formel, um das Menü aufzurufen.

Mathematik anzeigen als: Mit diesen Optionen können Sie den Quellcode der Formel (als MathML oder im Originalformat) anzeigen.

In die Zwischenablage kopieren: Diese Befehle kopieren den Quellcode der Formel als MathML oder im Originalformat in die Zwischenablage (wenn der Browser dies unterstützt).

Mathematik-Einstellungen: Hiermit können Sie Funktionen von MathJax steuern, wie z. B. die Größe der mathematischen Ausdrücke, den Mechanismus zur Darstellung von Gleichungen und den Umgang mit zu breiten Gleichungen, sowie die Sprache, die für die Menüs und Fehlermeldungen von MathJax verwendet werden soll.

Barrierefreiheit: MathJax kann mit Bildschirmleseprogrammen zusammenarbeiten, um Mathematik für Sehbehinderte zugänglich zu machen. Aktivieren Sie die Sprach- oder Braille-Generierung, um die Erstellung von Sprachausgaben und die Möglichkeit zur Interaktion mit Ausdrücken zu ermöglichen. Sie können den Stil des Explorers über dessen Menü steuern.

Mathematik-Zoom: Wenn Sie Schwierigkeiten haben, eine Gleichung zu lesen, kann MathJax die Darstellung vergrößern, damit Sie sie besser erkennen können, oder Sie können alle mathematischen Formeln auf der Seite vergrößern. Aktivieren Sie diese Funktionen im Menü Mathematik-Einstellungen.

Einstellungen: MathJax nutzt die localStorage-Datenbank Ihres Browsers, um von Ihnen gewählte Menüeinstellungen in Ihrem Browser zu speichern. Diese werden weder an MathJax übertragen noch zu Nutzerverfolgung genutzt.

", "Help/Title": "MathJax Hilfe", "Highlight": "Highlight", @@ -27,10 +40,23 @@ "Highlight/TreeColoring": "Baumfärbung", "Highlight/White": "Weiß", "Highlight/Yellow": "Gelb", + "Label/Accessibility": "\u00A0\u00A0 Barrierefreiheit:", + "Label/Code": "Codeformat:", + "Label/Rules": "Regeln:", + "MML/semantics": "Original als Anmerkung", + "MML/showSRE": "Semantische Attribute", + "MML/showTex": "LaTeX Attribute", + "MML/texHints": "TeX Hinweise", "Magnification": "Vergrößerung", "Magnification/Keyboard": "Tastatur", "Magnification/Mouse": "Maus", "Magnification/None": "Keine", + "Mathspeak": "Mathspeak", + "Options": "\u00A0 \u00A0 Optionen", + "Options/AssistiveMml": "MathML versteckt einbinden", + "Options/AutoCollapse": "Automatisches Ausblenden", + "Options/Collapsible": "Ausblendbare Teilformeln", + "Options/Enrich": "Semantische Anreicherung", "RoleDescription": "Mathematik beschreiben als", "Scale/NonZero": "Der Skalierungsfaktor darf nicht Null sein", "Scale/Percent": "Der Skalierungsfaktor muss ein Prozentsatz sein (z. B. %1)", @@ -39,16 +65,24 @@ "Semantic/Prefix": "Präfix", "Semantic/Role": "Rolle", "Semantic/Type": "Typ", + "Settings": "Einstellungen für Formeln", + "Settings/Language": "Sprache", + "Settings/MathmlIncludes": "MathML/SVG enthält", + "Settings/Renderer": "Formel Renderer", + "Settings/Reset": "Auf Standardwerte zurücksetzen", + "Settings/Scale": "Alle Formeln skalieren...", "Show": "Mathematik anzeigen als", "Show/Annotation": "Anmerkung", - "Show/BrailleCode": "Braille-Code", + "Show/Braille": "Braille-Code", "Show/Commands": "%1 Befehle", "Show/Error": "Fehlermeldung", "Show/MathMLcode": "MathML Code", "Show/Original": "Originalform", "Show/OriginalMathML": "original MathML", - "Show/SpeechText": "Sprachausgabe", - "Show/SvgImage": "SVG Code", + "Show/SVG": "SVG Code", + "Show/Speech": "Sprachausgabe", + "Speech": "\u00A0 \u00A0 Sprache", + "Subtitles": "Untertitel anzeigen", "Svg/NotProduced": "SVG kann nicht erzeugt werden.
Wechseln Sie zunächst zur SVG-Ausgabe.", "Tabbing/InTabOrder": "In Tab-Reihenfolge einbeziehen", "Tabbing/MathHelp": "Hilfemeldung bei Fokus", @@ -68,62 +102,26 @@ "Warn/StorageError": "MathJax localStorage Fehler: %1", "Wide/BreakInline": "Inline-Zeilenumbrüche zulassen", "Wide/Elide": "Auslassen", + "Wide/Expressions": "Breite Ausdrücke", "Wide/Linebreak": "Zeilenumbruch", "Wide/Overflow": "Überlauf", "Wide/Scale": "Skalierung", "Wide/Scroll": "Scrollen", "Wide/Truncate": "Kürzen", - "Wide/WideExpressions": "Breite Ausdrücke", "Zoom/Alt": "Alt", "Zoom/Click": "Klicken", + "Zoom/Command": "Befehl", "Zoom/Control": "Steuerung", "Zoom/DoubleClick": "Doppelklick", "Zoom/NoZoom": "Kein Zoom", + "Zoom/Option": "Option", "Zoom/Shift": "Umschalt", "Zoom/TriggerRequires": "Auslöser erfordert:", "Zoom/ZoomFactor": "Zoomfaktor", "Zoom/ZoomNow": "Jetzt einmal zoomen", "Zoom/ZoomTrigger": "Zoomauslöser", - "A11yLanguage": "Sprache", - "About": "Über MathJax", - "Accessibility": "\u00A0\u00A0 Barrierefreiheit:", - "AssistiveMml": "MathML versteckt einbinden", - "AutoCollapse": "Automatisches Ausblenden", - "AutoVoicing": "Automatische Sprachausgabe", - "Braille": "\u00A0 \u00A0 Braille", - "BrailleCombine": "Mit Sprachausgabe kombinieren", - "BrailleSpeech": "Sprachausgabe ersetzen", - "CHTML": "CHTML", - "Clearspeak": "Clearspeak", - "Code": "Codeformat:", - "Collapsible": "Ausblendbare Teilformeln", - "Command": "Befehl", - "Enrich": "Semantische Anreicherung", - "Explorer": " \u00A0 \u00A0 Explorer", - "Generate": "Erzeugen", - "Help": "MathJax Hilfe", - "Language": "Sprache", - "MathmlIncludes": "MathML/SVG enthält", - "Mathspeak": "Mathspeak", - "Option": "Option", - "Options": "\u00A0 \u00A0 Optionen", - "Renderer": "Formel Renderer", - "Reset": "Auf Standardwerte zurücksetzen", - "Rules": "Regeln:", - "SVG": "SVG", - "ScaleAllMath": "Alle Formeln skalieren...", - "Settings": "Einstellungen für Formeln", - "Speech": "\u00A0 \u00A0 Sprache", - "Subtitles": "Untertitel anzeigen", "clearspeak-default": "Auto", - "euro": "Euro", "mathspeak-brief": "Kurz", "mathspeak-default": "Ausführlich", - "mathspeak-sbrief": "Superkurz", - "nemeth": "Nemeth", - "semantics": "Original als Anmerkung", - "showSRE": "Semantische Attribute", - "showTex": "LaTeX Attribute", - "texHints": "TeX Hinweise", - "ueb": "UEB" + "mathspeak-sbrief": "Superkurz" } diff --git a/ts/ui/menu/__locales__/en.json b/ts/ui/menu/__locales__/en.json index 6d9eb8716..f7eca12a4 100644 --- a/ts/ui/menu/__locales__/en.json +++ b/ts/ui/menu/__locales__/en.json @@ -1,8 +1,18 @@ { + "A11yLanguage": "Language", + "About": "About MathJax", "About/DocType": "Document Type: %1", "About/InputJax": "Input Jax: %1", "About/Modules": "Modules Loaded:", "About/OutputJax": "Outut Jax: %1", + "AutoVoicing": "Auto Voicing", + "Braille": "\u00A0 \u00A0 Braille", + "Braille/Combine": "Combine with Speech", + "Braille/Speech": "Replace Speech", + "Braille/euro": "Euro", + "Braille/nemeth": "Nemeth", + "Braille/ueb": "UEB", + "Clearspeak": "Clearspeak", "Clearspeak/CurrentPrefs": "Current Preferences", "Clearspeak/NoPrefs": "No Preferences", "Clearspeak/PrefsFor": "Preferences for %1", @@ -10,6 +20,9 @@ "Clearspeak/Title": "Clearspeak Preferences", "Copy": "Copy to Clipboard", "Copy/Annotation": "Annotation", + "Explorer": "\u00A0 \u00A0 Explorer", + "Generate": "Generate", + "Help": "MathJax Help", "Help/Message": "

MathJax is a JavaScript library that allows page authors to include mathematics within their web pages. As a reader, you don't need to do anything to make that happen.

Browsers: MathJax works with all modern browsers including Edge, Firefox, Chrome, Safari, Opera, and most mobile browsers.

Math Menu: MathJax adds a contextual menu to equations. Right-click or CTRL-click on any mathematics to access the menu.

Show Math As: These options allow you to view the formula's source markup (as MathML or in its original format).

Copy to Clipboard: These options copy the formula's source markup, as MathML or in its original format, to the clipboard (in browsers that support that).

Math Settings: These give you control over features of MathJax, such the size of the mathematics, the mechanism used to display equations, how to handle equations that are too wide, and the language to use for MathJax's menus and error messages.

Accessibility: MathJax can work with screen readers to make mathematics accessible to the visually impaired. Turn on speech or braille generation to enable creation of speech strings and the ability to investigate expressions interactively. You can control the style of the explorer in its menu.

Math Zoom: If you are having difficulty reading an equation, MathJax can enlarge it to help you see it better, or you can scale all the math on the page to make it larger. Turn these features on in the Math Settings menu.

Preferences: MathJax uses your browser's localStorage database to save the preferences set via this menu locally in your browser. These are not used to track you, and are not transferred or used remotely by MathJax in any way.

", "Help/Title": "MathJax Help", "Highlight": "Highlight", @@ -27,10 +40,23 @@ "Highlight/TreeColoring": "Tree Coloring", "Highlight/White": "White", "Highlight/Yellow": "Yellow", + "Label/Accessibility": "\u00A0\u00A0 Accessibility:", + "Label/Code": "Code Format:", + "Label/Rules": "Rules:", + "MML/semantics": "Original as annotation", + "MML/showSRE": "Semantic attributes", + "MML/showTex": "LaTeX attributes", + "MML/texHints": "TeX hints", "Magnification": "Magnification", "Magnification/Keyboard": "Keyboard", "Magnification/Mouse": "Mouse", "Magnification/None": "None", + "Mathspeak": "Mathspeak", + "Options": "\u00A0 \u00A0 Options", + "Options/AssistiveMml": "Include Hidden MathML", + "Options/AutoCollapse": "Auto Collapse", + "Options/Collapsible": "Collapsible Math", + "Options/Enrich": "Semantic Enrichment", "RoleDescription": "Describe math as", "Scale/NonZero": "The scale should not be zero", "Scale/Percent": "The scale should be a percentage (e.g., %1)", @@ -39,16 +65,24 @@ "Semantic/Prefix": "Prefix", "Semantic/Role": "Role", "Semantic/Type": "Type", + "Settings": "Math Settings", + "Settings/Language": "Language", + "Settings/MathmlIncludes": "MathML/SVG has", + "Settings/Renderer": "Math Renderer", + "Settings/Reset": "Reset to defaults", + "Settings/Scale": "Scale All Math...", "Show": "Show Math As", "Show/Annotation": "Annotation", - "Show/BrailleCode": "Braille Code", + "Show/Braille": "Braille Code", "Show/Commands": "%1 Commands", "Show/Error": "Error Message", "Show/MathMLcode": "MathML Code", "Show/Original": "Original Form", "Show/OriginalMathML": "Original MathML", - "Show/SpeechText": "Speech Text", - "Show/SvgImage": "SVG Image", + "Show/SVG": "SVG Image", + "Show/Speech": "Speech Text", + "Speech": "\u00A0 \u00A0 Speech", + "Subtitles": "Show Subtitles", "Svg/NotProduced": "SVG can't be produced.
Try switching to SVG output first.", "Tabbing/InTabOrder": "Include in Tab Order", "Tabbing/MathHelp": "Help message on focus", @@ -68,12 +102,12 @@ "Warn/StorageError": "MathJax localStorage error: %1", "Wide/BreakInline": "Allow In-line Breaks", "Wide/Elide": "Elide", + "Wide/Expressions": "Wide Expressions", "Wide/Linebreak": "Linebreak", "Wide/Overflow": "Overflow", "Wide/Scale": "Scale", "Wide/Scroll": "Scroll", "Wide/Truncate": "Truncate", - "Wide/WideExpressions": "Wide Expressions", "Zoom/Alt": "Alt", "Zoom/Click": "Click", "Zoom/Command": "Command", @@ -86,42 +120,8 @@ "Zoom/ZoomFactor": "Zoom Factor", "Zoom/ZoomNow": "Zoom Once Now", "Zoom/ZoomTrigger": "Zoom Trigger", - "A11yLanguage": "Language", - "About": "About MathJax", - "Accessibility": "\u00A0\u00A0 Accessibility:", - "AssistiveMml": "Include Hidden MathML", - "AutoCollapse": "Auto Collapse", - "AutoVoicing": "Auto Voicing", - "Braille": "\u00A0 \u00A0 Braille", - "BrailleCombine": "Combine with Speech", - "BrailleSpeech": "Replace Speech", - "Clearspeak": "Clearspeak", - "Code": "Code Format:", - "Collapsible": "Collapsible Math", - "Enrich": "Semantic Enrichment", - "Explorer": "\u00A0 \u00A0 Explorer", - "Generate": "Generate", - "Help": "MathJax Help", - "Language": "Language", - "MathmlIncludes": "MathML/SVG has", - "Mathspeak": "Mathspeak", - "Options": "\u00A0 \u00A0 Options", - "Renderer": "Math Renderer", - "Reset": "Reset to defaults", - "Rules": "Rules:", - "ScaleAllMath": "Scale All Math...", - "Settings": "Math Settings", - "Speech": "\u00A0 \u00A0 Speech", - "Subtitles": "Show Subtitles", "clearspeak-default": "Auto", - "euro": "Euro", "mathspeak-brief": "Brief", "mathspeak-default": "Verbose", - "mathspeak-sbrief": "Superbrief", - "nemeth": "Nemeth", - "semantics": "Original as annotation", - "showSRE": "Semantic attributes", - "showTex": "LaTeX attributes", - "texHints": "TeX hints", - "ueb": "UEB" + "mathspeak-sbrief": "Superbrief" } From 18132b5ec4ecb79939c581acb2413bee21277b99 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Sat, 13 Jun 2026 07:02:20 -0400 Subject: [PATCH 4/5] Add copying of aria localization to the bundle directory --- components/mjs/a11y/explorer/config.json | 20 ++++++++++++++------ components/mjs/ui/menu/config.json | 20 ++++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/components/mjs/a11y/explorer/config.json b/components/mjs/a11y/explorer/config.json index 7289b5c04..5eeeb0b83 100644 --- a/components/mjs/a11y/explorer/config.json +++ b/components/mjs/a11y/explorer/config.json @@ -3,12 +3,20 @@ "component": "a11y/explorer", "targets": ["a11y/explorer.ts", "a11y/explorer"] }, - "copy": { - "to": "[bundle]/a11y/explorer", - "from": "[ts]/a11y/explorer", - "copy": ["__locales__"], - "excludes": ["__locales__/Component.ts"] - }, + "copy": [ + { + "to": "[bundle]/a11y/explorer", + "from": "[ts]/a11y/explorer", + "copy": ["__locales__"], + "excludes": ["__locales__/Component.ts"] + }, + { + "to": "[bundle]/a11y/aria", + "from": "[ts]/a11y/aria", + "copy": ["__locales__"], + "excludes": ["__locales__/Component.ts"] + } + ], "webpack": { "name": "a11y/explorer", "libs": [ diff --git a/components/mjs/ui/menu/config.json b/components/mjs/ui/menu/config.json index 6c49de3d7..78d1796aa 100644 --- a/components/mjs/ui/menu/config.json +++ b/components/mjs/ui/menu/config.json @@ -4,12 +4,20 @@ "targets": ["ui/menu", "a11y/speech/SpeechMenu.ts"], "excludeSubdirs": true }, - "copy": { - "to": "[bundle]/ui/menu", - "from": "[ts]/ui/menu", - "copy": ["__locales__"], - "excludes": ["__locales__/Component.ts"] - }, + "copy": [ + { + "to": "[bundle]/ui/menu", + "from": "[ts]/ui/menu", + "copy": ["__locales__"], + "excludes": ["__locales__/Component.ts"] + }, + { + "to": "[bundle]/a11y/aria", + "from": "[ts]/a11y/aria", + "copy": ["__locales__"], + "excludes": ["__locales__/Component.ts"] + } + ], "webpack": { "name": "ui/menu", "libs": [ From 5a9364e13da1fea68a85078f515f9b36a586929f Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Sat, 13 Jun 2026 07:18:04 -0400 Subject: [PATCH 5/5] Remove useless test and replace --- ts/ui/menu/MJContextMenu.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ts/ui/menu/MJContextMenu.ts b/ts/ui/menu/MJContextMenu.ts index 2a18aac48..5152f0fb6 100644 --- a/ts/ui/menu/MJContextMenu.ts +++ b/ts/ui/menu/MJContextMenu.ts @@ -166,10 +166,7 @@ export class MJContextMenu extends ContextMenu { // (can be removed in a later version) // const remap = map?.[fullname]; - let name = typeof remap === 'string' ? remap : fullname; - if (!name.match('/')) { - name = name.replace(/.*\//, ''); - } + const name = typeof remap === 'string' ? remap : fullname; map = typeof remap === 'object' ? remap : null; // // Look for the id in the menu list