Skip to content

Add scripts to allow addons from personal repos to be synchronized with Crowdin#1

Open
nvdaes wants to merge 77 commits intonvaccess:masterfrom
nvdaes:l10n
Open

Add scripts to allow addons from personal repos to be synchronized with Crowdin#1
nvdaes wants to merge 77 commits intonvaccess:masterfrom
nvdaes:l10n

Conversation

@nvdaes
Copy link
Copy Markdown

@nvdaes nvdaes commented Nov 24, 2025

Blocked by #7

Comment thread pyproject.toml
strictSetInference = true

# Compliant rules
reportAbstractUsage = true
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.

it's probably better to keep these rules than dropping to NVDA's standard

@nvdaes
Copy link
Copy Markdown
Author

nvdaes commented Nov 28, 2025

Purpose

Add-on authors may wish to help translators use Crowdin, the same framework where they translate NVDA. to translate messages and documentation for maintained add-ons:

Other details

  • Pot file are created/updated, and uploaded to a Crowdin project.
  • The readme.md file is converted to xliff and uploaded to a Crowdin project.
  • Po and xfiles are translated.
  • Translated files are downloaded and processed to be copied to locale/langCode/LC_MESSAGES/nvda.po, and doc/langCode/readme.md, in the addon folder.
    Authors need to store a Crowdin token with permissions to upload files to the Crowdin project as a repository secret.

Development approach

Use the Crowdin registration repo to add scripts usable by individual add-ons in personal repos.

@nvdaes
Copy link
Copy Markdown
Author

nvdaes commented Nov 28, 2025

I've tested that all check pass using this pyproject.toml file on this PR:

nvdaes/translateNvdaAddonsWithCrowdin#11

I use precommit, CodeQL and a workflow to check that all translatable messages have comments for translators.

I'll try to use the cache action to cache some add-on metadata like its id, and also hashfiles from l10nSources (taking the value of buildVars.py), and the hasf¡hfile of the readme.md, to determine if pot and xliff files should be updated.

@nvdaes
Copy link
Copy Markdown
Author

nvdaes commented Nov 30, 2025

Export translations to Crowdin running the workflow with update=False works properly:

https://github.com/nvdaes/translateNvdaAddonsWithCrowdin/actions/runs/19802210157

@nvdaes
Copy link
Copy Markdown
Author

nvdaes commented Nov 30, 2025

This time, updatexLiff is failing. Seems that adding blank lines to readme may cause problems:

https://github.com/nvdaes/translateNvdaAddonsWithCrowdin/actions/runs/19802391926/job/56731562709

@nvdaes
Copy link
Copy Markdown
Author

nvdaes commented Nov 30, 2025

If someone can help with this issue when update xliff, I'll be grateful.
I think that this is one of the bugest problems with xliff files. Sometimes sel lines are None and they don't have a strip method. I don't know if this should be also improved in NVDA
cc: @seanbudd

@seanbudd
Copy link
Copy Markdown
Member

seanbudd commented Dec 1, 2025

It might be easier to avoid xliff and just translate the markdown files directly. This won't support diffs very well but worth experimenting with

@nvdaes
Copy link
Copy Markdown
Author

nvdaes commented Dec 1, 2025

@seanbudd wrote:

It might be easier to avoid xliff and just translate the markdown files directly. This won't support diffs very well but worth experimenting with

OK.

@nvdaes
Copy link
Copy Markdown
Author

nvdaes commented Dec 3, 2025

@CyrilleB79, you were interested in this framework. If you want, feel free to see how the translateNvdaAddonsWithCrowdin.md can be translated in the project. Using xliff files is causing problems, as mentioned, and we are experimenting uploading md files instead.

@nvdaes
Copy link
Copy Markdown
Author

nvdaes commented Apr 8, 2026

@seanbudd wrote:

I think it's really important that the add-on dev community helps support this project.

I agree. Otherwise, we hav a lot of things to do in different projects. I'll request for volunteers for this. Good news is that I can print the list of members in the project via l10nUtil.exe.
If add-on devs don't support this, you may be still interested in adding this ability to nvdaL10n for managers.

nvdaes and others added 7 commits April 18, 2026 07:04
- move Python helper scripts from .github/workflows to .github/scripts for better separation of concerns
- add polib dependency and switch to uv sync for reproducible CI environment
- fix missing GH_TOKEN required for GitHub CLI (gh) commands
- fix l10nUtil.exe path resolution (use ./l10nUtil.exe instead of _l10n/l10nUtil.exe)
- improve Crowdin download behavior by avoiding processing empty translation files
- refine PO handling: preserve local translations, conditionally upload to Crowdin when needed
- refine XLIFF handling: update local documentation only, no upload back to Crowdin
- ensure safer, more deterministic, and more predictable translation synchronization logic
The script is already available in .github/scripts/.
Improve crowdinL10n.yml translation workflow
@nvdaes
Copy link
Copy Markdown
Author

nvdaes commented Apr 20, 2026

pre-commit.ci run

@nvdaes
Copy link
Copy Markdown
Author

nvdaes commented Apr 20, 2026

Thanks @abdel792 for contributing this PR via nvdaes#2

I've run pre-commit, and I think that this needs to be cleaned. For example, since we are using l10nUtil.exe, we may remove some dependencies.
I'll work on this. Let me know if something is missing from my side.

@nvdaes nvdaes marked this pull request as ready for review April 22, 2026 17:55
@nvdaes
Copy link
Copy Markdown
Author

nvdaes commented Apr 22, 2026

@seanbudd , I think this is ready for review. The workflow implemented by @abdel792 works well, though seems that xliff files may not work well for old add-ons, probably due to extra spaces in markdown files. We used a readme.md, and then we had to copy it to the svn repo with some modifications. I guess that something may be broken in the conversion or in the format, not sure.
@abdel792 , can you share a workflow to demonstrate how xliff files are transformed to md in the l10n branch of personal repos please?
I can show this successful workflow:

https://github.com/nvdaes/readFeeds/actions/runs/24792870808/job/72554821619

Also, I think that we will need to create a different PR to document how to upload pot and xliff files, including updating them if the markdown file is updated.

abdel792 and others added 3 commits April 23, 2026 00:27
…on logic

- Add Markdown language scoring (langid) in checkTranslation.py
- Extend script to support MD files and optional multi-file comparison
- Update workflow to handle XLIFF → MD conversion only when translated
- Implement multi-source comparison (XLIFF MD, remote MD, local MD)
- Apply best-quality selection before updating or uploading files
- Add full logging for all decision branches
- Improve fallback behavior when only one source is available
Improve Crowdin localization workflow with intelligent MD/XLIFF handling
@nvdaes nvdaes marked this pull request as draft April 22, 2026 23:59
@nvdaes
Copy link
Copy Markdown
Author

nvdaes commented Apr 23, 2026

@seanbudd, @abdel792 has made more and significant improvements. This can be shown here

https://github.com/nvdaes/readFeeds/actions/runs/24808570637/job/72608265836

I'll resolve conflicts and mark this as ready for review again.

@nvdaes nvdaes marked this pull request as ready for review April 23, 2026 00:09
@seanbudd seanbudd requested a review from Copilot April 23, 2026 06:29
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 6 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

addonId = buildVars.addon_info["addon_name"]
name = "addonId"
value = addonId
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

GITHUB_OUTPUT is accessed unconditionally; if this script is run outside GitHub Actions (or the env var is missing), it will raise a KeyError with a confusing traceback. Consider validating os.environ.get("GITHUB_OUTPUT") and exiting with a clear error message when it’s not set.

Suggested change
with open(os.environ["GITHUB_OUTPUT"], "a") as f:
githubOutput = os.environ.get("GITHUB_OUTPUT")
if not githubOutput:
print(
"Error: GITHUB_OUTPUT is not set. This script must be run in a GitHub Actions step "
"with GITHUB_OUTPUT available.",
file=sys.stderr,
)
raise SystemExit(1)
with open(githubOutput, "a") as f:

Copilot uses AI. Check for mistakes.
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.

This will be run inside GitHub Actions.

Comment thread readme.md
Comment on lines +185 to +186
Then, to export your add-on to Crowdin for the first time, run the `.github/workflows/exportAddonsToCrowdin.yml`, ensuring that the update option is set to false.
When you have updated messages or documentation, run the workflow setting update to true (which is the default option).
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The workflow referenced here (.github/workflows/exportAddonsToCrowdin.yml) doesn’t exist in this PR; the added workflow is crowdinL10n.yml. Please update the documentation to reference the correct workflow name (and ensure the described inputs/options like update=false match what the workflow actually supports).

Copilot uses AI. Check for mistakes.
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.

I'll update the readme

uses: astral-sh/setup-uv@v7

- name: Install dependencies
run: uv sync
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

uv sync will re-resolve dependencies if no lockfile is present, which can make CI non-reproducible (and slower). If the intent is to use locked deps, re-add uv.lock to the repo and run uv sync --frozen/--locked (or equivalent) so this workflow fails if the lock is out of date.

Suggested change
run: uv sync
run: uv sync --locked

Copilot uses AI. Check for mistakes.
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.

lock file should be present

if ($xliffValid) {
Write-Host "Converting XLIFF → MD"
$tempMd = "$env:TEMP\readme_$langCode.md"
./l10nUtil.exe xliff2md $xliffFile $tempMd
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

After running l10nUtil.exe xliff2md, the script assumes $tempMd was created and later Move-Items it. If xliff2md fails (non-zero exit code or no output file), this can cause misleading behavior or runtime errors. Consider checking $LASTEXITCODE and Test-Path $tempMd before proceeding, and fail the step when conversion fails.

Suggested change
./l10nUtil.exe xliff2md $xliffFile $tempMd
./l10nUtil.exe xliff2md $xliffFile $tempMd
if ($LASTEXITCODE -ne 0 -or -not (Test-Path $tempMd)) {
throw "xliff2md failed for '$xliffFile' or did not create expected output '$tempMd'."
}

Copilot uses AI. Check for mistakes.
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.

I think this is not needed for this workflow.

Comment on lines +44 to +52
- name: Download l10nUtil
run: |
gh release download --repo nvaccess/nvdaL10n --pattern "l10nUtil.exe"

- name: Download translations from Crowdin
shell: pwsh
run: |
./l10nUtil.exe exportTranslations -o _addonL10n -c addon

Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

This step runs several external commands (gh release download, l10nUtil.exe exportTranslations, l10nUtil.exe uploadTranslationFile, etc.) but doesn’t enforce failure on non-zero exit codes. In PowerShell, most external-command failures won’t stop the script unless you check $LASTEXITCODE (or explicitly throw/exit). Recommend adding strict error handling so the workflow fails fast when downloads/exports/uploads fail.

Copilot uses AI. Check for mistakes.
Comment on lines +265 to +271

git switch ${{ env.downloadTranslationsBranch }} 2>$null
if ($LASTEXITCODE -ne 0) {
git switch -c ${{ env.downloadTranslationsBranch }}
}

git push -f --set-upstream origin ${{ env.downloadTranslationsBranch }}
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The workflow commits on the current branch, then switches/creates the l10n branch afterwards. If l10n already exists locally, git switch l10n will move HEAD away from the new commit and the subsequent push won’t include the translation update. Switch/create/reset the target branch before committing (or explicitly push the commit to l10n, e.g. git push origin HEAD:l10n).

Suggested change
git switch ${{ env.downloadTranslationsBranch }} 2>$null
if ($LASTEXITCODE -ne 0) {
git switch -c ${{ env.downloadTranslationsBranch }}
}
git push -f --set-upstream origin ${{ env.downloadTranslationsBranch }}
git push -f --set-upstream origin HEAD:${{ env.downloadTranslationsBranch }}

Copilot uses AI. Check for mistakes.
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.

l10n shouldn't exis locally since the main branch will be checked out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants