Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 1 addition & 4 deletions .github/scripts/checkTranslation.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def getScoreFromApi(fileNameToSearch: str, langId: str) -> float:
# Also handles underscore to dash conversion for Crowdin compatibility
if langApi.lower().startswith(langId.lower().replace("_", "-")):
progress = float(item["data"]["translationProgress"])
return progress / 100
return progress

# Check pagination total.
total = resp["pagination"]["totalCount"]
Expand Down Expand Up @@ -139,9 +139,6 @@ def main():
# Default to poScore for .po and other localization files.
print(f"poScore={score}")

# Exit with success (0) if there is at least 50% translated content.
sys.exit(0 if score > 0.5 else 1)


if __name__ == "__main__":
main()
128 changes: 80 additions & 48 deletions .github/scripts/crowdinSync.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@ if (Test-Path $mdFile) {
try {
Copy-Item "$addonId.xliff" $tempXliff -Force
Write-Host "DEBUG: Updating XLIFF source based on readme.md..."
uv run .github/scripts/markdownTranslate.py updateXliff -m $mdFile -x $tempXliff -o $xliffFile
} finally {
./l10nUtil.exe md2xliff $mdFile $xliffFile -o $tempXliff
}
finally {
if (Test-Path $tempXliff) {
Remove-Item $tempXliff -Force
}
}
} else {
}
else {
Write-Host "DEBUG: XLIFF template not found. Creating new one from readme.md..."
uv run .github/scripts/markdownTranslate.py generateXliff -m $mdFile -o $xliffFile
./l10nUtil.exe md2xliff $mdFile $xliffFile
}
}

Expand All @@ -49,8 +51,10 @@ if (Test-Path $potFile) {
if (Test-Path $xliffFile) {
Write-Host "DEBUG: Uploading updated XLIFF source to Crowdin..."
./l10nUtil.exe uploadSourceFile "$xliffFile" -c $env:L10N_UTIL_CONFIG

git add "$xliffFile"
git diff --staged --quiet

if ($LASTEXITCODE -ne 0) {
git commit -m "Update $xliffFile for $addonId"
}
Expand All @@ -69,127 +73,155 @@ New-Item -ItemType Directory -Force -Path addon/doc | Out-Null
$languageMappings = Get-Content -Raw ".github/scripts/languageMappings.json" | ConvertFrom-Json

foreach ($dir in Get-ChildItem -Path "_addonL10n/$addonId" -Directory) {

$langCode = $dir.Name

if ($langCode -eq "en") { continue }
if ($langCode -eq "en") {
continue
}

# --- Identify codes

$crowdinLang = $null

# Use the ."variable" syntax to correctly read the PSCustomObject from JSON
if ($languageMappings.PSObject.Properties.Name -contains $langCode) {
$crowdinLang = $languageMappings."$langCode"
}

# Fallback: If no mapping is found, replace underscores with dashes for Crowdin compatibility
if (-not $crowdinLang) {
$crowdinLang = $langCode.Replace('_', '-')
}

# The $langCode (folder name from Crowdin) represents the local repository language code.
# It matches the NVDA directory structure, so no extra mapping is needed.
Write-Host "--- Processing Language: $langCode (Crowdin: $crowdinLang) ---" -ForegroundColor Cyan

# Paths
$remoteMd = Join-Path $dir.FullName "$addonId.md"

$remoteXliff = Join-Path $dir.FullName "$addonId.xliff"
$remotePo = Join-Path $dir.FullName "$addonId.po"

$localMdDir = "addon/doc/$langCode"
$localMd = "$localMdDir/readme.md"

$localPoPath = "addon/locale/$langCode/LC_MESSAGES/nvda.po"

# --- 3.1 PO FILE PROCESSING ---
$poImported = $false
$scorePo = 0.0
$threshold = $env:MIN_PERCENTAGE_TRANSLATED

if (Test-Path $remotePo) {
Write-Host "DEBUG: Checking Remote PO progress for $crowdinLang..."
uv run python .github/scripts/checkTranslation.py "$addonId.po" $crowdinLang
if ($LASTEXITCODE -eq 0) {
Write-Host "SUCCESS: Remote PO is valid. Importing to $localPoPath"

Write-Host "DEBUG: Evaluating Remote PO score..."

$res = uv run python .github/scripts/checkTranslation.py "$addonId.po" $crowdinLang

$scorePo = [double](
($res | Select-String "poScore=").ToString().Split("=")[1]
)

Write-Host "DEBUG: PO Score -> $scorePo"

if ($scorePo -ge $threshold) {

Write-Host "SUCCESS: Remote PO is above threshold. Importing to $localPoPath"

New-Item -ItemType Directory -Force -Path (Split-Path $localPoPath) | Out-Null

Move-Item $remotePo $localPoPath -Force

$poImported = $true
} else {
Write-Host "WARNING: Remote PO progress is below threshold."
}
else {

Write-Host "WARNING: Remote PO score is below threshold ($threshold)."
}
}

if (-not $poImported -and (Test-Path $localPoPath)) {

Write-Host "ACTION: Uploading local legacy PO to Crowdin ($crowdinLang) as fallback."

./l10nUtil.exe uploadTranslationFile $crowdinLang "$addonId.po" $localPoPath -c $env:L10N_UTIL_CONFIG
}

# --- 3.2 DOCUMENTATION PROCESSING (MD & XLIFF) ---
$scoreMd = 0.0
$scoreXliff = 0.0
# --- 3.2 DOCUMENTATION PROCESSING (XLIFF ONLY) ---

if (Test-Path $remoteMd) {
Write-Host "DEBUG: Evaluating Remote Markdown score..."
$res = uv run python .github/scripts/checkTranslation.py "$addonId.md" $crowdinLang
$scoreMd = [double]($res | Select-String "mdScore=").ToString().Split("=")[1]
} else {
Write-Host "DEBUG: No remote Markdown file found for this language."
}
$scoreXliff = 0.0

if (Test-Path $remoteXliff) {

Write-Host "DEBUG: Evaluating Remote XLIFF score..."

$res = uv run python .github/scripts/checkTranslation.py "$addonId.xliff" $crowdinLang
$scoreXliff = [double]($res | Select-String "xliffScore=").ToString().Split("=")[1]
} else {

$scoreXliff = [double](
($res | Select-String "xliffScore=").ToString().Split("=")[1]
)
}
else {
Write-Host "DEBUG: No remote XLIFF file found for this language."
}

Write-Host "DEBUG: Comparison Scores -> MD: $scoreMd | XLIFF: $scoreXliff"
Write-Host "DEBUG: XLIFF Score -> $scoreXliff"

$threshold = 0.5
$threshold = $env:MIN_PERCENTAGE_TRANSLATED
$docImported = $false

if ($scoreXliff -gt $threshold -or $scoreMd -gt $threshold) {
if (!(Test-Path $localMdDir)) { New-Item -ItemType Directory -Force -Path $localMdDir | Out-Null }

if ($scoreXliff -ge $scoreMd) {
Write-Host "SUCCESS: XLIFF is better or equal. Converting XLIFF to local MD ($langCode)..."
./l10nUtil.exe xliff2md $remoteXliff $localMd
$docImported = $true
} else {
Write-Host "SUCCESS: Markdown is better. Importing Remote MD to local ($langCode)..."
Move-Item $remoteMd $localMd -Force
$docImported = $true
if ($scoreXliff -ge $threshold) {

if (!(Test-Path $localMdDir)) {
New-Item -ItemType Directory -Force -Path $localMdDir | Out-Null
}
} else {
Write-Host "WARNING: Both remote MD and XLIFF scores are below threshold ($threshold)."

Write-Host "SUCCESS: Importing documentation from XLIFF ($langCode)..."

./l10nUtil.exe xliff2md $remoteXliff $localMd

$docImported = $true
}
else {

if (-not $docImported -and (Test-Path $localMd)) {
Write-Host "ACTION: Documentation quality too low. Uploading local MD to Crowdin ($crowdinLang) as fallback."
./l10nUtil.exe uploadTranslationFile $crowdinLang "$addonId.md" $localMd -c $env:L10N_UTIL_CONFIG
Write-Host "WARNING: Remote XLIFF score is below threshold ($threshold)."
}

# No Markdown fallback upload anymore.
# XLIFF is now the single translation source in Crowdin.
}

# --- STEP 4: COMMIT UPDATED TRANSLATIONS ---

git add addon/locale addon/doc

git diff --staged --quiet

if ($LASTEXITCODE -ne 0) {

git commit -m "Update translations for $addonId from Crowdin (Automatic Sync)"

Write-Host "SUCCESS: Translations committed."
} else {
}
else {

Write-Host "DEBUG: No changes in translations to commit."
}

# Push all generated commits after successful Crowdin synchronization

$pushOutput = git push 2>&1

# Get the full repository name in "owner/repository" format (e.g., hkatic/clock)
$repository = $env:GITHUB_REPOSITORY

Write-Host $pushOutput

if ($LASTEXITCODE -ne 0) {

Write-Host "ERROR: Failed to push commits to $repository."
}
elseif ($pushOutput -match "Everything up-to-date") {

Write-Host "INFO: No new commits needed to be pushed."
}
else {

Write-Host "SUCCESS: New commits successfully pushed to $repository."
}
96 changes: 47 additions & 49 deletions .github/scripts/langCodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,57 @@

# Mapping between Crowdin language IDs (keys) and standard NVDA directory names (values).
# This dictionary acts as the symmetrical counterpart to 'languageMappings.json' implemented by @nvdaes.
# It ensures that translations exported from Crowdin are stored in the correct
# It ensures that translations exported from Crowdin are stored in the correct
# local paths (e.g., 'es-ES' from Crowdin goes into the 'es' folder).
CROWDIN_TO_NVDA = {
# Arabic variants
"ar-SA": "ar_SA",

# Spanish variants
"es-ES": "es",
"es-CO": "es_CO",

# Portuguese variants
"pt-BR": "pt_BR",
"pt-PT": "pt_PT",

# Chinese variants
"zh-CN": "zh_CN",
"zh-HK": "zh_HK",
"zh-TW": "zh_TW",

# Other specific mappings from the NVDA ecosystem
"af": "af_ZA",
"de-CH": "de_CH",
"nb": "nb_NO",
"nn-NO": "nn_NO",
"sr-CS": "sr"
# Arabic variants
"ar-SA": "ar_SA",
# Spanish variants
"es-ES": "es",
"es-CO": "es_CO",
# Portuguese variants
"pt-BR": "pt_BR",
"pt-PT": "pt_PT",
# Chinese variants
"zh-CN": "zh_CN",
"zh-HK": "zh_HK",
"zh-TW": "zh_TW",
# Other specific mappings from the NVDA ecosystem
"af": "af_ZA",
"de-CH": "de_CH",
"nb": "nb_NO",
"nn-NO": "nn_NO",
"sr-CS": "sr",
}


def get_nvda_code(crowdin_code):
"""
Returns the appropriate local directory name for a given Crowdin language ID.

Args:
crowdin_code (str): The language identifier from Crowdin (e.g., 'pt-BR', 'fr').

Returns:
str: The corresponding NVDA locale folder name (e.g., 'pt_BR', 'fr').
"""
# 1. Direct check in our verified map (Priority)
if crowdin_code in CROWDIN_TO_NVDA:
return CROWDIN_TO_NVDA[crowdin_code]

# 2. Automated conversion for regional variants: Crowdin "xx-YY" -> NVDA "xx_YY"
# This handles regional codes not explicitly defined in the map.
if "-" in crowdin_code:
return crowdin_code.replace("-", "_")

# 3. Default: Return as is.
# This covers base languages that don't use regional folders in NVDA
# (e.g., 'fr', 'tr', 'bg', 'fi', 'fa').
return crowdin_code
"""
Returns the appropriate local directory name for a given Crowdin language ID.

Args:
crowdin_code (str): The language identifier from Crowdin (e.g., 'pt-BR', 'fr').

Returns:
str: The corresponding NVDA locale folder name (e.g., 'pt_BR', 'fr').
"""
# 1. Direct check in our verified map (Priority)
if crowdin_code in CROWDIN_TO_NVDA:
return CROWDIN_TO_NVDA[crowdin_code]

# 2. Automated conversion for regional variants: Crowdin "xx-YY" -> NVDA "xx_YY"
# This handles regional codes not explicitly defined in the map.
if "-" in crowdin_code:
return crowdin_code.replace("-", "_")

# 3. Default: Return as is.
# This covers base languages that don't use regional folders in NVDA
# (e.g., 'fr', 'tr', 'bg', 'fi', 'fa').
return crowdin_code


if __name__ == "__main__":
# Ensure a language code was provided as a command-line argument
if len(sys.argv) > 1:
# Standardize input and output the mapped code for PowerShell to capture
print(get_nvda_code(sys.argv[1]))
# Ensure a language code was provided as a command-line argument
if len(sys.argv) > 1:
# Standardize input and output the mapped code for PowerShell to capture
print(get_nvda_code(sys.argv[1]))
1 change: 0 additions & 1 deletion .github/workflows/auto-update-translations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,3 @@ on:
jobs:
auto_update_translations:
uses: abdel792/autoUpdateTranslations/.github/workflows/l10n-updates.yaml@8ac89c644395cf2aad6e03a4bb2be5c36526d12a

4 changes: 2 additions & 2 deletions .github/workflows/build_addon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:

steps:
- name: Checkout repo
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
Expand Down Expand Up @@ -60,7 +60,7 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: download all artifacts
uses: actions/download-artifact@v8
with:
Expand Down
Loading
Loading