> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/nvaccess/nvda/llms.txt
> Use this file to discover all available pages before exploring further.

# Add-on Distribution

> Package and distribute NVDA add-ons

## Add-on Package Structure

An NVDA add-on is distributed as a `.nvda-addon` file, which is a ZIP archive containing:

<CodeGroup>
  ```text Directory Structure theme={null}
  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
  ```
</CodeGroup>

## Manifest File

The `manifest.ini` file contains required metadata:

<CodeGroup>
  ```ini manifest.ini theme={null}
  name = myAddon
  summary = My NVDA Add-on
  description = A longer description of what this add-on does.
  author = Your Name <your.email@example.com>
  version = 1.0.0
  minimumNVDAVersion = 2023.1.0
  lastTestedNVDAVersion = 2024.1.0
  url = https://github.com/yourusername/myaddon
  docFileName = readme.html
  ```

  ```ini Advanced manifest.ini theme={null}
  name = advancedAddon
  summary = Advanced NVDA Add-on
  description = A comprehensive add-on with many features.
  author = Developer Name
  version = 2.1.3
  minimumNVDAVersion = 2023.1.0
  lastTestedNVDAVersion = 2024.1.0
  url = https://addons.nvda-project.org/addons/advancedAddon.en.html
  docFileName = readme.html
  changelog = """
  Version 2.1.3:
  - Fixed compatibility with NVDA 2024.1
  - Improved braille support

  Version 2.1.0:
  - Added new quick navigation keys
  - Enhanced performance
  """

  # Custom braille tables
  [brailleTables]
  	[[myTable.ctb]]
  		displayName = My Custom Braille Table
  		contracted = false
  		input = true
  		output = true
  ```
</CodeGroup>

### Manifest Fields

<ParamField path="name" type="str" required>
  Unique identifier (lowerCamelCase recommended). Used as the add-on ID.
</ParamField>

<ParamField path="summary" type="str" required>
  Short label shown to users (one line).
</ParamField>

<ParamField path="description" type="str">
  Longer description with more details.
</ParamField>

<ParamField path="author" type="str" required>
  Author name and optionally email.
</ParamField>

<ParamField path="version" type="str" required>
  Version number (semantic versioning recommended: `major.minor.patch`).
</ParamField>

<ParamField path="minimumNVDAVersion" type="str" required>
  Minimum NVDA version required (e.g., `2023.1.0`).
</ParamField>

<ParamField path="lastTestedNVDAVersion" type="str" required>
  Last NVDA version tested with this add-on (e.g., `2024.1.0`).
  Must be >= `minimumNVDAVersion`.
</ParamField>

<ParamField path="url" type="str">
  Homepage or documentation URL (should begin with `https://`).
</ParamField>

<ParamField path="docFileName" type="str">
  Default documentation filename in `doc/` directory.
</ParamField>

<ParamField path="changelog" type="str">
  Version history and changes.
</ParamField>

## Version Compatibility

<Warning>
  **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
</Warning>

```python Checking Versions theme={null}
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:

<CodeGroup>
  ```python installTasks.py theme={null}
  # 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
  	)
  ```

  ```python Migration Example theme={null}
  # installTasks.py - migrating from old version

  import addonHandler
  import config
  import os

  def onInstall():
  	"""Migrate settings from old version."""
  	addon = addonHandler.getCodeAddon()
  	oldConfigPath = os.path.join(
  		config.getUserDefaultConfigPath(),
  		"myAddon.ini"
  	)
  	
  	# Migrate old config file
  	if os.path.exists(oldConfigPath):
  		# Read old config
  		from configobj import ConfigObj
  		oldConfig = ConfigObj(oldConfigPath)
  		
  		# Migrate to new format
  		config.conf["myAddon"] = {
  			"enabled": oldConfig.get("enabled", True),
  			"level": int(oldConfig.get("level", 1))
  		}
  		config.conf.save()
  		
  		# Remove old config
  		os.remove(oldConfigPath)
  ```
</CodeGroup>

<Note>
  Install tasks have access to the add-on's translations via `addon.getTranslationsInstance()`.
</Note>

## Translations

Support multiple languages:

<Steps>
  <Step title="Create POT file">
    Extract translatable strings to a template:

    ```bash theme={null}
    xgettext --language=Python --output=myAddon.pot **/*.py
    ```
  </Step>

  <Step title="Create PO files">
    Create language-specific files:

    ```bash theme={null}
    msginit --input=myAddon.pot --locale=es --output=locale/es/LC_MESSAGES/nvda.po
    ```
  </Step>

  <Step title="Translate strings">
    Edit `.po` files with translations.
  </Step>

  <Step title="Compile to MO">
    ```bash theme={null}
    msgfmt locale/es/LC_MESSAGES/nvda.po -o locale/es/LC_MESSAGES/nvda.mo
    ```
  </Step>

  <Step title="Translate manifest (optional)">
    Create `locale/es/manifest.ini` with translated `summary`, `description`, and `changelog`.
  </Step>
</Steps>

### Using Translations in Code

```python translations.py theme={null}
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:

<CodeGroup>
  ```html doc/en/readme.html theme={null}
  <!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>
  ```

  ```markdown doc/en/readme.md theme={null}
  # My NVDA Add-on

  ## Description

  This add-on provides...

  ## Features

  - Feature 1
  - Feature 2
  - Feature 3

  ## Keyboard Commands

  | Command | Description |
  |---------|-------------|
  | NVDA+Shift+M | Opens main dialog |
  | NVDA+Shift+I | Shows information |

  ## Configuration

  Settings can be found in NVDA Settings > My Add-on.

  ### Available Options

  - **Enable feature**: Turns the main feature on/off
  - **Level**: Sets the verbosity level (0-3)

  ## Compatibility

  This add-on is compatible with NVDA 2023.1 and later.

  ## Changelog

  ### Version 1.0.0
  - Initial release

  ## Support

  For issues or feature requests, please visit:
  https://github.com/user/addon/issues
  ```
</CodeGroup>

## Creating the Package

### Manual Packaging

<Steps>
  <Step title="Prepare directory">
    Create directory with all required files in proper structure.
  </Step>

  <Step title="Create ZIP archive">
    ```bash theme={null}
    cd myAddon
    zip -r ../myAddon-1.0.0.nvda-addon *
    ```

    Or use Python:

    ```python theme={null}
    from addonHandler import createAddonBundleFromPath
    bundle = createAddonBundleFromPath("path/to/myAddon")
    ```
  </Step>

  <Step title="Rename to .nvda-addon">
    Ensure file extension is `.nvda-addon`.
  </Step>
</Steps>

### Using Add-on Template

The [NVDA Add-on Template](https://github.com/nvaccess/AddonTemplate) provides automated packaging:

```bash theme={null}
# 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

<Steps>
  <Step title="Test installation">
    Install the packaged add-on and verify it loads correctly.
  </Step>

  <Step title="Test in multiple NVDA versions">
    Test with your minimum and maximum supported versions.
  </Step>

  <Step title="Test uninstallation">
    Ensure clean uninstall with no leftover files or settings.
  </Step>

  <Step title="Test upgrades">
    Install old version, then upgrade to new version.
  </Step>

  <Step title="Test translations">
    Switch NVDA language and verify translations work.
  </Step>

  <Step title="Review logs">
    Check NVDA log for errors or warnings.
  </Step>
</Steps>

## Submitting to Add-on Store

The official NVDA Add-on Store is the recommended distribution method:

<Steps>
  <Step title="Review submission requirements">
    Read the [Submission Guide](https://github.com/nvaccess/addon-datastore/blob/master/docs/submitters/submissionGuide.md).
  </Step>

  <Step title="Create GitHub repository">
    Host your add-on source on GitHub with proper documentation.
  </Step>

  <Step title="Submit via pull request">
    Submit to the [addon-datastore repository](https://github.com/nvaccess/addon-datastore).
  </Step>

  <Step title="Respond to review">
    Address any feedback from reviewers.
  </Step>

  <Step title="Updates">
    Submit new versions via pull request with updated manifest.
  </Step>
</Steps>

### Store Requirements

<Warning>
  **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)
</Warning>

## 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

```bash theme={null}
# Create release
git tag v1.0.0
git push origin v1.0.0

# Upload .nvda-addon file to GitHub release
```

### Community Add-ons

* [NVDA Community Add-ons](https://github.com/nvdaaddons)
* Collaborative development and distribution

## Version Updates

When releasing updates:

<Steps>
  <Step title="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
  </Step>

  <Step title="Update lastTestedNVDAVersion">
    Test with latest NVDA and update if compatible.
  </Step>

  <Step title="Update changelog">
    Document all changes in `manifest.ini` or separate changelog.
  </Step>

  <Step title="Test thoroughly">
    Test installation, upgrade, and all functionality.
  </Step>

  <Step title="Tag release">
    Create git tag matching version number.
  </Step>
</Steps>

## Best Practices

<Note>
  **Development:**

  * Use semantic versioning
  * Keep comprehensive changelog
  * Test with multiple NVDA versions
  * Provide clear documentation
  * Respond to user issues promptly
  * Follow NVDA coding standards
</Note>

<Warning>
  **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
</Warning>

## Continuous Integration

Automate testing with GitHub Actions:

```yaml .github/workflows/build.yml theme={null}
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

* [NVDA Add-on Store](https://addonstore.nvaccess.org/)
* [Submission Guide](https://github.com/nvaccess/addon-datastore/blob/master/docs/submitters/submissionGuide.md)
* [Add-on Template](https://github.com/nvaccess/AddonTemplate)
* [Community Add-ons](https://github.com/nvdaaddons)
* [NVDA API Mailing List](https://groups.google.com/a/nvaccess.org/g/nvda-api)

## Related Documentation

* [Overview →](/development/addons/overview)
* [App Modules →](/development/addons/app-modules)
* [Global Plugins →](/development/addons/global-plugins)
* [API Reference →](/development/addons/api-reference)
