From 48cf94da89e575de386c308a4ed4051183f0e5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noelia=20Ruiz=20Mart=C3=ADnez?= Date: Sat, 7 Mar 2026 10:21:23 +0100 Subject: [PATCH 1/2] Use new template --- .../workflows/checkTranslatorsComments.yml | 8 +- .github/workflows/manualRelease.yaml | 12 +- .gitignore | 6 +- .pre-commit-config.yaml | 41 ++- .vscode/extensions.json | 12 +- .vscode/settings.json | 37 +-- .vscode/typings/__builtins__.pyi | 2 - buildVars.py | 112 +++++--- manifest-translated.ini.tpl | 1 + manifest.ini.tpl | 1 + pyproject.toml | 154 +++++------ sconstruct | 246 ++++++------------ site_scons/site_tools/NVDATool/__init__.py | 110 ++++++++ site_scons/site_tools/NVDATool/addon.py | 23 ++ site_scons/site_tools/NVDATool/docs.py | 59 +++++ site_scons/site_tools/NVDATool/manifests.py | 67 +++++ site_scons/site_tools/NVDATool/typings.py | 38 +++ site_scons/site_tools/NVDATool/utils.py | 27 ++ 18 files changed, 598 insertions(+), 358 deletions(-) create mode 100644 site_scons/site_tools/NVDATool/__init__.py create mode 100644 site_scons/site_tools/NVDATool/addon.py create mode 100644 site_scons/site_tools/NVDATool/docs.py create mode 100644 site_scons/site_tools/NVDATool/manifests.py create mode 100644 site_scons/site_tools/NVDATool/typings.py create mode 100644 site_scons/site_tools/NVDATool/utils.py diff --git a/.github/workflows/checkTranslatorsComments.yml b/.github/workflows/checkTranslatorsComments.yml index e12b8aae..e9fecb1b 100644 --- a/.github/workflows/checkTranslatorsComments.yml +++ b/.github/workflows/checkTranslatorsComments.yml @@ -17,12 +17,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - - name: Set up Python 3.8 - uses: actions/setup-python@v5 + - name: Set up Python 3.13 + uses: actions/setup-python@v6 with: - python-version: 3.11 + python-version: 3.13 - name: Install dependencies run: | diff --git a/.github/workflows/manualRelease.yaml b/.github/workflows/manualRelease.yaml index 6aaaefb4..93822143 100644 --- a/.github/workflows/manualRelease.yaml +++ b/.github/workflows/manualRelease.yaml @@ -20,15 +20,17 @@ jobs: buildAndUpload: continue-on-error: true runs-on: ubuntu-latest + permissions: + contents: write steps: - id: checkoutCode name: Checkout code - uses: actions/checkout@v4 - - name: Set up Python 3.11 - uses: actions/setup-python@v5 + uses: actions/checkout@v5 + - name: Set up Python 3.13 + uses: actions/setup-python@v6 with: - python-version: 3.11 + python-version: 3.13 - name: Install dependencies run: | pip install scons markdown @@ -40,7 +42,7 @@ jobs: with open("buildVars.py", 'r+', encoding='utf-8') as f: text = f.read() version = "${{ github.event.inputs.version }}" - text = re.sub(r"\"addon_version\": .*?,", f"\"addon_version\": \"{version}\",", text) + text = re.sub(r"addon_version=\".*?\",", f"addon_version=\"{version}\",", text) f.seek(0) f.write(text) f.truncate() diff --git a/.gitignore b/.gitignore index 64dbf228..0be8af1c 100755 --- a/.gitignore +++ b/.gitignore @@ -2,10 +2,10 @@ addon/doc/*.css addon/doc/en/ *_docHandler.py *.html -addon/*.ini -addon/locale/*/*.ini +manifest.ini *.mo *.pot -*.pyc +*.py[co] *.nvda-addon .sconsign.dblite +/[0-9]*.[0-9]*.[0-9]*.json diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ee931ed3..207177da 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,15 +1,16 @@ - +# Copied from https://github.com/nvaccess/nvda # https://pre-commit.ci/ # Configuration for Continuous Integration service ci: - skip: [pyrightLocal] + # Pyright does not seem to work in pre-commit CI + skip: [pyright] autoupdate_schedule: monthly autoupdate_commit_msg: "Pre-commit auto-update" autofix_commit_msg: "Pre-commit auto-fix" submodules: true default_language_version: - python: python3.11 + python: python3.13 repos: - repo: https://github.com/pre-commit-ci/pre-commit-ci-config @@ -29,7 +30,7 @@ repos: hooks: # Prevents commits to certain branches - id: no-commit-to-branch - args: ["--branch", "main"] + args: ["--branch", "main", "--branch", "master", ] # Checks that large files have not been added. Default cut-off for "large" files is 500kb. - id: check-added-large-files # Checks python syntax @@ -42,26 +43,24 @@ repos: - id: debug-statements # Removes trailing whitespace. - id: trailing-whitespace - types_or: [python, markdown, toml, yaml] + types_or: [python, c, c++, batch, markdown, toml, yaml, powershell] # Ensures all files end in 1 (and only 1) newline. - id: end-of-file-fixer - types_or: [python, markdown, toml, yaml] + types_or: [python, c, c++, batch, markdown, toml, yaml, powershell] # Removes the UTF-8 BOM from files that have it. # See https://github.com/nvaccess/nvda/blob/master/projectDocs/dev/codingStandards.md#encoding - id: fix-byte-order-marker - types_or: [python, markdown, toml, yaml] + types_or: [python, c, c++, batch, markdown, toml, yaml, powershell] # Validates TOML files. - id: check-toml # Validates YAML files. - id: check-yaml - # Validates XML files. # Ensures that links to lines in files under version control point to a particular commit. - id: check-vcs-permalinks # Avoids using reserved Windows filenames. - id: check-illegal-windows-names - - repo: https://github.com/asottile/add-trailing-comma - rev: v3.1.0 + rev: v3.2.0 hooks: # Ruff preserves indent/new-line formatting of function arguments, list items, and similar iterables, # if a trailing comma is added. @@ -70,7 +69,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Matches Ruff version in pyproject. - rev: v0.8.1 + rev: v0.12.7 hooks: - id: ruff name: lint with ruff @@ -78,19 +77,11 @@ repos: - id: ruff-format name: format with ruff -- repo: https://github.com/RobertCraigie/pyright-python - rev: v1.1.394 +- repo: local hooks: - - id: pyright - alias: pyrightLocal - name: Check types with pyright -- repo: https://github.com/RobertCraigie/pyright-python - rev: v1.1.396 - hooks: - - id: pyright - alias: pyrightCI - name: Check types with pyright - # use nodejs version of pyright and install pyproject.toml for CI - additional_dependencies: [".", "pyright[nodejs]"] - stages: [manual] # Only run from CI manually + - id: pyright + name: type check with pyright + entry: uv run pyright + language: system + types: [python] diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 68496ec8..854ca858 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,18 +3,10 @@ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp // List of extensions which should be recommended for users of this workspace. "recommendations": [ - "ms-vscode.cpptools", "ms-python.python", "ms-python.vscode-pylance", - "cschlosser.doxdocgen", - "davidanson.vscode-markdownlint", - "ms-vscode.powershell", - "charliermarsh.ruff", - "editorconfig.editorconfig", - "njpwerner.autodocstring", - "ms-python.debugpy", - "ms-vscode.remote-server", - "benjamin-simmonds.pythoncpp-debug", + "redhat.vscode-yaml", + "charliermarsh.ruff" ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [] diff --git a/.vscode/settings.json b/.vscode/settings.json index ec0e2b37..057f5a3c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,37 +1,16 @@ { "editor.accessibilitySupport": "on", - "ruff.enable": true, - "python.defaultInterpreterPath": "${workspaceFolder}/../nvda/.venv/Scripts/python.exe", "python.autoComplete.extraPaths": [ - "addon", - "${workspaceFolder}/../nvda/source", - "${workspaceFolder}/../nvda/miscDeps/python" + "../nvda/source", + "../nvda/miscDeps/python" ], - "python.analysis.extraPaths": [ - "addon", - "${workspaceFolder}/../nvda/source", - "${workspaceFolder}/../nvda/miscDeps/python" - ], - "python.analysis.stubPath": "${workspaceFolder}/.vscode/typings", "files.insertFinalNewline": true, "files.trimFinalNewlines": true, - "files.eol": "\n", "editor.insertSpaces": false, - "python.testing.unittestArgs": [ - "-v", - "-s", - "tests.unit", - "-p", - "test_*.py" + "python.analysis.stubPath": "${workspaceFolder}/.vscode/typings", + "python.analysis.extraPaths": [ + "../nvda/source", + "../nvda/miscDeps/python" ], - "autoDocstring.docstringFormat": "sphinx-notypes", - "python.testing.pytestEnabled": false, - "python.testing.nosetestsEnabled": false, - "python.testing.unittestEnabled": true, - "[python]": { - "editor.defaultFormatter": "charliermarsh.ruff" - }, - "python.analysis.autoImportCompletions": true, - "python.analysis.importFormat": "relative" - } - \ No newline at end of file + "python.defaultInterpreterPath": "${workspaceFolder}/../nvda/.venv/scripts/python.exe" +} diff --git a/.vscode/typings/__builtins__.pyi b/.vscode/typings/__builtins__.pyi index 0ce73b77..48cbde4d 100644 --- a/.vscode/typings/__builtins__.pyi +++ b/.vscode/typings/__builtins__.pyi @@ -1,4 +1,2 @@ def _(msg: str) -> str: ... def pgettext(context: str, message: str) -> str: ... -def ngettext(msgid1: str, msgid2: str, n: int) -> str: ... -def npgettext(context: str, msgid1: str, msgid2: str, n: int) -> str: ... diff --git a/buildVars.py b/buildVars.py index 9d765f80..d126bdb6 100755 --- a/buildVars.py +++ b/buildVars.py @@ -1,61 +1,101 @@ -# -*- coding: UTF-8 -*- -import os.path # Build customizations # Change this file instead of sconstruct or manifest files, whenever possible. -# Full getext (please don't change) -_ = lambda x: x +from site_scons.site_tools.NVDATool.typings import AddonInfo, BrailleTables, SymbolDictionaries + +# Since some strings in `addon_info` are translatable, +# we need to include them in the .po files. +# Gettext recognizes only strings given as parameters to the `_` function. +# To avoid initializing translations in this module we simply import a "fake" `_` function +# which returns whatever is given to it as an argument. +from site_scons.site_tools.NVDATool.utils import _ + # Add-on information variables -addon_info = { - # for previously unpublished addons, please follow the community guidelines at: - # https://bitbucket.org/nvdaaddonteam/todo/raw/master/guideLines.txt - # add-on Name, internal for nvda - "addon_name": "placeMarkers", - # Add-on summary, usually the user visible name of the addon. - # Translators: Summary for this add-on to be shown on installation and add-on information. - "addon_summary": _("Place markers"), +addon_info = AddonInfo( + # add-on Name/identifier, internal for NVDA + addon_name="placeMarkers", + # Add-on summary/title, usually the user visible name of the add-on + # Translators: Summary/title for this add-on + # to be shown on installation and add-on information found in add-on store + addon_summary=_("Place markers"), # Add-on description - # Translators: Long description to be shown for this add-on on add-on information from add-ons manager - "addon_description": _("Add-on for setting place markers on specific virtual documents"), + # Translators: Long description to be shown for this add-on on add-on information from add-on store + addon_description=_("Add-on for setting place markers on specific virtual documents"), # version - "addon_version": "50.0.0", + addon_version="50.0.0", + # Brief changelog for this version + # Translators: what's new content for the add-on version to be shown in the add-on store + addon_changelog=_("* Compatible with NVDA 2026.1."), # Author(s) - "addon_author": "Noelia , Chris ", + addon_author="Noelia , Chris ", # URL for the add-on documentation support - "addon_url": "https://github.com/nvdaes/placeMarkers", + addon_url="https://github.com/nvdaes/placeMarkers", + # URL for the add-on repository where the source code can be found + addon_sourceURL=None, # Documentation file name - "addon_docFileName": "readme.html", - # Minimum NVDA version supported (e.g. "2018.3") - "addon_minimumNVDAVersion": "2025.2", - # Last NVDA version supported/tested (e.g. "2018.4", ideally more recent than minimum version) - "addon_lastTestedNVDAVersion": "2025.2", - # Add-on update channel (default is stable or None) - "addon_updateChannel": None, -} - + addon_docFileName="readme.html", + # Minimum NVDA version supported (e.g. "2019.3.0", minor version is optional) + addon_minimumNVDAVersion="2026.1", + # Last NVDA version supported/tested (e.g. "2024.4.0", ideally more recent than minimum version) + addon_lastTestedNVDAVersion="2026.1", + # Add-on update channel (default is None, denoting stable releases, + # and for development releases, use "dev".) + # Do not change unless you know what you are doing! + addon_updateChannel=None, + # Add-on license such as GPL 2 + addon_license=None, + # URL for the license document the ad-on is licensed under + addon_licenseURL=None, +) # Define the python files that are the sources of your add-on. -# You can use glob expressions here, they will be expanded. -pythonSources = [ - os.path.join("addon", "*.py"), - os.path.join("addon", "globalPlugins", "placeMarkers", "*.py"), -] +# You can either list every file (using ""/") as a path separator, +# or use glob expressions. +# For example to include all files with a ".py" extension from the "globalPlugins" dir of your add-on +# the list can be written as follows: +# pythonSources = ["addon/globalPlugins/*.py"] +# For more information on SCons Glob expressions please take a look at: +# https://scons.org/doc/production/HTML/scons-user/apd.html +pythonSources: list[str] = ["addon/globalPlugins/*.py", "addon/globalPlugins/placeMarkers/*.py"] # Files that contain strings for translation. Usually your python sources -i18nSources = pythonSources + ["buildVars.py"] +i18nSources: list[str] = pythonSources + ["buildVars.py"] # Files that will be ignored when building the nvda-addon file # Paths are relative to the addon directory, not to the root directory of your addon sources. -excludedFiles = [] +# You can either list every file (using ""/") as a path separator, +# or use glob expressions. +excludedFiles: list[str] = [] + # Base language for the NVDA add-on -# If your add-on is written in a language other than english, modify this variable. +# If your add-on is written in a language other than english, modify this variable. # For example, set baseLanguage to "es" if your add-on is primarily written in spanish. -baseLanguage = "en" +# You must also edit .gitignore file to specify base language files to be ignored. +baseLanguage: str = "en" # Markdown extensions for add-on documentation # Most add-ons do not require additional Markdown extensions. # If you need to add support for markup such as tables, fill out the below list. # Extensions string must be of the form "markdown.extensions.extensionName" # e.g. "markdown.extensions.tables" to add tables. -markdownExtensions = [] +markdownExtensions: list[str] = [] + +# Custom braille translation tables +# If your add-on includes custom braille tables (most will not), fill out this dictionary. +# Each key is a dictionary named according to braille table file name, +# with keys inside recording the following attributes: +# displayName (name of the table shown to users and translatable), +# contracted (contracted (True) or uncontracted (False) braille code), +# output (shown in output table list), +# input (shown in input table list). +brailleTables: BrailleTables = {} + +# Custom speech symbol dictionaries +# Symbol dictionary files reside in the locale folder, e.g. `locale\en`, and are named `symbols-.dic`. +# If your add-on includes custom speech symbol 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. +symbolDictionaries: SymbolDictionaries = {} diff --git a/manifest-translated.ini.tpl b/manifest-translated.ini.tpl index c06aa842..6df6d42e 100755 --- a/manifest-translated.ini.tpl +++ b/manifest-translated.ini.tpl @@ -1,2 +1,3 @@ summary = "{addon_summary}" description = """{addon_description}""" +changelog = """{addon_changelog}""" diff --git a/manifest.ini.tpl b/manifest.ini.tpl index d44355dd..2b7b0eb3 100755 --- a/manifest.ini.tpl +++ b/manifest.ini.tpl @@ -4,6 +4,7 @@ description = """{addon_description}""" author = "{addon_author}" url = {addon_url} version = {addon_version} +changelog = """{addon_changelog}""" docFileName = {addon_docFileName} minimumNVDAVersion = {addon_minimumNVDAVersion} lastTestedNVDAVersion = {addon_lastTestedNVDAVersion} diff --git a/pyproject.toml b/pyproject.toml index 894268da..aa8752d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,31 +1,45 @@ [build-system] -requires = ["setuptools~=72.0", "wheel"] +requires = ["setuptools~=80.9", "wheel"] build-backend = "setuptools.build_meta" [project] -name = "placeMarkers" +name = "addonTemplate" dynamic = ["version"] -description = "placeMarkers add-on for NVDA" +description = "NVDA add-on template" maintainers = [ - {name = "Noelia ruiz Martínez", email = "nrm1977@gmail.com"}, + {name = "NV Access", email = "info@nvaccess.org"}, ] -requires-python = ">=3.11,<3.12" +requires-python = ">=3.13,<3.14" classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: End Users/Desktop", - "License :: OSI Approved :: GNU General Public License v2", + "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", "Operating System :: Microsoft :: Windows", "Programming Language :: Python :: 3", "Topic :: Accessibility", ] readme = "readme.md" -license = {file = "LICENSE"} +license = {file = "COPYING.TXT"} dependencies = [ - "wxPython==4.2.2", + # Build add-on + "scons==4.10.1", + "Markdown==3.10", + # Translations management + "requests==2.32.5", + "nh3==0.3.2", + "crowdin-api-client==1.24.1", + "lxml==6.0.2", + "mdx_truly_sane_lists==1.3", + "markdown-link-attr-modifier==0.2.1", + "mdx-gh-links==0.4", + # Lint + "uv==0.9.11", + "ruff==0.14.5", + "pre-commit==4.2.0", + "pyright[nodejs]==1.1.407", ] - [project.urls] -Repository = "https://github.com/nvdaes/placeMarkers" +Repository = "https://github.com/nvaccess/addonTemplate" [tool.ruff] line-length = 110 @@ -73,7 +87,7 @@ logger-objects = ["logHandler.log"] "sconstruct" = ["F821"] [tool.pyright] -venvPath = "../nvda/.venv" +venvPath = ".venv" venv = "." pythonPlatform = "Windows" typeCheckingMode = "strict" @@ -87,6 +101,7 @@ exclude = [ ".git", "__pycache__", ".venv", + "site_scons", # When excluding concrete paths relative to a directory, # not matching multiple folders by name e.g. `__pycache__`, # paths are relative to the configuration file. @@ -109,102 +124,89 @@ strictDictionaryInference = true strictSetInference = true # Compliant rules +reportAbstractUsage = true +reportArgumentType = true reportAssertAlwaysTrue = true reportAssertTypeFailure = true +reportAssignmentType = true +reportAttributeAccessIssue = true +reportCallInDefaultInitializer = true +reportCallIssue = true +reportConstantRedefinition = true reportDuplicateImport = true +reportFunctionMemberAccess = true +reportGeneralTypeIssues = true +reportImplicitOverride = true +reportImplicitStringConcatenation = true +reportImportCycles = true +reportIncompatibleMethodOverride = true +reportIncompatibleVariableOverride = true reportIncompleteStub = true -reportInconsistentOverload = true reportInconsistentConstructor = true +reportInconsistentOverload = true +reportIndexIssue = true reportInvalidStringEscapeSequence = true reportInvalidStubStatement = true +reportInvalidTypeArguments = true +reportInvalidTypeForm = true reportInvalidTypeVarUse = true reportMatchNotExhaustive = true -reportMissingModuleSource = true reportMissingImports = true +reportMissingModuleSource = true +reportMissingParameterType = true +reportMissingSuperCall = true +reportMissingTypeArgument = true reportNoOverloadImplementation = true +reportOperatorIssue = true +reportOptionalCall = true reportOptionalContextManager = true +reportOptionalIterable = true +reportOptionalMemberAccess = true +reportOptionalOperand = true +reportOptionalSubscript = true reportOverlappingOverload = true +reportPossiblyUnboundVariable = true reportPrivateImportUsage = true +reportPrivateUsage = true reportPropertyTypeMismatch = true +reportRedeclaration = true +reportReturnType = true reportSelfClsParameterName = true -reportShadowedImports = true reportTypeCommentUsage = true reportTypedDictNotRequiredAccess = true -reportUndefinedVariable = true -reportUnusedExpression = true reportUnboundVariable = true +reportUndefinedVariable = true reportUnhashable = true +reportUninitializedInstanceVariable = true +reportUnknownArgumentType = true +reportUnknownLambdaType = true +reportUnknownMemberType = true +reportUnknownParameterType = true +reportUnknownVariableType = true reportUnnecessaryCast = true +reportUnnecessaryComparison = true reportUnnecessaryContains = true +reportUnnecessaryIsInstance = true reportUnnecessaryTypeIgnoreComment = true +reportUnsupportedDunderAll = true +reportUntypedBaseClass = true reportUntypedClassDecorator = true reportUntypedFunctionDecorator = true +reportUntypedNamedTuple = true +reportUnusedCallResult = true reportUnusedClass = true reportUnusedCoroutine = true reportUnusedExcept = true +reportUnusedExpression = true +reportUnusedFunction = true +reportUnusedImport = true +reportUnusedVariable = true +reportWildcardImportFromLibrary = true reportDeprecated = true # Can be enabled by generating type stubs for modules via pyright CLI reportMissingTypeStubs = false -reportUnsupportedDunderAll = false -reportAbstractUsage = false -reportUntypedBaseClass = false -reportOptionalIterable = false -reportCallInDefaultInitializer = false -reportInvalidTypeArguments = false -reportUntypedNamedTuple = false -reportRedeclaration = false -reportOptionalCall = false -reportConstantRedefinition = false -reportWildcardImportFromLibrary = false -reportIncompatibleVariableOverride = false -reportInvalidTypeForm = false -reportGeneralTypeIssues = false -reportOptionalOperand = false -reportUnnecessaryComparison = false -reportFunctionMemberAccess = false -reportUnnecessaryIsInstance = false -reportUnusedFunction = false -reportImportCycles = false -reportUnusedImport = false -reportUnusedVariable = false -reportOperatorIssue = false -reportAssignmentType = false -reportReturnType = false -reportPossiblyUnboundVariable = false -reportMissingSuperCall = false -reportUninitializedInstanceVariable = false -reportUnknownLambdaType = false -reportMissingTypeArgument = false -reportImplicitStringConcatenation = false -reportIncompatibleMethodOverride = false -reportPrivateUsage = false -reportUnusedCallResult = false -reportOptionalSubscript = false -reportCallIssue = false -reportOptionalMemberAccess = false -reportImplicitOverride = false -reportIndexIssue = false -reportAttributeAccessIssue = false -reportArgumentType = false -reportUnknownParameterType = false -reportMissingParameterType = false -reportUnknownVariableType = false -reportUnknownArgumentType = false -reportUnknownMemberType = false - -[dependency-groups] -dev = [ - "SCons==4.8.1", - "setuptools~=72.0", -] -lint = [ - "ruff==0.8.1", - "pre-commit==4.0.1", - "pyright==1.1.396", -] - -[tool.setuptools.dynamic] -version = {attr = "buildVersion.version_detailed"} +# Bad rules +# These are sorted alphabetically and should be enabled and moved to compliant rules section when resolved. diff --git a/sconstruct b/sconstruct index c1a306f3..481a7ac8 100755 --- a/sconstruct +++ b/sconstruct @@ -1,208 +1,103 @@ -# NVDA add-on template SCONSTRUCT file -# Copyright (C) 2012-2021 Rui Batista, Noelia Martinez, Joseph Lee +# NVDA add-on template SCONSTRUCT file +# Copyright (C) 2012-2025 Rui Batista, Noelia Martinez, Joseph Lee # This file is covered by the GNU General Public License. # See the file COPYING.txt for more details. -import codecs -import gettext import os import os.path -import zipfile import sys +from pathlib import Path +from collections.abc import Iterable +from typing import Final # While names imported below are available by default in every SConscript # Linters aren't aware about them. -# To avoid Flake8 F821 warnings about them they are imported explicitly. +# To avoid PyRight `reportUndefinedVariable` errors about them they are imported explicitly. # When using other Scons functions please add them to the line below. -from SCons.Script import BoolVariable, Builder, Copy, Environment, Variables +from SCons.Script import EnsurePythonVersion, Variables, BoolVariable, Environment, Copy -sys.dont_write_bytecode = True +# Imports for type hints +from SCons.Node import FS + +# Add-on localization exchange facility and the template requires Python 3.10. +# For best practice, use Python 3.11 or later to align with NVDA development. +EnsurePythonVersion(3, 10) # Bytecode should not be written for build vars module to keep the repository root folder clean. +sys.dont_write_bytecode = True + import buildVars # NOQA: E402 -def md2html(source, dest): - import markdown - - # Use extensions if defined. - mdExtensions = buildVars.markdownExtensions - lang = os.path.basename(os.path.dirname(source)).replace("_", "-") - localeLang = os.path.basename(os.path.dirname(source)) - try: - _ = gettext.translation( - "nvda", localedir=os.path.join("addon", "locale"), languages=[localeLang] - ).gettext - summary = _(buildVars.addon_info["addon_summary"]) - except Exception: - summary = buildVars.addon_info["addon_summary"] - title = "{addonSummary} {addonVersion}".format( - addonSummary=summary, addonVersion=buildVars.addon_info["addon_version"] - ) - headerDic = { - '[[!meta title="': "# ", - '"]]': " #", - } - with codecs.open(source, "r", "utf-8") as f: - mdText = f.read() - for k, v in headerDic.items(): - mdText = mdText.replace(k, v, 1) - htmlText = markdown.markdown(mdText, extensions=mdExtensions) - # Optimization: build resulting HTML text in one go instead of writing parts separately. - docText = "\n".join( - [ - "", - '' % lang, - "", - '', - '', - "%s" % title, - "\n", - htmlText, - "\n", - ] - ) - with codecs.open(dest, "w", "utf-8") as f: - f.write(docText) +def validateVersionNumber(key: str, val: str, _): + # Used to make sure version major.minor.patch are integers to comply with NV Access add-on store. + # Ignore all this if version number is not specified. + if val == "0.0.0": + return + versionNumber = val.split(".") + if len(versionNumber) < 3: + raise ValueError(f"{key} must have three parts (major.minor.patch)") + if not all([part.isnumeric() for part in versionNumber]): + raise ValueError(f"{key} (major.minor.patch) must be integers") -def mdTool(env): - mdAction = env.Action( - lambda target, source, env: md2html(source[0].path, target[0].path), - lambda target, source, env: "Generating % s" % target[0], - ) - mdBuilder = env.Builder( - action=mdAction, - suffix=".html", - src_suffix=".md", - ) - env["BUILDERS"]["markdown"] = mdBuilder +def expandGlobs(patterns: Iterable[str], rootdir: Path = Path(".")) -> list[FS.Entry]: + return [env.Entry(e) for pattern in patterns for e in rootdir.glob(pattern.lstrip('/'))] + + +addonDir: Final = Path("addon/") +localeDir: Final = addonDir / "locale" +docsDir: Final = addonDir / "doc" vars = Variables() vars.Add("version", "The version of this build", buildVars.addon_info["addon_version"]) +vars.Add("versionNumber", "Version number of the form major.minor.patch", "0.0.0", validateVersionNumber) vars.Add(BoolVariable("dev", "Whether this is a daily development version", False)) vars.Add("channel", "Update channel for this build", buildVars.addon_info["addon_updateChannel"]) -env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", mdTool]) -env.Append(**buildVars.addon_info) +env = Environment(variables=vars, ENV=os.environ, tools=["gettexttool", "NVDATool"]) +env.Append( + addon_info=buildVars.addon_info, + brailleTables=buildVars.brailleTables, + symbolDictionaries=buildVars.symbolDictionaries, +) if env["dev"]: - import datetime + from datetime import date - buildDate = datetime.datetime.now() - year, month, day = str(buildDate.year), str(buildDate.month), str(buildDate.day) - env["addon_version"] = "".join([year, month.zfill(2), day.zfill(2), "-dev"]) + versionTimestamp = date.today().strftime('%Y%m%d') + version = f"{versionTimestamp}.0.0" + env["addon_info"]["addon_version"] = version + env["versionNumber"] = version env["channel"] = "dev" elif env["version"] is not None: - env["addon_version"] = env["version"] + env["addon_info"]["addon_version"] = env["version"] if "channel" in env and env["channel"] is not None: - env["addon_updateChannel"] = env["channel"] - -buildVars.addon_info["addon_version"] = env["addon_version"] -buildVars.addon_info["addon_updateChannel"] = env["addon_updateChannel"] - -addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") - - -def addonGenerator(target, source, env, for_signature): - action = env.Action( - lambda target, source, env: createAddonBundleFromPath(source[0].abspath, target[0].abspath) and None, - lambda target, source, env: "Generating Addon %s" % target[0], - ) - return action - - -def manifestGenerator(target, source, env, for_signature): - action = env.Action( - lambda target, source, env: generateManifest(source[0].abspath, target[0].abspath) and None, - lambda target, source, env: "Generating manifest %s" % target[0], - ) - return action - - -def translatedManifestGenerator(target, source, env, for_signature): - dir = os.path.abspath(os.path.join(os.path.dirname(str(source[0])), "..")) - lang = os.path.basename(dir) - action = env.Action( - lambda target, source, env: generateTranslatedManifest(source[1].abspath, lang, target[0].abspath) - and None, - lambda target, source, env: "Generating translated manifest %s" % target[0], - ) - return action - - -env["BUILDERS"]["NVDAAddon"] = Builder(generator=addonGenerator) -env["BUILDERS"]["NVDAManifest"] = Builder(generator=manifestGenerator) -env["BUILDERS"]["NVDATranslatedManifest"] = Builder(generator=translatedManifestGenerator) - - -def createAddonHelp(dir): - docsDir = os.path.join(dir, "doc") - if os.path.isfile("style.css"): - cssPath = os.path.join(docsDir, "style.css") - cssTarget = env.Command(cssPath, "style.css", Copy("$TARGET", "$SOURCE")) - env.Depends(addon, cssTarget) - if os.path.isfile("readme.md"): - readmePath = os.path.join(docsDir, buildVars.baseLanguage, "readme.md") - readmeTarget = env.Command(readmePath, "readme.md", Copy("$TARGET", "$SOURCE")) - env.Depends(addon, readmeTarget) + env["addon_info"]["addon_updateChannel"] = env["channel"] +# This is necessary for further use in formatting file names. +env.Append(**env["addon_info"]) -def createAddonBundleFromPath(path, dest): - """Creates a bundle from a directory that contains an addon manifest file.""" - basedir = os.path.abspath(path) - with zipfile.ZipFile(dest, "w", zipfile.ZIP_DEFLATED) as z: - # FIXME: the include/exclude feature may or may not be useful. Also python files can be pre-compiled. - for dir, dirnames, filenames in os.walk(basedir): - relativePath = os.path.relpath(dir, basedir) - for filename in filenames: - pathInBundle = os.path.join(relativePath, filename) - absPath = os.path.join(dir, filename) - if pathInBundle not in buildVars.excludedFiles: - z.write(absPath, pathInBundle) - return dest +addonFile = env.File("${addon_name}-${addon_version}.nvda-addon") +addon = env.NVDAAddon(addonFile, env.Dir(addonDir), excludePatterns=buildVars.excludedFiles) -def generateManifest(source, dest): - addon_info = buildVars.addon_info - with codecs.open(source, "r", "utf-8") as f: - manifest_template = f.read() - manifest = manifest_template.format(**addon_info) - with codecs.open(dest, "w", "utf-8") as f: - f.write(manifest) - - -def generateTranslatedManifest(source, language, out): - _ = gettext.translation("nvda", localedir=os.path.join("addon", "locale"), languages=[language]).gettext - vars = {} - for var in ("addon_summary", "addon_description"): - vars[var] = _(buildVars.addon_info[var]) - with codecs.open(source, "r", "utf-8") as f: - manifest_template = f.read() - result = manifest_template.format(**vars) - with codecs.open(out, "w", "utf-8") as f: - f.write(result) - - -def expandGlobs(files): - return [f for pattern in files for f in env.Glob(pattern)] - - -addon = env.NVDAAddon(addonFile, env.Dir("addon")) - -langDirs = [f for f in env.Glob(os.path.join("addon", "locale", "*"))] +langDirs: list[FS.Dir] = [env.Dir(d) for d in env.Glob(localeDir/"*/") if d.isdir()] # Allow all NVDA's gettext po files to be compiled in source/locale, and manifest files to be generated +moByLang: dict[str, FS.File] = {} for dir in langDirs: poFile = dir.File(os.path.join("LC_MESSAGES", "nvda.po")) - moFile = env.gettextMoFile(poFile) - env.Depends(moFile, poFile) + moTarget = env.gettextMoFile(poFile) + moFile = env.File(moTarget[0]) + moByLang[dir.name] = moFile + env.Depends(moTarget, poFile) translatedManifest = env.NVDATranslatedManifest( - dir.File("manifest.ini"), [moFile, os.path.join("manifest-translated.ini.tpl")] + dir.File("manifest.ini"), [moFile, "manifest-translated.ini.tpl"] ) env.Depends(translatedManifest, ["buildVars.py"]) - env.Depends(addon, [translatedManifest, moFile]) + env.Depends(addon, [translatedManifest, moTarget]) pythonFiles = expandGlobs(buildVars.pythonSources) for file in pythonFiles: @@ -210,15 +105,30 @@ for file in pythonFiles: # Convert markdown files to html # We need at least doc in English and should enable the Help button for the add-on in Add-ons Manager -createAddonHelp("addon") -for mdFile in env.Glob(os.path.join("addon", "doc", "*", "*.md")): - htmlFile = env.markdown(mdFile) +if (cssFile := Path("style.css")).is_file(): + cssPath = docsDir / cssFile + cssTarget = env.Command(str(cssPath), str(cssFile), Copy("$TARGET", "$SOURCE")) + env.Depends(addon, cssTarget) + +if (readmeFile := Path("readme.md")).is_file(): + readmePath = docsDir / buildVars.baseLanguage / readmeFile + readmeTarget = env.Command(str(readmePath), str(readmeFile), Copy("$TARGET", "$SOURCE")) + env.Depends(addon, readmeTarget) + +for mdFile in env.Glob(docsDir/"*/*.md"): + # the title of the html file is translated based on the contents of something in the moFile for a language. + # Thus, we find the moFile for this language and depend on it if it exists. + lang = mdFile.dir.name + moFile = moByLang.get(lang) + htmlFile = env.md2html(mdFile, moFile=moFile, mdExtensions=buildVars.markdownExtensions) env.Depends(htmlFile, mdFile) + if moFile: + env.Depends(htmlFile, moFile) env.Depends(addon, htmlFile) # Pot target i18nFiles = expandGlobs(buildVars.i18nSources) -gettextvars = { +gettextvars: dict[str, str] = { "gettext_package_bugs_address": "nvda-translations@groups.io", "gettext_package_name": buildVars.addon_info["addon_name"], "gettext_package_version": buildVars.addon_info["addon_version"], @@ -232,7 +142,7 @@ env.Alias("mergePot", mergePot) env.Depends(mergePot, i18nFiles) # Generate Manifest path -manifest = env.NVDAManifest(os.path.join("addon", "manifest.ini"), os.path.join("manifest.ini.tpl")) +manifest = env.NVDAManifest(env.File(addonDir/"manifest.ini"), "manifest.ini.tpl") # Ensure manifest is rebuilt if buildVars is updated. env.Depends(manifest, "buildVars.py") diff --git a/site_scons/site_tools/NVDATool/__init__.py b/site_scons/site_tools/NVDATool/__init__.py new file mode 100644 index 00000000..ff31eecd --- /dev/null +++ b/site_scons/site_tools/NVDATool/__init__.py @@ -0,0 +1,110 @@ +""" +This tool generates NVDA extensions. + +Builders: + +- NVDAAddon: Creates a .nvda-addon zip file. Requires the `excludePatterns` environment variable. +- NVDAManifest: Creates the manifest.ini file. +- NVDATranslatedManifest: Creates the manifest.ini file with only translated information. +- md2html: Build HTML from Markdown + +The following environment variables are required to create the manifest: + +- addon_info: .typing.AddonInfo +- brailleTables: .typings.BrailleTables +- symbolDictionaries: .typings.SymbolDictionaries + +The following environment variables are required to build the HTML: + +- moFile: str | pathlib.Path | None +- mdExtensions: list[str] +- addon_info: .typings.AddonInfo + +""" + +from SCons.Script import Environment, Builder + +from .addon import createAddonBundleFromPath +from .manifests import generateManifest, generateTranslatedManifest +from .docs import md2html + + +def generate(env: Environment): + env.SetDefault(excludePatterns=tuple()) + + addonAction = env.Action( + lambda target, source, env: createAddonBundleFromPath( + source[0].abspath, + target[0].abspath, + env["excludePatterns"], + ) + and None, + lambda target, source, env: f"Generating Addon {target[0]}", + ) + env["BUILDERS"]["NVDAAddon"] = Builder( + action=addonAction, + suffix=".nvda-addon", + src_suffix="/", + ) + + env.SetDefault(brailleTables={}) + env.SetDefault(symbolDictionaries={}) + + manifestAction = env.Action( + lambda target, source, env: generateManifest( + source[0].abspath, + target[0].abspath, + addon_info=env["addon_info"], + brailleTables=env["brailleTables"], + symbolDictionaries=env["symbolDictionaries"], + ) + and None, + lambda target, source, env: f"Generating manifest {target[0]}", + ) + env["BUILDERS"]["NVDAManifest"] = Builder( + action=manifestAction, + suffix=".ini", + src_siffix=".ini.tpl", + ) + + translatedManifestAction = env.Action( + lambda target, source, env: generateTranslatedManifest( + source[1].abspath, + target[0].abspath, + mo=source[0].abspath, + addon_info=env["addon_info"], + brailleTables=env["brailleTables"], + symbolDictionaries=env["symbolDictionaries"], + ) + and None, + lambda target, source, env: f"Generating translated manifest {target[0]}", + ) + + env["BUILDERS"]["NVDATranslatedManifest"] = Builder( + action=translatedManifestAction, + suffix=".ini", + src_siffix=".ini.tpl", + ) + + env.SetDefault(mdExtensions={}) + + mdAction = env.Action( + lambda target, source, env: md2html( + source[0].path, + target[0].path, + moFile=env["moFile"].path if env["moFile"] else None, + mdExtensions=env["mdExtensions"], + addon_info=env["addon_info"], + ) + and None, + lambda target, source, env: f"Generating {target[0]}", + ) + env["BUILDERS"]["md2html"] = env.Builder( + action=mdAction, + suffix=".html", + src_suffix=".md", + ) + + +def exists(): + return True diff --git a/site_scons/site_tools/NVDATool/addon.py b/site_scons/site_tools/NVDATool/addon.py new file mode 100644 index 00000000..7d67516e --- /dev/null +++ b/site_scons/site_tools/NVDATool/addon.py @@ -0,0 +1,23 @@ +import zipfile +from collections.abc import Iterable +from pathlib import Path + + +def matchesNoPatterns(path: Path, patterns: Iterable[str]) -> bool: + """Checks if the path, the first argument, does not match any of the patterns passed as the second argument.""" + return not any((path.match(pattern) for pattern in patterns)) + + +def createAddonBundleFromPath(path: str | Path, dest: str, excludePatterns: Iterable[str]): + """Creates a bundle from a directory that contains an addon manifest file.""" + if isinstance(path, str): + path = Path(path) + basedir = path.absolute() + with zipfile.ZipFile(dest, "w", zipfile.ZIP_DEFLATED) as z: + for p in basedir.rglob("*"): + if p.is_dir(): + continue + pathInBundle = p.relative_to(basedir) + if matchesNoPatterns(pathInBundle, excludePatterns): + z.write(p, pathInBundle) + return dest diff --git a/site_scons/site_tools/NVDATool/docs.py b/site_scons/site_tools/NVDATool/docs.py new file mode 100644 index 00000000..e1f80ade --- /dev/null +++ b/site_scons/site_tools/NVDATool/docs.py @@ -0,0 +1,59 @@ +import gettext +from pathlib import Path + +import markdown + +from .typings import AddonInfo + + +def md2html( + source: str | Path, + dest: str | Path, + *, + moFile: str | Path | None, + mdExtensions: list[str], + addon_info: AddonInfo, +): + if isinstance(source, str): + source = Path(source) + if isinstance(dest, str): + dest = Path(dest) + if isinstance(moFile, str): + moFile = Path(moFile) + + try: + with moFile.open("rb") as f: + _ = gettext.GNUTranslations(f).gettext + except Exception: + summary = addon_info["addon_summary"] + else: + summary = _(addon_info["addon_summary"]) + version = addon_info["addon_version"] + title = f"{summary} {version}" + lang = source.parent.name.replace("_", "-") + headerDic = { + '[[!meta title="': "# ", + '"]]': " #", + } + with source.open("r", encoding="utf-8") as f: + mdText = f.read() + for k, v in headerDic.items(): + mdText = mdText.replace(k, v, 1) + htmlText = markdown.markdown(mdText, extensions=mdExtensions) + # Optimization: build resulting HTML text in one go instead of writing parts separately. + docText = "\n".join( + ( + "", + f'', + "", + '', + '', + '', + f"{title}", + "\n", + htmlText, + "\n", + ), + ) + with dest.open("w", encoding="utf-8") as f: + f.write(docText) # type: ignore diff --git a/site_scons/site_tools/NVDATool/manifests.py b/site_scons/site_tools/NVDATool/manifests.py new file mode 100644 index 00000000..a55785eb --- /dev/null +++ b/site_scons/site_tools/NVDATool/manifests.py @@ -0,0 +1,67 @@ +import codecs +import gettext +from functools import partial + +from .typings import AddonInfo, BrailleTables, SymbolDictionaries +from .utils import format_nested_section + + +def generateManifest( + source: str, + dest: str, + addon_info: AddonInfo, + brailleTables: BrailleTables, + symbolDictionaries: SymbolDictionaries, +): + # Prepare the root manifest section + with codecs.open(source, "r", "utf-8") as f: + manifest_template = f.read() + manifest = manifest_template.format(**addon_info) + # Add additional manifest sections such as custom braile tables + # Custom braille translation tables + if brailleTables: + manifest += format_nested_section("brailleTables", brailleTables) + + # Custom speech symbol dictionaries + if symbolDictionaries: + manifest += format_nested_section("symbolDictionaries", symbolDictionaries) + + with codecs.open(dest, "w", "utf-8") as f: + f.write(manifest) + + +def generateTranslatedManifest( + source: str, + dest: str, + *, + mo: str, + addon_info: AddonInfo, + brailleTables: BrailleTables, + symbolDictionaries: SymbolDictionaries, +): + with open(mo, "rb") as f: + _ = gettext.GNUTranslations(f).gettext + vars: dict[str, str] = {} + for var in ("addon_summary", "addon_description", "addon_changelog"): + vars[var] = _(addon_info[var]) + with codecs.open(source, "r", "utf-8") as f: + manifest_template = f.read() + manifest = manifest_template.format(**vars) + + _format_section_only_with_displayName = partial( + format_nested_section, + include_only_keys=("displayName",), + _=_, + ) + + # Add additional manifest sections such as custom braile tables + # Custom braille translation tables + if brailleTables: + manifest += _format_section_only_with_displayName("brailleTables", brailleTables) + + # Custom speech symbol dictionaries + if symbolDictionaries: + manifest += _format_section_only_with_displayName("symbolDictionaries", symbolDictionaries) + + with codecs.open(dest, "w", "utf-8") as f: + f.write(manifest) diff --git a/site_scons/site_tools/NVDATool/typings.py b/site_scons/site_tools/NVDATool/typings.py new file mode 100644 index 00000000..650a7599 --- /dev/null +++ b/site_scons/site_tools/NVDATool/typings.py @@ -0,0 +1,38 @@ +from typing import TypedDict, Protocol + + +class AddonInfo(TypedDict): + addon_name: str + addon_summary: str + addon_description: str + addon_version: str + addon_changelog: str + addon_author: str + addon_url: str | None + addon_sourceURL: str | None + addon_docFileName: str + addon_minimumNVDAVersion: str | None + addon_lastTestedNVDAVersion: str | None + addon_updateChannel: str | None + addon_license: str | None + addon_licenseURL: str | None + + +class BrailleTableAttributes(TypedDict): + displayName: str + contracted: bool + output: bool + input: bool + + +class SymbolDictionaryAttributes(TypedDict): + displayName: str + mandatory: bool + + +BrailleTables = dict[str, BrailleTableAttributes] +SymbolDictionaries = dict[str, SymbolDictionaryAttributes] + + +class Strable(Protocol): + def __str__(self) -> str: ... diff --git a/site_scons/site_tools/NVDATool/utils.py b/site_scons/site_tools/NVDATool/utils.py new file mode 100644 index 00000000..c9008412 --- /dev/null +++ b/site_scons/site_tools/NVDATool/utils.py @@ -0,0 +1,27 @@ +from collections.abc import Callable, Container, Mapping + +from .typings import Strable + + +def _(arg: str) -> str: + """ + A function that passes the string to it without doing anything to it. + Needed for recognizing strings for translation by Gettext. + """ + return arg + + +def format_nested_section( + section_name: str, + data: Mapping[str, Mapping[str, Strable]], + include_only_keys: Container[str] | None = None, + _: Callable[[str], str] = _, +) -> str: + lines = [f"\n[{section_name}]"] + for item_name, inner_dict in data.items(): + lines.append(f"[[{item_name}]]") + for key, val in inner_dict.items(): + if include_only_keys and key not in include_only_keys: + continue + lines.append(f"{key} = {_(str(val))}") + return "\n".join(lines) + "\n" From 217b380d2385d52bc1f08846d1a7156969b2ee8d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 7 Mar 2026 09:24:12 +0000 Subject: [PATCH 2/2] Pre-commit auto-fix --- addon/doc/ar/readme.md | 2 +- addon/doc/bg/readme.md | 1 - addon/doc/da/readme.md | 1 - addon/doc/de/readme.md | 1 - addon/doc/es/readme.md | 1 - addon/doc/fi/readme.md | 1 - addon/doc/fr/readme.md | 1 - addon/doc/gl/readme.md | 1 - addon/doc/hr/readme.md | 1 - addon/doc/it/readme.md | 1 - addon/doc/ja/readme.md | 1 - addon/doc/ne/readme.md | 2 +- addon/doc/pl/readme.md | 1 - addon/doc/pt_BR/readme.md | 1 - addon/doc/pt_PT/readme.md | 1 - addon/doc/ro/readme.md | 1 - addon/doc/ru/readme.md | 1 - addon/doc/sk/readme.md | 1 - addon/doc/tr/readme.md | 1 - addon/doc/uk/readme.md | 1 - addon/doc/vi/readme.md | 1 - addon/doc/zh_CN/readme.md | 1 - 22 files changed, 2 insertions(+), 22 deletions(-) diff --git a/addon/doc/ar/readme.md b/addon/doc/ar/readme.md index 2cf2c86b..435b3920 100644 --- a/addon/doc/ar/readme.md +++ b/addon/doc/ar/readme.md @@ -116,7 +116,7 @@ bookmarks. ## مستجدات الإصدار 4.0 ## * إزالة معرف الجزيآت باسم ملف العلامة المرجعية, والتي كانت تتسبب في حدوث بعض - المشكلات مع إضافة فيرفوكس ePUBREADER + المشكلات مع إضافة فيرفوكس ePUBREADER * إمكانية الوصول لملف المساعدة الخاص بالإضافة من مدير الإضافات البرمجية ## مستجدات الإصدار 3.1 ## diff --git a/addon/doc/bg/readme.md b/addon/doc/bg/readme.md index c15c4a17..80abe905 100644 --- a/addon/doc/bg/readme.md +++ b/addon/doc/bg/readme.md @@ -237,4 +237,3 @@ Bookmark&Search, разработени от същия автор. Трябва испански, словашки, словенски, Тамил. [[!tag dev stable]] - diff --git a/addon/doc/da/readme.md b/addon/doc/da/readme.md index 4996456e..9fcf8152 100644 --- a/addon/doc/da/readme.md +++ b/addon/doc/da/readme.md @@ -229,4 +229,3 @@ placemarkers. slovakisk, slovensk og tamilsk. [[!tag dev stable]] - diff --git a/addon/doc/de/readme.md b/addon/doc/de/readme.md index f9ae9f50..73aef897 100644 --- a/addon/doc/de/readme.md +++ b/addon/doc/de/readme.md @@ -243,4 +243,3 @@ empfielt sich daher stattdessen Suchanfragen zu verwenden. Portugiesisch, Spanisch, Slovakisch, Slovenisch, Tamil. [[!tag dev stable]] - diff --git a/addon/doc/es/readme.md b/addon/doc/es/readme.md index 46fba0dc..7cd7a268 100644 --- a/addon/doc/es/readme.md +++ b/addon/doc/es/readme.md @@ -236,4 +236,3 @@ y no las marcas. Portugués del Brasil, Tamil. [[!tag dev stable]] - diff --git a/addon/doc/fi/readme.md b/addon/doc/fi/readme.md index 001ca274..582d6f01 100644 --- a/addon/doc/fi/readme.md +++ b/addon/doc/fi/readme.md @@ -223,4 +223,3 @@ paikkamerkkien asemesta sivukohtaista hakua. ja tamili. [[!tag dev stable]] - diff --git a/addon/doc/fr/readme.md b/addon/doc/fr/readme.md index c5ee880f..bd1eef61 100644 --- a/addon/doc/fr/readme.md +++ b/addon/doc/fr/readme.md @@ -248,4 +248,3 @@ spécifique, pas les marqueurs. Slovaque, Slovène, Tamil. [[!tag dev stable]] - diff --git a/addon/doc/gl/readme.md b/addon/doc/gl/readme.md index 391bb04e..225b3bcc 100644 --- a/addon/doc/gl/readme.md +++ b/addon/doc/gl/readme.md @@ -231,4 +231,3 @@ placemarkers. Xaponés. [[!tag dev stable]] - diff --git a/addon/doc/hr/readme.md b/addon/doc/hr/readme.md index 22f24847..6b09fad9 100644 --- a/addon/doc/hr/readme.md +++ b/addon/doc/hr/readme.md @@ -223,4 +223,3 @@ placemarkers. španjolski, slovački, slovenski, tamilski. [[!tag dev stable]] - diff --git a/addon/doc/it/readme.md b/addon/doc/it/readme.md index 98671951..0bdfe8f4 100644 --- a/addon/doc/it/readme.md +++ b/addon/doc/it/readme.md @@ -235,4 +235,3 @@ placemarkers. spagnolo, slovacco, sloveno, tamil. [[!tag dev stable]] - diff --git a/addon/doc/ja/readme.md b/addon/doc/ja/readme.md index 11264fa0..9c108542 100644 --- a/addon/doc/ja/readme.md +++ b/addon/doc/ja/readme.md @@ -178,4 +178,3 @@ placemarkers. ブラジルポルトガル語、ファルシ(ペルシア)語、フィンランド語、フランス語、ガリシア語、ドイツ語、イタリア語、日本語、韓国語、ネパール語、ポルトガル語、スペイン語、スロバキア語、スロベニア語、タミル語。 [[!tag dev stable]] - diff --git a/addon/doc/ne/readme.md b/addon/doc/ne/readme.md index 8f616e7d..12451f57 100644 --- a/addon/doc/ne/readme.md +++ b/addon/doc/ne/readme.md @@ -100,7 +100,7 @@ bookmarks. * योजना वाचनलाई थप गरियो । ## २.0 मा गरिएका परिवर्तनहरू ## -* हरेक फाइलमा भएका विभिन्न खोजीलाइ मेटाउन र बचत गर्ने विकल्पहरू थप गरियो । +* हरेक फाइलमा भएका विभिन्न खोजीलाइ मेटाउन र बचत गर्ने विकल्पहरू थप गरियो । * गैर ल्याटीन वर्णहरू भएका मार्ग हुदाँ काटिने समस्या हल गरियो । * अब नेत्रवाणीको लगानीसङ्केत पातो प्रयोग गरेर द्रुतमार्ग कायम गर्न सक्ने बनाइयो । diff --git a/addon/doc/pl/readme.md b/addon/doc/pl/readme.md index 430de800..4dbbfff1 100644 --- a/addon/doc/pl/readme.md +++ b/addon/doc/pl/readme.md @@ -227,4 +227,3 @@ placemarkers. hiszpański, słowacki, słoweński, tamilski. [[!tag dev stable]] - diff --git a/addon/doc/pt_BR/readme.md b/addon/doc/pt_BR/readme.md index ced5a769..2514689e 100644 --- a/addon/doc/pt_BR/readme.md +++ b/addon/doc/pt_BR/readme.md @@ -238,4 +238,3 @@ marcadores de posição. Brasileiro, Tâmil. [[!tag dev stable]] - diff --git a/addon/doc/pt_PT/readme.md b/addon/doc/pt_PT/readme.md index 282a8a73..0a7131a4 100644 --- a/addon/doc/pt_PT/readme.md +++ b/addon/doc/pt_PT/readme.md @@ -231,4 +231,3 @@ placemarkers. eslovaco, esloveno, tamil. [[!tag dev stable]] - diff --git a/addon/doc/ro/readme.md b/addon/doc/ro/readme.md index a6ed4acc..c7d74fa4 100644 --- a/addon/doc/ro/readme.md +++ b/addon/doc/ro/readme.md @@ -224,4 +224,3 @@ placemarkers. Spaniolă, Slovacă, Slovenă, Tamilă. [[!tag dev stable]] - diff --git a/addon/doc/ru/readme.md b/addon/doc/ru/readme.md index ba39d9e2..43692fa9 100644 --- a/addon/doc/ru/readme.md +++ b/addon/doc/ru/readme.md @@ -227,4 +227,3 @@ placemarkers. португальский, испанский, словацкий, словенский, тамильский. [[!tag dev stable]] - diff --git a/addon/doc/sk/readme.md b/addon/doc/sk/readme.md index eb250ea9..44c5491a 100644 --- a/addon/doc/sk/readme.md +++ b/addon/doc/sk/readme.md @@ -209,4 +209,3 @@ placemarkers. Perzština, Portugalčina, španielčina, slovenčina, slovinčina, Tamilčina. [[!tag dev stable]] - diff --git a/addon/doc/tr/readme.md b/addon/doc/tr/readme.md index 1edb2d1a..0fc5175f 100644 --- a/addon/doc/tr/readme.md +++ b/addon/doc/tr/readme.md @@ -219,4 +219,3 @@ sayfalarda, yer imlerini değil, belirli aramayı kullanmak daha iyidir. Portekizcesi, Farsça, Fince. [[!tag dev stable]] - diff --git a/addon/doc/uk/readme.md b/addon/doc/uk/readme.md index 6605af7b..71777be1 100644 --- a/addon/doc/uk/readme.md +++ b/addon/doc/uk/readme.md @@ -223,4 +223,3 @@ placemarkers. португальську, іспанську, словацьку, словенську, тамільську. [[!tag dev stable]] - diff --git a/addon/doc/vi/readme.md b/addon/doc/vi/readme.md index 1a84331f..875fbf7c 100644 --- a/addon/doc/vi/readme.md +++ b/addon/doc/vi/readme.md @@ -219,4 +219,3 @@ placemarkers. Slovenia, Nam Ấn Độ. [[!tag dev stable]] - diff --git a/addon/doc/zh_CN/readme.md b/addon/doc/zh_CN/readme.md index fb2c3ec6..c94ee0ec 100644 --- a/addon/doc/zh_CN/readme.md +++ b/addon/doc/zh_CN/readme.md @@ -153,4 +153,3 @@ * 翻译为:巴西葡萄牙语,波斯语,芬兰语,法语,加利西亚语,德语,意大利语,日语,韩语,尼泊尔语,葡萄牙语,西班牙语,斯洛伐克语,斯洛维尼亚语,泰米尔语。 [[!tag dev stable]] -