From 18b92d711dbdad6741724ef2885e45dd996af7ca Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Sat, 28 Feb 2026 05:42:43 -0700 Subject: [PATCH 1/8] BiuldVars: add custom speech dictionaries support. NVDA 2026.2 introduces custom speech dictionaries support for add-ons. A custom speech dictionary adds pronunciation rules for text. These dictionraies follow NVDA's speech dictionary format (tab separated values) and is named name.dic e.g. addonSpeech.dic and resides in 'speechDicts' top-level (addon) folder. In buildVars, add support for speech dictionraies, deriving comments and definition from custom symbol dictionaries definition. --- buildVars.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/buildVars.py b/buildVars.py index c125fae..cdcffc1 100644 --- a/buildVars.py +++ b/buildVars.py @@ -101,3 +101,12 @@ # displayName (name of the speech dictionary shown to users and translatable), # mandatory (True when always enabled, False when not. symbolDictionaries: SymbolDictionaries = {} + +# Custom speech dictionaries (distinct from symbol dictionaries above) +# Speech dictionary files reside in the speechDicts folder and are named `name.dic`. +# If your add-on includes custom speech (pronunciation) dictionaries (most will not), fill out this dictionary. +# Each key is the name of the dictionary, +# with keys inside recording the following attributes: +# displayName (name of the speech dictionary shown to users and translatable), +# mandatory (True when always enabled, False when not. +speechDictionaries: SpeechDictionaries = {} From 52b0291384809408a85344e4a28e6177b383a4a4 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Sat, 28 Feb 2026 05:43:07 -0700 Subject: [PATCH 2/8] SConstruct: recognize custom speech dictionraies as part of buildVars attribute lookup. --- sconstruct | 1 + 1 file changed, 1 insertion(+) diff --git a/sconstruct b/sconstruct index 481a7ac..cdbad3f 100644 --- a/sconstruct +++ b/sconstruct @@ -61,6 +61,7 @@ env.Append( addon_info=buildVars.addon_info, brailleTables=buildVars.brailleTables, symbolDictionaries=buildVars.symbolDictionaries, + speechDictionaries=buildVars.speechDictionaries, ) if env["dev"]: From c3f98a1de46c92324accbc16192ff53acb6a058f Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Sat, 28 Feb 2026 05:56:12 -0700 Subject: [PATCH 3/8] Site tools/manifest typing: define speech (pronunciation) dictionary attributes (same as symbol dictionary (name/mandatory)) --- site_scons/site_tools/NVDATool/typings.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/site_scons/site_tools/NVDATool/typings.py b/site_scons/site_tools/NVDATool/typings.py index 650a759..0375538 100644 --- a/site_scons/site_tools/NVDATool/typings.py +++ b/site_scons/site_tools/NVDATool/typings.py @@ -30,8 +30,14 @@ class SymbolDictionaryAttributes(TypedDict): mandatory: bool +class SpeechDictionaryAttributes(TypedDict): + displayName: str + mandatory: bool + + BrailleTables = dict[str, BrailleTableAttributes] SymbolDictionaries = dict[str, SymbolDictionaryAttributes] +SpeechDictionaries = dict[str, SpeechDictionaryAttributes] class Strable(Protocol): From 7bb6a42a884ec5697104e827a93cfc100df7b9cc Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Sat, 28 Feb 2026 05:58:10 -0700 Subject: [PATCH 4/8] Site tools/manifest: add speech pronunicatoin dictionary to manifest when building the add-on and localized manifest --- site_scons/site_tools/NVDATool/__init__.py | 4 ++++ site_scons/site_tools/NVDATool/manifests.py | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py index ff31eec..a71857d 100644 --- a/site_scons/site_tools/NVDATool/__init__.py +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -13,6 +13,7 @@ - addon_info: .typing.AddonInfo - brailleTables: .typings.BrailleTables - symbolDictionaries: .typings.SymbolDictionaries +- speechDictionaries: .typings.SpeechDictionaries The following environment variables are required to build the HTML: @@ -49,6 +50,7 @@ def generate(env: Environment): env.SetDefault(brailleTables={}) env.SetDefault(symbolDictionaries={}) + env.SetDefault(speechDictionaries={}) manifestAction = env.Action( lambda target, source, env: generateManifest( @@ -57,6 +59,7 @@ def generate(env: Environment): addon_info=env["addon_info"], brailleTables=env["brailleTables"], symbolDictionaries=env["symbolDictionaries"], + speechDictionaries=env["speechDictionaries"], ) and None, lambda target, source, env: f"Generating manifest {target[0]}", @@ -75,6 +78,7 @@ def generate(env: Environment): addon_info=env["addon_info"], brailleTables=env["brailleTables"], symbolDictionaries=env["symbolDictionaries"], + speechDictionaries=env["speechDictionaries"], ) and None, lambda target, source, env: f"Generating translated manifest {target[0]}", diff --git a/site_scons/site_tools/NVDATool/manifests.py b/site_scons/site_tools/NVDATool/manifests.py index a55785e..7723b0b 100644 --- a/site_scons/site_tools/NVDATool/manifests.py +++ b/site_scons/site_tools/NVDATool/manifests.py @@ -2,7 +2,7 @@ import gettext from functools import partial -from .typings import AddonInfo, BrailleTables, SymbolDictionaries +from .typings import AddonInfo, BrailleTables, SymbolDictionaries, SpeechDictionaries from .utils import format_nested_section @@ -12,6 +12,7 @@ def generateManifest( addon_info: AddonInfo, brailleTables: BrailleTables, symbolDictionaries: SymbolDictionaries, + speechDictionaries: SpeechDictionaries, ): # Prepare the root manifest section with codecs.open(source, "r", "utf-8") as f: @@ -26,6 +27,10 @@ def generateManifest( if symbolDictionaries: manifest += format_nested_section("symbolDictionaries", symbolDictionaries) + # Custom speech pronunciation dictionaries + if speechDictionaries: + manifest += format_nested_section("speechDictionaries", speechDictionaries) + with codecs.open(dest, "w", "utf-8") as f: f.write(manifest) @@ -38,6 +43,7 @@ def generateTranslatedManifest( addon_info: AddonInfo, brailleTables: BrailleTables, symbolDictionaries: SymbolDictionaries, + speechDictionaries: SpeechDictionaries, ): with open(mo, "rb") as f: _ = gettext.GNUTranslations(f).gettext @@ -63,5 +69,9 @@ def generateTranslatedManifest( if symbolDictionaries: manifest += _format_section_only_with_displayName("symbolDictionaries", symbolDictionaries) + # Custom speech pronunciation dictionaries + if speechDictionaries: + manifest += _format_section_only_with_displayName("speechDictionaries", speechDictionaries) + with codecs.open(dest, "w", "utf-8") as f: f.write(manifest) From cd23da52c2e21d3a9aea2fa8dc6552215a616b98 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Sat, 28 Feb 2026 06:18:05 -0700 Subject: [PATCH 5/8] Readme: add information on custom speech pronunciation dictionaries --- readme.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/readme.md b/readme.md index 9ad847a..41a50bb 100644 --- a/readme.md +++ b/readme.md @@ -142,6 +142,16 @@ Information on custom symbol dictionaries must be specified in buildVars under ` Note: you must fill out this dictionary if at least one custom symbol dictionary is included in the add-on. If not, leave the dictionary empty. +###### Speech pronunciation dictionaries + +Information on custom speech (pronunciation) dictionaries must be specified in buildVars under `speechDictionaries` dictionary as follows: + +* Dictionary name (string key for a nested dictionary): each `symbolDictionaries` entry is a name for the included custom speech dictionary placed in `speechDicts` folder inside `addon` folder. The file is named `.dic`. This nested dictionary should specify: + * displayName (string): the name of the dictionary shown to users and is translatable. + * mandatory (True/False): Always enabled (True) or optional and visible in the GUI (False) + +Note: you must fill out this dictionary if at least one custom speech dictionary is included in the add-on. If not, leave the dictionary empty. + ### To manage documentation files for your addon: 1. Copy the `readme.md` file for your add-on to the first created folder, where you copied `buildVars.py`. You can also copy `style.css` to improve the presentation of HTML documents. From 80ed9cefe85bdc3d413af2c1aeeec6323a3bcb24 Mon Sep 17 00:00:00 2001 From: Sean Budd Date: Mon, 2 Mar 2026 15:06:10 +1100 Subject: [PATCH 6/8] Apply suggestions from code review --- readme.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 41a50bb..117eb2c 100644 --- a/readme.md +++ b/readme.md @@ -146,11 +146,14 @@ Note: you must fill out this dictionary if at least one custom symbol dictionary Information on custom speech (pronunciation) dictionaries must be specified in buildVars under `speechDictionaries` dictionary as follows: -* Dictionary name (string key for a nested dictionary): each `symbolDictionaries` entry is a name for the included custom speech dictionary placed in `speechDicts` folder inside `addon` folder. The file is named `.dic`. This nested dictionary should specify: +* Dictionary name (string key for a nested dictionary): each `symbolDictionaries` entry is a name for the included custom speech dictionary placed in `speechDicts` folder inside `addon` folder. +The file is named `.dic`. +This nested dictionary should specify: * displayName (string): the name of the dictionary shown to users and is translatable. * mandatory (True/False): Always enabled (True) or optional and visible in the GUI (False) -Note: you must fill out this dictionary if at least one custom speech dictionary is included in the add-on. If not, leave the dictionary empty. +Note: you must fill out this dictionary if at least one custom speech dictionary is included in the add-on. +If not, leave the dictionary empty. ### To manage documentation files for your addon: From a0b80b2c8ce74f23235a5e10fb64782fe73fca0e Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 2 Mar 2026 05:01:18 -0700 Subject: [PATCH 7/8] BuildVars: add missing parentheses Co-authored-by: Sean Budd --- buildVars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildVars.py b/buildVars.py index cdcffc1..568e1a0 100644 --- a/buildVars.py +++ b/buildVars.py @@ -108,5 +108,5 @@ # Each key is the name of the dictionary, # with keys inside recording the following attributes: # displayName (name of the speech dictionary shown to users and translatable), -# mandatory (True when always enabled, False when not. +# mandatory (True when always enabled, False when not). speechDictionaries: SpeechDictionaries = {} From 491876c44d694e9374d164c90aa049730bf8108d Mon Sep 17 00:00:00 2001 From: Sean Budd Date: Tue, 3 Mar 2026 10:46:34 +1100 Subject: [PATCH 8/8] Update buildVars.py --- buildVars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildVars.py b/buildVars.py index 568e1a0..decefe4 100644 --- a/buildVars.py +++ b/buildVars.py @@ -99,7 +99,7 @@ # Each key is the name of the dictionary, # with keys inside recording the following attributes: # displayName (name of the speech dictionary shown to users and translatable), -# mandatory (True when always enabled, False when not. +# mandatory (True when always enabled, False when not). symbolDictionaries: SymbolDictionaries = {} # Custom speech dictionaries (distinct from symbol dictionaries above)