Add-on Package Structure
An NVDA add-on is distributed as a .nvda-addon file, which is a ZIP archive containing:
myAddon/
├── manifest.ini # Required: Add-on metadata
├── appModules/ # Optional: Application modules
│ └── myapp.py
├── globalPlugins/ # Optional: Global plugins
│ └── myplugin.py
├── synthDrivers/ # Optional: Speech synthesizers
├── brailleDisplayDrivers/ # Optional: Braille drivers
├── locale/ # Optional: Translations
│ ├── en/
│ │ ├── LC_MESSAGES/
│ │ │ └── nvda.mo
│ │ └── manifest.ini # Translated manifest
│ └── es/
├── doc/ # Optional: Documentation
│ ├── en/
│ │ └── readme.html
│ └── es/
└── installTasks.py # Optional: Install/uninstall tasks
Manifest File
The manifest.ini file contains required metadata:
manifest.ini
Advanced manifest.ini
name = myAddon
summary = My NVDA Add-on
description = A longer description of what this add-on does.
author = Your Name <[email protected] >
version = 1.0.0
minimumNVDAVersion = 2023.1.0
lastTestedNVDAVersion = 2024.1.0
url = https://github.com/yourusername/myaddon
docFileName = readme.html
Manifest Fields
Unique identifier (lowerCamelCase recommended). Used as the add-on ID.
Short label shown to users (one line).
Longer description with more details.
Author name and optionally email.
Version number (semantic versioning recommended: major.minor.patch).
Minimum NVDA version required (e.g., 2023.1.0).
Last NVDA version tested with this add-on (e.g., 2024.1.0).
Must be >= minimumNVDAVersion.
Homepage or documentation URL (should begin with https://).
Default documentation filename in doc/ directory.
Version history and changes.
Version Compatibility
API Version Requirements:
Set minimumNVDAVersion to the oldest NVDA version you support
Set lastTestedNVDAVersion to the latest version you’ve tested
Test your add-on with both the minimum and latest versions
Update lastTestedNVDAVersion when testing with new NVDA releases
import addonAPIVersion
# Current NVDA API version
current = addonAPIVersion. CURRENT # (2024, 1, 0)
# Backwards compatible to
backCompat = addonAPIVersion. BACK_COMPAT_TO # (2023, 1, 0)
# Check if add-on is compatible
from addonHandler.addonVersionCheck import isAddonCompatible
if isAddonCompatible(addon):
# Add-on can run
pass
Install Tasks
Optional installTasks.py module for installation/uninstallation logic:
installTasks.py
Migration Example
# installTasks.py - runs during install/uninstall
import addonHandler
import gui
import wx
def onInstall ():
"""Called when add-on is being installed."""
# Import addon for translations
addon = addonHandler.getCodeAddon()
translations = addon.getTranslationsInstance()
_ = translations.gettext
# Show welcome message
gui.messageBox(
_( "Thank you for installing My Add-on!" ),
_( "Installation Complete" ),
wx. OK | wx. ICON_INFORMATION
)
# Create config if needed
import config
if "myAddon" not in config.conf:
config.conf[ "myAddon" ] = {}
config.conf[ "myAddon" ][ "enabled" ] = True
config.conf.save()
def onUninstall ():
"""Called when add-on is being uninstalled."""
# Clean up configuration
import config
if "myAddon" in config.conf:
del config.conf[ "myAddon" ]
config.conf.save()
# Show farewell
addon = addonHandler.getCodeAddon()
translations = addon.getTranslationsInstance()
_ = translations.gettext
gui.messageBox(
_( "My Add-on has been removed. Thank you for using it!" ),
_( "Uninstallation Complete" ),
wx. OK | wx. ICON_INFORMATION
)
Install tasks have access to the add-on’s translations via addon.getTranslationsInstance().
Translations
Support multiple languages:
Create POT file
Extract translatable strings to a template: xgettext --language=Python --output=myAddon.pot ** / * .py
Create PO files
Create language-specific files: msginit --input=myAddon.pot --locale=es --output=locale/es/LC_MESSAGES/nvda.po
Translate strings
Edit .po files with translations.
Compile to MO
msgfmt locale/es/LC_MESSAGES/nvda.po -o locale/es/LC_MESSAGES/nvda.mo
Translate manifest (optional)
Create locale/es/manifest.ini with translated summary, description, and changelog.
Using Translations in Code
import addonHandler
# Initialize translations (at top of module)
addonHandler.initTranslation()
# Use translation functions
def myFunction ():
# Simple translation
message = _( "Hello, world!" )
# Plural forms
count = 5
message = ngettext(
" {count} item" ,
" {count} items" ,
count
).format( count = count)
# Context (disambiguate same English, different meanings)
message = pgettext( "verb" , "Open" )
message = pgettext( "adjective" , "Open" )
Documentation
Provide user documentation:
doc/en/readme.html
doc/en/readme.md
<! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< title > My NVDA Add-on </ title >
</ head >
< body >
< h1 > My NVDA Add-on </ h1 >
< h2 > Description </ h2 >
< p > This add-on provides... </ p >
< h2 > Features </ h2 >
< ul >
< li > Feature 1 </ li >
< li > Feature 2 </ li >
</ ul >
< h2 > Keyboard Commands </ h2 >
< table >
< tr >
< th > Command </ th >
< th > Description </ th >
</ tr >
< tr >
< td > NVDA+Shift+M </ td >
< td > Opens main dialog </ td >
</ tr >
</ table >
< h2 > Configuration </ h2 >
< p > Settings can be found in NVDA Settings dialog... </ p >
< h2 > Support </ h2 >
< p > For issues, visit: < a href = "https://github.com/user/addon/issues" > GitHub Issues </ a ></ p >
</ body >
</ html >
Creating the Package
Manual Packaging
Prepare directory
Create directory with all required files in proper structure.
Create ZIP archive
cd myAddon
zip -r ../myAddon-1.0.0.nvda-addon *
Or use Python: from addonHandler import createAddonBundleFromPath
bundle = createAddonBundleFromPath( "path/to/myAddon" )
Rename to .nvda-addon
Ensure file extension is .nvda-addon.
Using Add-on Template
The NVDA Add-on Template provides automated packaging:
# Clone template
git clone https://github.com/nvaccess/AddonTemplate.git myAddon
cd myAddon
# Install dependencies
pip install -r requirements.txt
# Edit manifest and add your code
# Build add-on
scons
# Output: myAddon-1.0.0.nvda-addon
Testing Before Distribution
Test installation
Install the packaged add-on and verify it loads correctly.
Test in multiple NVDA versions
Test with your minimum and maximum supported versions.
Test uninstallation
Ensure clean uninstall with no leftover files or settings.
Test upgrades
Install old version, then upgrade to new version.
Test translations
Switch NVDA language and verify translations work.
Review logs
Check NVDA log for errors or warnings.
Submitting to Add-on Store
The official NVDA Add-on Store is the recommended distribution method:
Review submission requirements
Create GitHub repository
Host your add-on source on GitHub with proper documentation.
Respond to review
Address any feedback from reviewers.
Updates
Submit new versions via pull request with updated manifest.
Store Requirements
Submission Requirements:
Source code must be publicly available
Add-on must pass automated tests
Must follow NVDA coding standards
Documentation must be included
License must be compatible (GPL preferred)
Alternative Distribution
Besides the Add-on Store, you can distribute via:
Direct Download
Host .nvda-addon file on your website
Users download and install manually
Include clear installation instructions
GitHub Releases
# Create release
git tag v1.0.0
git push origin v1.0.0
# Upload .nvda-addon file to GitHub release
Version Updates
When releasing updates:
Update version number
Increment version in manifest.ini:
Major (1.0.0 → 2.0.0): Breaking changes
Minor (1.0.0 → 1.1.0): New features
Patch (1.0.0 → 1.0.1): Bug fixes
Update lastTestedNVDAVersion
Test with latest NVDA and update if compatible.
Update changelog
Document all changes in manifest.ini or separate changelog.
Test thoroughly
Test installation, upgrade, and all functionality.
Tag release
Create git tag matching version number.
Best Practices
Development:
Use semantic versioning
Keep comprehensive changelog
Test with multiple NVDA versions
Provide clear documentation
Respond to user issues promptly
Follow NVDA coding standards
Security:
Don’t include sensitive data in add-on
Validate all user input
Use HTTPS for all URLs
Don’t execute arbitrary code
Be careful with file system operations
Continuous Integration
Automate testing with GitHub Actions:
.github/workflows/build.yml
name : Build Add-on
on : [ push , pull_request ]
jobs :
build :
runs-on : windows-latest
steps :
- uses : actions/checkout@v3
- name : Set up Python
uses : actions/setup-python@v4
with :
python-version : '3.11'
- name : Install dependencies
run : |
python -m pip install --upgrade pip
pip install scons markdown
- name : Build add-on
run : scons
- name : Upload artifact
uses : actions/upload-artifact@v3
with :
name : addon-package
path : '*.nvda-addon'
Licensing
Choose an appropriate license:
GPL v2 or later (recommended, same as NVDA)
MIT License
Apache License 2.0
Include COPYING.txt or LICENSE.txt in your add-on.
Resources