ci: add GitHub Actions workflow for pushing translations to POEditor
This commit is contained in:
Executable
+138
@@ -0,0 +1,138 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
I18N_DIR=resources/i18n
|
||||||
|
|
||||||
|
# Normalize JSON for deterministic comparison:
|
||||||
|
# remove empty/null attributes, sort keys alphabetically
|
||||||
|
process_json() {
|
||||||
|
jq 'walk(if type == "object" then with_entries(select(.value != null and .value != "" and .value != [] and .value != {})) | to_entries | sort_by(.key) | from_entries else . end)' "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get list of all languages configured in the POEditor project
|
||||||
|
get_language_list() {
|
||||||
|
curl -s -X POST https://api.poeditor.com/v2/languages/list \
|
||||||
|
-d api_token="${POEDITOR_APIKEY}" \
|
||||||
|
-d id="${POEDITOR_PROJECTID}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract language name from the language list JSON given a language code
|
||||||
|
get_language_name() {
|
||||||
|
lang_code="$1"
|
||||||
|
lang_list="$2"
|
||||||
|
echo "$lang_list" | jq -r ".result.languages[] | select(.code == \"$lang_code\") | .name"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract language code from a file path (e.g., "resources/i18n/fr.json" -> "fr")
|
||||||
|
get_lang_code() {
|
||||||
|
filepath="$1"
|
||||||
|
filename=$(basename "$filepath")
|
||||||
|
echo "${filename%.*}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Export the current translation for a language from POEditor (v2 API)
|
||||||
|
export_language() {
|
||||||
|
lang_code="$1"
|
||||||
|
response=$(curl -s -X POST https://api.poeditor.com/v2/projects/export \
|
||||||
|
-d api_token="${POEDITOR_APIKEY}" \
|
||||||
|
-d id="${POEDITOR_PROJECTID}" \
|
||||||
|
-d language="$lang_code" \
|
||||||
|
-d type="key_value_json")
|
||||||
|
|
||||||
|
url=$(echo "$response" | jq -r '.result.url')
|
||||||
|
if [ -z "$url" ] || [ "$url" = "null" ]; then
|
||||||
|
echo "Failed to export $lang_code: $response" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "$url"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Flatten nested JSON to POEditor languages/update format.
|
||||||
|
# POEditor uses term + context pairs, where:
|
||||||
|
# term = the leaf key name
|
||||||
|
# context = the parent path as "key1"."key2"."key3" (empty for root keys)
|
||||||
|
flatten_to_poeditor() {
|
||||||
|
jq -c '[paths(scalars) as $p |
|
||||||
|
{
|
||||||
|
"term": ($p | last | tostring),
|
||||||
|
"context": (if ($p | length) > 1 then ($p[:-1] | map("\"" + tostring + "\"") | join(".")) else "" end),
|
||||||
|
"translation": {"content": getpath($p)}
|
||||||
|
}
|
||||||
|
]' "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Update translations for a language in POEditor via languages/update API
|
||||||
|
update_language() {
|
||||||
|
lang_code="$1"
|
||||||
|
file="$2"
|
||||||
|
|
||||||
|
flatten_to_poeditor "$file" > /tmp/poeditor_data.json
|
||||||
|
response=$(curl -s -X POST https://api.poeditor.com/v2/languages/update \
|
||||||
|
-d api_token="${POEDITOR_APIKEY}" \
|
||||||
|
-d id="${POEDITOR_PROJECTID}" \
|
||||||
|
-d language="$lang_code" \
|
||||||
|
--data-urlencode data@/tmp/poeditor_data.json)
|
||||||
|
rm -f /tmp/poeditor_data.json
|
||||||
|
|
||||||
|
status=$(echo "$response" | jq -r '.response.status')
|
||||||
|
if [ "$status" != "success" ]; then
|
||||||
|
echo "Failed to update $lang_code: $response" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
parsed=$(echo "$response" | jq -r '.result.translations.parsed')
|
||||||
|
added=$(echo "$response" | jq -r '.result.translations.added')
|
||||||
|
updated=$(echo "$response" | jq -r '.result.translations.updated')
|
||||||
|
echo " Translations - parsed: $parsed, added: $added, updated: $updated"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Main ---
|
||||||
|
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "Usage: $0 <file1> [file2] ..."
|
||||||
|
echo "No files specified. Nothing to do."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
lang_list=$(get_language_list)
|
||||||
|
upload_count=0
|
||||||
|
|
||||||
|
for file in "$@"; do
|
||||||
|
if [ ! -f "$file" ]; then
|
||||||
|
echo "Warning: File not found: $file, skipping"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
lang_code=$(get_lang_code "$file")
|
||||||
|
lang_name=$(get_language_name "$lang_code" "$lang_list")
|
||||||
|
|
||||||
|
if [ -z "$lang_name" ]; then
|
||||||
|
echo "Warning: Language code '$lang_code' not found in POEditor, skipping $file"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Processing $lang_name ($lang_code)..."
|
||||||
|
|
||||||
|
# Export current state from POEditor
|
||||||
|
url=$(export_language "$lang_code")
|
||||||
|
curl -sSL "$url" -o poeditor_export.json
|
||||||
|
|
||||||
|
# Normalize both files for comparison
|
||||||
|
process_json "$file" > local_normalized.json
|
||||||
|
process_json poeditor_export.json > remote_normalized.json
|
||||||
|
|
||||||
|
# Compare normalized versions
|
||||||
|
if diff -q local_normalized.json remote_normalized.json > /dev/null 2>&1; then
|
||||||
|
echo " No differences, skipping"
|
||||||
|
else
|
||||||
|
echo " Differences found, updating POEditor..."
|
||||||
|
update_language "$lang_code" "$file"
|
||||||
|
upload_count=$((upload_count + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f poeditor_export.json local_normalized.json remote_normalized.json
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Done. Updated $upload_count translation(s) in POEditor."
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
name: POEditor export
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- 'resources/i18n/*.json'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
push-translations:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.repository_owner == 'navidrome' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
|
||||||
|
- name: Detect changed translation files
|
||||||
|
id: changed
|
||||||
|
run: |
|
||||||
|
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD -- 'resources/i18n/*.json' | tr '\n' ' ')
|
||||||
|
echo "files=$CHANGED_FILES" >> $GITHUB_OUTPUT
|
||||||
|
echo "Changed translation files: $CHANGED_FILES"
|
||||||
|
|
||||||
|
- name: Push translations to POEditor
|
||||||
|
if: ${{ steps.changed.outputs.files != '' }}
|
||||||
|
env:
|
||||||
|
POEDITOR_APIKEY: ${{ secrets.POEDITOR_APIKEY }}
|
||||||
|
POEDITOR_PROJECTID: ${{ secrets.POEDITOR_PROJECTID }}
|
||||||
|
run: |
|
||||||
|
.github/workflows/push-translations.sh ${{ steps.changed.outputs.files }}
|
||||||
Reference in New Issue
Block a user