Skip to content

Translate texts, guides, etc by @floriangeigl #38

Translate texts, guides, etc by @floriangeigl

Translate texts, guides, etc by @floriangeigl #38

name: Translate texts, guides, etc
run-name: Translate texts, guides, etc by @${{ github.actor }}
concurrency:
group: translate-content
cancel-in-progress: true
on:
push:
branches:
- "main"
paths:
- "UserGuide.md"
- "Advertisement.md"
- "ConnectIQStore/MeditateStoreDescription-en.txt"
- "userGuideScreenshots/hero_meditate.png"
- ".github/codex/prompts/translate-content.md"
- ".github/workflows/translate-content.yml"
jobs:
detect-changes:
name: Detect changes
runs-on: ubuntu-latest
outputs:
text_changed: ${{ steps.filter.outputs.text }}
image_changed: ${{ steps.filter.outputs.image }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
text:
- "UserGuide.md"
- "Advertisement.md"
- "ConnectIQStore/MeditateStoreDescription-en.txt"
- ".github/codex/prompts/translate-content.md"
- ".github/workflows/translate-content.yml"
image:
- "userGuideScreenshots/hero_meditate.png"
- ".github/workflows/translate-content.yml"
prepare-branch:
name: Prepare translation branch
needs: detect-changes
if: needs.detect-changes.outputs.text_changed == 'true' || needs.detect-changes.outputs.image_changed == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: cleanup and create release-translation branch
run: |
git push -d origin release-translation || echo "no remote branch to cleanup"
git checkout -B release-translation
git push -f origin release-translation
translate-text:
name: Translate text
needs: [detect-changes, prepare-branch]
if: needs.detect-changes.outputs.text_changed == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
ref: release-translation
- name: Translate via Codex
id: codex_translate
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.CHATGPT_TOKEN }}
prompt-file: .github/codex/prompts/translate-content.md
codex-args: --full-auto
safety-strategy: drop-sudo
sandbox: workspace-write
model: gpt-5.4
- name: Commit text translations
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add ./generated/
git commit -m "Update text translations" || echo "No text translation changes to commit"
git pull --rebase origin release-translation || true
git push origin release-translation
translate-images:
name: Translate hero image
needs: [detect-changes, prepare-branch]
if: needs.detect-changes.outputs.image_changed == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
ref: release-translation
- name: Translate hero image for each language
shell: bash
env:
OPENAI_API_KEY: ${{ secrets.CHATGPT_TOKEN }}
run: |
pip install Pillow --quiet
declare -A LANG_FULL=([de]="German" [pt]="Brazilian Portuguese" [ko]="Korean" [es]="Spanish" [zh]="Chinese Simplified" [uk]="Ukrainian" [ja]="Japanese" [fr]="French")
SRC="userGuideScreenshots/hero_meditate.png"
mkdir -p generated/HeroImages
# Pad to 1536x1024 (nearest supported size) preserving center
python3 -c "
from PIL import Image
img = Image.open('$SRC').convert('RGB')
padded = Image.new('RGB', (1536, 1024), (0, 0, 0))
x = (1536 - img.width) // 2
y = (1024 - img.height) // 2
padded.paste(img, (x, y))
padded.save('/tmp/hero_padded.png')
"
for lang in "${!LANG_FULL[@]}"; do
lang_name="${LANG_FULL[$lang]}"
echo "Translating hero image to ${lang_name} (${lang})..."
prompt="You are editing a hero banner image for a Garmin watch meditation and breathwork app.
Translate ALL visible text in this image into ${lang_name}.
Rules:
- Translate the app name naturally (e.g. for German: \"Meditation & Atemübungen\"). Use the most idiomatic phrasing. Keep the & separator.
- Translate all other visible text naturally and fluently, as a native ${lang_name} speaker would write it. The tone is warm, gentle, and encouraging.
- Preserve the EXACT same visual design: background, colors, gradients, watch imagery, layout, typography style, text positioning, and text sizing.
- Only change the language of the text. Everything else must remain pixel-identical.
- The result must look professional and polished, as if it were the original design in ${lang_name}.
- Do NOT add any new text, watermarks, logos, or visual elements."
response=$(curl -s -X POST "https://api.openai.com/v1/images/edits" \
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
-F "model=gpt-image-1" \
-F "image[]=@/tmp/hero_padded.png" \
-F "prompt=${prompt}" \
-F "size=1536x1024" \
-F "quality=high")
# Check for errors
error=$(echo "$response" | jq -r '.error.message // empty')
if [ -n "$error" ]; then
echo "::warning::Failed to translate hero image to ${lang_name}: ${error}"
continue
fi
# Decode base64 image
echo "$response" | jq -r '.data[0].b64_json' | base64 -d > "/tmp/hero_${lang}_raw.png"
# Crop back to center 2:1 ratio then resize to exact 1440x720
python3 -c "
from PIL import Image
img = Image.open('/tmp/hero_${lang}_raw.png').convert('RGB')
# Center crop to 1536x768
left = (img.width - 1536) // 2
top = (img.height - 768) // 2
img = img.crop((left, top, left + 1536, top + 768))
img = img.resize((1440, 720), Image.LANCZOS)
img.save('generated/HeroImages/hero_meditate-${lang}.png')
"
rm -f "/tmp/hero_${lang}_raw.png"
echo "Done: generated/HeroImages/hero_meditate-${lang}.png"
done
rm -f /tmp/hero_padded.png
- name: Commit image translations
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add ./generated/HeroImages/
git commit -m "Update hero image translations" || echo "No image translation changes to commit"
git pull --rebase origin release-translation || true
git push origin release-translation
create-pr:
name: Create or update PR
needs: [detect-changes, translate-text, translate-images]
if: always() && (needs.translate-text.result == 'success' || needs.translate-images.result == 'success')
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Create or update PR to main
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const head = `${owner}:release-translation`;
const base = 'main';
const { data: pulls } = await github.rest.pulls.list({
owner,
repo,
head,
base,
state: 'open',
per_page: 10,
});
if (pulls.length > 0) {
core.info(`PR already exists: #${pulls[0].number}`);
return;
}
const { data: pr } = await github.rest.pulls.create({
owner,
repo,
head: 'release-translation',
base,
title: 'Update translations',
body: 'Automated translation updates generated by the translation workflow.',
maintainer_can_modify: true,
});
core.info(`Created PR: #${pr.number}`);