Skip to content
Draft
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
28c060d
Add scripts to allow addons from personal repos to be synchronized wi…
nvdaes Nov 24, 2025
4669430
use a json file to store addonId, and use it to filter files to get C…
nvdaes Nov 24, 2025
b188560
Try to get files just for the current add-on
nvdaes Nov 24, 2025
7092615
Add workflow to export an add-on to Crowdin (authors would need to be…
nvdaes Nov 24, 2025
e89640d
Use buildVars, not metadata.json file
nvdaes Nov 25, 2025
4c7771b
Add userAccount to buildVars, and step to get addon-id to GitHub work…
nvdaes Nov 26, 2025
c529cee
Update files after testing exporting an add-on to Crowdin, needs refi…
nvdaes Nov 26, 2025
186b755
Add python version file
nvdaes Nov 26, 2025
f1fbf8e
Improve pyproject and update precommit config after testing that chec…
nvdaes Nov 26, 2025
b867a9a
Restore rules
nvdaes Nov 27, 2025
47ed91c
Restore pyproject
nvdaes Nov 27, 2025
402002e
Improve uv project
nvdaes Nov 27, 2025
d820711
Remove files
nvdaes Nov 27, 2025
9f6b3dc
Calculate hash of i18nSources
nvdaes Nov 29, 2025
4c938ec
Update workflow
nvdaes Nov 30, 2025
a303210
Update _l10n
nvdaes Nov 30, 2025
a0d02da
Upload md file
nvdaes Dec 1, 2025
a8d4252
Updates
nvdaes Dec 3, 2025
1a1e6fd
Update l10nUtil
nvdaes Dec 14, 2025
d2395b0
Update workflow
nvdaes Dec 14, 2025
e4dafe1
Update readme
nvdaes Dec 16, 2025
f76904e
Update readme.md
nvdaes Dec 16, 2025
aea5eba
Update _l10n/crowdinSync.py
nvdaes Dec 16, 2025
f7ccaf6
Add setOutput.py to separate Python code from yaml file
nvdaes Dec 16, 2025
0276e22
Remove bad comment
nvdaes Dec 16, 2025
253eb46
Reset pyproject to master
nvdaes Dec 16, 2025
c51e7ad
reset .pre-commit configuration to master
nvdaes Dec 16, 2025
cd4816c
Remove userAccount variable, since we use markdown, not xliff
nvdaes Dec 17, 2025
314220b
Update or add files from scratch depending on existence of hashFile
nvdaes Dec 17, 2025
f3e8b8d
Use addMd and addPotFromScratch outputs
nvdaes Dec 17, 2025
de4fa15
Update dependencies
nvdaes Dec 20, 2025
46a105a
Update setOutput
nvdaes Dec 20, 2025
053d4de
Update workflow
nvdaes Dec 20, 2025
4a3f5a0
Update lock
nvdaes Dec 20, 2025
3c1a73e
Merge branch 'master' into l10n
nvdaes Dec 21, 2025
dbe74dc
Verify uv lock
nvdaes Dec 21, 2025
e717292
Add uv to dependencies in case this is relevant to verify the lock ac…
nvdaes Dec 21, 2025
c4ed575
Remove debug statement
nvdaes Dec 21, 2025
befa647
Run pre-commit
nvdaes Dec 21, 2025
05c8161
Update dependencies
nvdaes Dec 22, 2025
9a0f62a
Deleted Pyproject to avoid conflicts
nvdaes Dec 22, 2025
4abd788
Reset pyproject to master
nvdaes Dec 22, 2025
c256364
Remove _l10n since this will be added as a submodule
nvdaes Dec 22, 2025
0505a3b
Don't run pre-commit since it requires a different token to access hooks
nvdaes Dec 22, 2025
fd2554b
Merge translations into branch
nvdaes Dec 22, 2025
b30f46f
Add project id without using vars
nvdaes Dec 22, 2025
70293c8
Schedule workflow
nvdaes Dec 22, 2025
da09c8c
Rename workflow
nvdaes Dec 22, 2025
5c52f33
Create PR
nvdaes Dec 22, 2025
d0d5e03
Don't create a PR since this n¡may need a personal access token
nvdaes Dec 22, 2025
b40f94a
Update removing permissions for PR
nvdaes Dec 22, 2025
cb7807e
Update Python version compatible with ubuntu-latest
nvdaes Dec 28, 2025
1449a01
Add dry-run
nvdaes Dec 28, 2025
697d048
Optimize workflow to test with act and docker locally
nvdaes Dec 28, 2025
8c9247b
Update uv.lock
nvdaes Dec 30, 2025
a8469fb
Merge branch 'master' into l10n
nvdaes Dec 30, 2025
6089057
Add workflow call to build the add-on, so it can be reused in other w…
nvdaes Mar 25, 2026
d8154a9
Update workflow for testing
nvdaes Mar 25, 2026
e699abc
Improve workflow to download files from Crowdin
nvdaes Apr 18, 2026
5eafa64
Update setOutputs to set just add-on id to download translations
nvdaes Apr 18, 2026
0eb3223
[crowdinL10n.yml] improve CI translation workflow
abdel792 Apr 20, 2026
a195110
Remove duplicate setOutputs.py from .github/workflows
abdel792 Apr 20, 2026
9ed44a3
Merge pull request #2 from abdel792/l10nImprovements
nvdaes Apr 20, 2026
e62bd5b
Update lock
nvdaes Apr 20, 2026
37c4d4f
Merge
nvdaes Apr 20, 2026
91995bd
Try to fix pre-commit configuration
nvdaes Apr 20, 2026
0c613be
Reset gitignore to master
nvdaes Apr 20, 2026
0b65072
Require polib 1.2.0
nvdaes Apr 20, 2026
f9ba8fe
Update lock file
nvdaes Apr 20, 2026
8e514ba
Remove sha256 file
nvdaes Apr 20, 2026
9c64247
Remove verification of lock file in pre-commit config, not present in…
nvdaes Apr 20, 2026
7589fef
Remove Crowdin client
nvdaes Apr 20, 2026
94b1a88
Update lock file
nvdaes Apr 20, 2026
65290e4
Enhance Crowdin l10n workflow with MD quality evaluation and comparis…
abdel792 Apr 22, 2026
059bb53
Merge pull request #3 from abdel792/l10nImprovements
nvdaes Apr 22, 2026
c9c3bab
Run pre-commit
nvdaes Apr 22, 2026
25dcb5d
Fix conflicts
nvdaes Apr 23, 2026
b1d2acf
Restore deleted space
nvdaes Apr 24, 2026
263b7e2
Update lock file
nvdaes Apr 24, 2026
f6e18c2
Remove score staff
nvdaes Apr 24, 2026
5573061
Add ps1 script and rename setOutputs.py
nvdaes Apr 24, 2026
87cbed4
Run ps1 script from workflow
nvdaes Apr 24, 2026
1a18717
Change python version to 3.13
nvdaes Apr 24, 2026
471a320
Update lock file
nvdaes Apr 24, 2026
e50a716
Fix checkTranslation script according to pre-commit
nvdaes Apr 24, 2026
f5150cb
Remove getAddonInfo step
nvdaes Apr 24, 2026
3c7093c
Update scripts and workflow
nvdaes Apr 25, 2026
065a16f
Fix pyright
nvdaes Apr 25, 2026
c4dd521
Add markdownTranslate
nvdaes Apr 25, 2026
00cf8cf
Update markdownTranslate
nvdaes Apr 25, 2026
480c70e
Exclude markdownTranslate from pyright, it contains many errors
nvdaes Apr 26, 2026
ce2d2da
Update source files
nvdaes Apr 26, 2026
3981ece
Updates
nvdaes Apr 26, 2026
0def05d
Fix files with pre-commit
nvdaes Apr 26, 2026
1c92aab
refactor: use Crowdin API for translation checks and optimize file se…
abdel792 Apr 26, 2026
9b5bf84
Merge pull request #4 from abdel792/l10nImprovements
nvdaes Apr 26, 2026
c5e4076
Add crowdin api client
nvdaes Apr 26, 2026
c098392
Add dependencies
nvdaes Apr 26, 2026
39416a0
Remove limit for list of files, fetch all
nvdaes Apr 26, 2026
2396864
Improvements
nvdaes Apr 26, 2026
94929e7
Updates
nvdaes Apr 26, 2026
57d46f5
Add language mappings
nvdaes Apr 27, 2026
74388d0
Fix
nvdaes Apr 27, 2026
1f8eb84
refactor: modernize translation workflow and integrate language mappings
abdel792 Apr 28, 2026
ef0a614
docs: update readme with translation workflow instructions
abdel792 Apr 28, 2026
92e1cd6
docs: fix translation mailing list address in readme.md
abdel792 Apr 28, 2026
f92a73f
Merge pull request #5 from abdel792/l10nImprovements
nvdaes Apr 28, 2026
6a884c0
Fix readme
nvdaes Apr 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions .github/workflows/exportAddonToCrowdin.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Export add-on to Crowdin

on:

workflow_dispatch:
inputs:
update:
description: 'true to update preexisting sources, false to add them from scratch'
type: boolean
required: false
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout add-on
uses: actions/checkout@v6
- name: "Set up Python"
uses: actions/setup-python@v6
with:
python-version-file: ".python-version"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install scons markdown
sudo apt update
sudo apt install gettext
- name: Build add-on and pot file
run: |
scons
scons pot
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v7
- name: Get add-on id
id: getAddonId
shell: python
run: |
import os, sys
sys.path.insert(0, os.getcwd())
import buildVars
addonId = buildVars.addon_info["addon_name"]
name = 'addonId'
value = addonId
with open(os.environ['GITHUB_OUTPUT'], 'a') as f:
f.write(f"{name}={value}")
- name: Generate xliff
if: ${{ !inputs.update }}
run: |
uv run ./_l10n/markdownTranslate.py generateXliff -m readme.md -o ${{ steps.getAddonId.outputs.addonId }}.xliff
- name: update xliff
if: ${{ inputs.update }}
run: |
uv run ./_l10n/markdownTranslate.py updateXliff -x ${{ steps.getAddonId.outputs.addonId }}.xliff -m readme.md -o ${{ steps.getAddonId.outputs.addonId }}.xliff.temp
mv ${{ steps.getAddonId.outputs.addonId }}.xliff.temp ${{ steps.getAddonId.outputs.addonId }}.xliff
fi
- name: Upload to Crowdin
if: ${{ !inputs.update }}
run: |
uv run ./_l10n/l10nUtil.py uploadSourceFile --localFilePath=${{ steps.getAddonId.outputs.addonId }}.xliff
uv run ./_l10n/l10nUtil.py uploadSourceFile --localFilePath=${{ steps.getAddonId.outputs.addonId }}.pot
env:
crowdinProjectID: ${{ vars.CROWDIN_ID }}
crowdinAuthToken: ${{ secrets.CROWDIN_TOKEN }}
- name: Update sources
if: ${{ inputs.update }}
run: |
uv run ./_l10n/crowdinSync.py uploadSourceFile ${{ steps.getAddonId.outputs.addonId }}.pot
uv run ./_l10n/crowdinSync.py uploadSourceFile ${{ steps.getAddonId.outputs.addonId }}.xliff
env:
crowdinProjectID: ${{ vars.CROWDIN_ID }}
crowdinAuthToken: ${{ secrets.CROWDIN_TOKEN }}
- name: Commit and push json file
id: commit
run: |
git config --local user.name github-actions
git config --local user.email github-actions@github.com
git status
git add _l10n/l10n.json
if git diff --staged --quiet; then
echo "Nothing added to commit."
echo "has_changes=false" >> $GITHUB_OUTPUT
else
echo "has_changes=true" >> $GITHUB_OUTPUT
git commit -m "Update Crowdin file ids"
git push
fi
97 changes: 91 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,92 @@
# Copied from https://github.com/nvaccess/nvda
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.

Could we do this PR first and separately?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done. See PR #4

# https://pre-commit.ci/
# Configuration for Continuous Integration service
ci:
# 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.13

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: check-ast
- id: check-case-conflict
- id: check-yaml
- repo: https://github.com/pre-commit-ci/pre-commit-ci-config
rev: v1.6.1
hooks:
- id: check-pre-commit-ci-config

- repo: meta
hooks:
# ensures that exclude directives apply to any file in the repository.
- id: check-useless-excludes
# ensures that the configured hooks apply to at least one file in the repository.
- id: check-hooks-apply

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
# Prevents commits to certain branches
- id: no-commit-to-branch
args: ["--branch", "main", ]
# Checks that large files have not been added. Default cut-off for "large" files is 500kb.
- id: check-added-large-files
# Checks python syntax
- id: check-ast
# Checks for filenames that will conflict on case insensitive filesystems (the majority of Windows filesystems, most of the time)
- id: check-case-conflict
# Checks for artifacts from resolving merge conflicts.
- id: check-merge-conflict
# Checks Python files for debug statements, such as python's breakpoint function, or those inserted by some IDEs.
- id: debug-statements
# Removes trailing whitespace.
- id: trailing-whitespace
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, 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, c, c++, batch, markdown, toml, yaml, powershell]
# Validates TOML files.
- id: check-toml
# Validates YAML files.
- id: check-yaml
# 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.2.0
hooks:
# Ruff preserves indent/new-line formatting of function arguments, list items, and similar iterables,
# if a trailing comma is added.
# This adds a trailing comma to args/iterable items in case it was missed.
- id: add-trailing-comma

- repo: https://github.com/astral-sh/ruff-pre-commit
# Matches Ruff version in pyproject.
rev: v0.12.7
hooks:
- id: ruff
name: lint with ruff
args: [ --fix ]
- id: ruff-format
name: format with ruff

- repo: https://github.com/RobertCraigie/pyright-python
rev: v1.1.406
hooks:
- id: pyright
name: Check types with pyright
additional_dependencies: [ "pyright[nodejs]==1.1.406" ]

- repo: https://github.com/DavidAnson/markdownlint-cli2
rev: v0.18.1
hooks:
- id: markdownlint-cli2
name: Lint markdown files
args: ["--fix"]
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13
92 changes: 92 additions & 0 deletions _l10n/crowdinSync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# A part of NonVisual Desktop Access (NVDA)
# based on file from https://github.com/jcsteh/osara
# Copyright (C) 2023-2024 NV Access Limited, James Teh
Comment thread
nvdaes marked this conversation as resolved.
Outdated
# This file may be used under the terms of the GNU General Public License, version 2 or later.
# For more details see: https://www.gnu.org/licenses/gpl-2.0.html


import argparse
import os

import requests

from l10nUtil import getFiles

AUTH_TOKEN = os.getenv("crowdinAuthToken", "").strip()
if not AUTH_TOKEN:
raise ValueError("crowdinAuthToken environment variable not set")
PROJECT_ID = os.getenv("crowdinProjectID", "").strip()
if not PROJECT_ID:
raise ValueError("crowdinProjectID environment variable not set")


def request(
path: str,
method=requests.get,
headers: dict[str, str] | None = None,
**kwargs,
) -> requests.Response:
if headers is None:
headers = {}
headers["Authorization"] = f"Bearer {AUTH_TOKEN}"
r = method(
f"https://api.crowdin.com/api/v2/{path}",
headers=headers,
**kwargs,
)
# Convert errors to exceptions, but print the response before raising.
try:
r.raise_for_status()
except requests.exceptions.HTTPError:
print(r.json())
raise
return r


def projectRequest(path: str, **kwargs) -> requests.Response:
return request(f"projects/{PROJECT_ID}/{path}", **kwargs)


def uploadSourceFile(localFilePath: str) -> None:
files = getFiles()
fn = os.path.basename(localFilePath)
crowdinFileID = files.get(fn)
print(f"Uploading {localFilePath} to Crowdin temporary storage as {fn}")
with open(localFilePath, "rb") as f:
r = request(
"storages",
method=requests.post,
headers={"Crowdin-API-FileName": fn},
data=f,
)
storageID = r.json()["data"]["id"]
print(f"Updating file {crowdinFileID} on Crowdin with storage ID {storageID}")
r = projectRequest(
f"files/{crowdinFileID}",
method=requests.put,
json={"storageId": storageID},
)
revisionId = r.json()["data"]["revisionId"]
print(f"Updated to revision {revisionId}")


def main():
parser = argparse.ArgumentParser(
description="Syncs translations with Crowdin.",
)
commands = parser.add_subparsers(dest="command", required=True)
uploadCommand = commands.add_parser(
"uploadSourceFile",
help="Upload a source file to Crowdin.",
)
# uploadCommand.add_argument("crowdinFileID", type=int, help="The Crowdin file ID.")
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.

should this be removed?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done

uploadCommand.add_argument("localFilePath", help="The path to the local file.")
args = parser.parse_args()
if args.command == "uploadSourceFile":
uploadSourceFile(args.localFilePath)
else:
raise ValueError(f"Unknown command: {args.command}")


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions _l10n/files.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions _l10n/l10n.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"translateNvdaAddonsWithCrowdin.xliff": 442, "translateNvdaAddonsWithCrowdin.pot": 444}
Loading