Skip to content

improve behaviour and docs for mathcat speech style#19740

Merged
seanbudd merged 48 commits into
betafrom
mathSpeechStyles
Mar 20, 2026
Merged

improve behaviour and docs for mathcat speech style#19740
seanbudd merged 48 commits into
betafrom
mathSpeechStyles

Conversation

@seanbudd

@seanbudd seanbudd commented Mar 5, 2026

Copy link
Copy Markdown
Member

Link to issue number:

Closes #19689

Summary of the issue:

MathCAT speech styles depend on the language, however current documentation and handling is incorrect.
We try to store the active speech style in the NVDA config directly, but that value becomes unavailable when switching languages, and so settings dialogs won't be populated.
For example: ClearSpeak, the default, isn't available for all languages such as French.
Instead, we need to store speech styles based on the language, and create smart fallback handling for defaults.

Description of user facing changes:

MathCAT speech styles should now be saved and set correctly when switching languages.

The automatic language should be listed in the math language settings.

Description of developer facing changes:

Removed many parts of the unreleased mathcat API

Description of development approach:

Configuration and Data Model Changes:

  • Speech style configuration is now stored per language under math["speech"][<language>]["speechStyle"], rather than as a global setting. The config spec and related logic have been updated to reflect this. [1] [2] [3]
  • Improve math language handling

Settings Dialog Updates:

  • The Math settings dialog now displays and saves speech style options based on the selected math language. The UI uses localization.getSpeechStyles() directly to populate available speech styles, and saves the selected style under the current language. [1] [2] [3]

Code Simplification and Cleanup:

  • Removed logic for translating and mapping speech style display strings, including the SpeechStyleOption enum and helper functions for display/config value conversion. Type hints and imports have been modernized and cleaned up. [1] [2] [3] [4] [5] [6] [7] [8]

Documentation Updates:

  • The user guide now clarifies that speech style options are language-dependent, and describes the default and available styles for English and other languages. [1] [2]

Testing strategy:

Known issues with pull request:

Code Review Checklist:

  • Documentation:
    • Change log entry
    • User Documentation
    • Developer / Technical Documentation
    • Context sensitive help for GUI changes
  • Testing:
    • Unit tests
    • System (end to end) tests
    • Manual testing
  • UX of all users considered:
    • Speech
    • Braille
    • Low Vision
    • Different web browsers
    • Localization in other languages / culture than English
  • API is compatible with existing add-ons.
  • Security precautions taken.

@seanbudd seanbudd added this to the 2026.1 milestone Mar 5, 2026
@seanbudd seanbudd requested review from a team as code owners March 5, 2026 02:07
@seanbudd seanbudd marked this pull request as draft March 5, 2026 02:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates NVDA’s MathCAT integration so MathCAT speech style is stored per math language (with better fallback behavior), and updates user documentation to clarify language-dependent speech styles.

Changes:

  • Store MathCAT speech style under config.conf["math"]["speech"][<language>]["speechStyle"] instead of a single global setting.
  • Simplify Math settings panel speech-style handling by using localization.getSpeechStyles() directly.
  • Update the User Guide to clarify speech-style availability and defaults are language-dependent.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
user_docs/en/userGuide.md Clarifies that available speech styles (and default) depend on math speech language.
source/mathPres/MathCAT/preferences.py Updates preference defaults/loading to use per-language speech-style config with fallback logic.
source/gui/settingsDialogs.py Updates Math settings UI to load/save speech style under the selected language.
source/config/configSpec.py Adjusts config schema to represent speech style per language under math.speech.
Comments suppressed due to low confidence (2)

source/config/configSpec.py:382

  • This change moves math.speech.speechStyle to a per-language location, which is a schema change. Please bump latestSchemaVersion and add a profileUpgradeSteps migration so existing user configs (with math.speech.speechStyle) are preserved (e.g., copy the old value into math.speech[<currentLanguage>].speechStyle or similar).
[math]
	[[speech]]
		impairment = option("LearningDisability", "Blindness", "LowVision", default="Blindness")
		# any known language code and sub-code -- could be en-uk, etc
		language = string(default="Auto")
		# Set a different speechStyle depending on the language
		[__many__]]
			# Any known speech style for the language (falls back to ClearSpeak)
			speechStyle = string(default="")

source/gui/settingsDialogs.py:2793

  • The speech-style choices are only populated once using the initial mathLanguage. There is no EVT_CHOICE handler for self.languageList, so changing the language in the dialog won't repopulate self.speechStyleList (and can result in saving a style that isn't valid for the newly selected language). Bind a choice-change handler to update the speech-style list (choices + selection) whenever the math language selection changes.
		# Translators: MathCAT language option
		languageText = pgettext("math", "Language:")
		self.languageOptions, self.languageCodes = localization.getLanguages()
		self.languageList = speechGroup.addLabeledControl(
			languageText,
			wx.Choice,
			choices=self.languageOptions,
		)
		self.bindHelpEvent("MathSpeechLanguage", self.languageList)
		mathLanguage = config.conf["math"]["speech"]["language"]
		languageIndex = self.languageCodes.index(mathLanguage)
		self.languageList.SetSelection(languageIndex)

		# Translators: MathCAT decimal separator option.
		decimalSeparatorText = pgettext("math", "Decimal separator for numbers:")
		self.decimalSeparatorList = speechGroup.addLabeledControl(
			decimalSeparatorText,
			wx.Choice,
			choices=[option.displayString for option in DecimalSeparatorOption],
		)
		self.bindHelpEvent("MathSpeechDecimalSeparator", self.decimalSeparatorList)
		self.decimalSeparatorList.SetSelection(
			self._getEnumIndexFromConfigValue(
				DecimalSeparatorOption,
				config.conf["math"]["other"]["decimalSeparator"],
			),
		)

		# Translators: Select a speech style.
		speechStyleText = pgettext("math", "Speech style:")
		self.speechStyleList = speechGroup.addLabeledControl(
			speechStyleText,
			wx.Choice,
			choices=localization.getSpeechStyles(mathLanguage),
		)
		self.bindHelpEvent("MathSpeechStyle", self.speechStyleList)
		speechStyle = config.conf["math"]["speech"][mathLanguage]["speechStyle"]
		if speechStyle:
			self.speechStyleList.SetStringSelection(speechStyle)
		else:
			self.speechStyleList.SetSelection(0)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread source/mathPres/MathCAT/preferences.py
seanbudd and others added 2 commits March 5, 2026 13:14
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@CyrilleB79

Copy link
Copy Markdown
Contributor

Some questions and remarks:

  1. Does this PR address the following point of Math settings: Speech style is empty #19689:

Ideally, the "Automatic" item should also indicate the actual language used by MathCAT, i.e. English when using a French voice.

  1. Today, MathCAT is not translated in French. I may use it in 2026.1, with English fallback, modifying some settings. If I do not modify speech style, i.e. keep default English speech style, what will happen if MathCAT is then translated in French, say for 2026.2? Will speech style be correctly mapped to default French speech style?

Thanks.

@seanbudd seanbudd added the conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review. label Mar 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread source/mathPres/MathCAT/localization.py Outdated
Comment thread source/mathPres/MathCAT/localization.py
Comment thread source/mathPres/MathCAT/preferences.py Outdated
Comment thread source/mathPres/MathCAT/preferences.py
Comment thread source/mathPres/MathCAT/preferences.py Outdated
Comment thread source/gui/settingsDialogs.py Outdated
@seanbudd

Copy link
Copy Markdown
Member Author

Hi @CyrilleB79 - could you open a new issue for the language switching problem?

If speech rules for the main language cannot be found, English ("en") is used.

I think I've clarified this, but I think it's still worth keeping the automatic value in the display string

@Qchristensen Qchristensen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reads well.

@CyrilleB79

Copy link
Copy Markdown
Contributor

Hi @CyrilleB79 - could you open a new issue for the language switching problem?

Done: #19811.
Please note that it's not a language switching problem in the sense of automatic language switching.

Comment thread source/config/configSpec.py Outdated
@CyrilleB79

CyrilleB79 commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

Hi @CyrilleB79 - could you open a new issue for the language switching problem?

If speech rules for the main language cannot be found, English ("en") is used.

I think I've clarified this, but I think it's still worth keeping the automatic value in the display string

But isn't a problem for you to have at the same time:

  • Language set to "Automatic (French (France))" in the GUI
  • Math content read in English

IMO this is really confusing.
Since French is not supported by MathCAT, I do not expect it to appear in the language list, not even in the Automatic option.

Comment thread user_docs/en/userGuide.md Outdated
Comment thread user_docs/en/userGuide.md Outdated
Comment thread user_docs/en/userGuide.md Outdated
Comment thread user_docs/en/userGuide.md Outdated
Comment thread source/gui/settingsDialogs.py Outdated
Comment thread source/gui/settingsDialogs.py
Co-authored-by: Cyrille Bougot <cyrille.bougot2@laposte.net>
Co-authored-by: Sean Budd <seanbudd123@gmail.com>
@seanbudd

Copy link
Copy Markdown
Member Author

IMO this is really confusing.
Since French is not supported by MathCAT, I do not expect it to appear in the language list, not even in the Automatic option.

I understand this may be confusing, but this is the current behaviour in MathCAT and betas. The scope of this PR is already massive. We can consider changing this behaviour in a follow up PR

seanbudd and others added 4 commits March 19, 2026 16:01
Co-authored-by: Sascha Cowley <16543535+SaschaCowley@users.noreply.github.com>
Co-authored-by: Cyrille Bougot <cyrille.bougot2@laposte.net>
@seanbudd seanbudd enabled auto-merge (squash) March 19, 2026 07:55
@CyrilleB79

Copy link
Copy Markdown
Contributor

@seanbudd wrote:

IMO this is really confusing.
Since French is not supported by MathCAT, I do not expect it to appear in the language list, not even in the Automatic option.

I understand this may be confusing, but this is the current behaviour in MathCAT and betas. The scope of this PR is already massive. We can consider changing this behaviour in a follow up PR

Sorry Sean, not sure if we really understand each other. My point here is about what is displayed, not about behaviour.

And no, what is displayed in this PR is not the same as what is displayed in beta:

  • in beta: "Language: Automatic (Use Voice's Language)"
  • in this PR: "Language: Automatic (French (France))"

Displaying the voice language explicitly is more confusing because it let users think that this language has been validated as a supported language for math content reading and that it can actually be used to read math.
On the opposite, just displaying "Use voice's language" can be understood as: "I'll try to use the voice language; but if not supported, I'll fallback to something else ." (i.e. English).
If the user wants to see the language used by the current voice, they can just check the Speech/Voice settings.

To summarize, beta version does not give the impression that math content can be read in French while this PR does.

@seanbudd seanbudd disabled auto-merge March 19, 2026 08:51
@seanbudd

Copy link
Copy Markdown
Member Author

Thanks for explaining further @CyrilleB79 - we've decided to remove the specifier for now and just use "Automatic"

@seanbudd seanbudd enabled auto-merge (squash) March 20, 2026 02:03
@seanbudd seanbudd merged commit 90ee3cd into beta Mar 20, 2026
39 checks passed
@seanbudd seanbudd deleted the mathSpeechStyles branch March 20, 2026 02:52
seanbudd pushed a commit that referenced this pull request Mar 23, 2026
Link to issue number:

Fix-up of #19740
Summary of the issue:

After the merge of #19740, the math panel cannot be displayed and the following error is logged:

ERROR - unhandled exception (15:06:18.687) - MainThread (1844):
Traceback (most recent call last):
  File "gui\settingsDialogs.py", line 6230, in onCategoryChange
    super().onCategoryChange(evt)
    ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
  File "gui\settingsDialogs.py", line 750, in onCategoryChange
    self._doCategoryChange(newIndex)
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^
  File "gui\settingsDialogs.py", line 733, in _doCategoryChange
    newCat = self._getCategoryPanel(newCatId)
  File "gui\settingsDialogs.py", line 660, in _getCategoryPanel
    panel = cls(parent=self.container)
  File "gui\settingsDialogs.py", line 392, in __init__
    self._buildGui()
    ~~~~~~~~~~~~~~^^
  File "gui\settingsDialogs.py", line 402, in _buildGui
    self.makeSettings(self.settingsSizer)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
  File "gui\settingsDialogs.py", line 2771, in makeSettings
    languageIndex = next(filter(lambda idxLang: idxLang[1].code == mathLang, self.languageOptions))[0]
                    ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "gui\settingsDialogs.py", line 2771, in <lambda>
    languageIndex = next(filter(lambda idxLang: idxLang[1].code == mathLang, self.languageOptions))[0]
                                                ~~~~~~~^^^
TypeError: 'LanguageInfo' object is not subscriptable
I

Description of user facing changes:

The math panel can be displayed again.
Description of developer facing changes:

None
Description of development approach:

    Used enumerate on the language list to be able to retrieve its index.
    Also used a generator expression instead of the use of the filter function + lambda since the previous code seemed less readable and less modern Python syntax. This is debatable though, and I can restore the old syntax, just adding enumerate, if you prefer.
    Added a DEBUGWARNING log when the config language is not found in supported languages.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants