Translating a product listing is mostly solved: titles, bullets and descriptions go through any translation API. The images are where localization usually stops. Infographics with dimensions and feature callouts, "what's in the box" visuals, size charts and supplier photos with Chinese labels all keep their original text, and on a marketplace the images are what shoppers read first.
This tutorial builds a script that takes a folder of product images and a list of target languages, sends each image to an image translation API, and saves the translated versions next to the originals, ready to review.
It uses TranslateMyImage, which I build. The API detects the text in the image, translates it, and returns a new image with the translation placed where the original text was.
What you need
- API access, which is included from the Pro plan. Each completed translation consumes one credit.
- An API key from the API Keys page of the dashboard. It is shown once, so store it right away.
- Python 3.9+ and
requests.
pip install requests
export TMI_API_KEY="your-key"
The endpoint takes multipart/form-data (the full reference is at translatemyimage.com/api-docs):
| Field | Required | Values |
|---|---|---|
file |
yes | JPG, PNG or WEBP, up to 10 MB and 25 megapixels |
target_lang |
yes |
en, fr, es, de, it, pt, ja, ko, zh
|
source_lang |
no | a language code, or auto (the default) |
quality |
no |
basic (default) or ultra
|
business_type |
no |
default, marketing, edition, technical documentation
|
One image
import os
from pathlib import Path
import requests
API_URL = "https://translatemyimage.com/api/translate"
HEADERS = {"X-API-Key": os.environ["TMI_API_KEY"]}
class TranslationError(Exception):
def __init__(self, status: int, payload: dict):
super().__init__(f"{status}: {payload.get('error')} ({payload.get('code')})")
self.status = status
self.code = payload.get("code")
def translate_image(path: Path, target_lang: str) -> dict:
with path.open("rb") as f:
r = requests.post(
API_URL,
headers=HEADERS,
files={"file": (path.name, f)},
data={
"target_lang": target_lang,
"source_lang": "auto",
"business_type": "marketing",
},
timeout=300,
)
payload = r.json()
if not r.ok or not payload.get("success"):
raise TranslationError(r.status_code, payload)
return payload["translation"]
A successful response looks like this:
{
"success": true,
"translation": {
"id": "clxb1234567890",
"originalUrl": "https://…/original.jpg",
"translatedUrl": "https://…/translated.jpg",
"sourceLang": "zh",
"targetLang": "en",
"status": "completed"
}
}
business_type: "marketing" suits ad creatives and listing images. Use "technical documentation" for manuals, spec sheets and diagrams.
A folder, several languages
import time
LANGS = ["de", "fr", "es", "it"] # e.g. the EU Amazon marketplaces
def translate_with_retry(path: Path, lang: str, attempts: int = 4) -> dict:
for attempt in range(attempts):
try:
return translate_image(path, lang)
except TranslationError as error:
transient = error.status == 429 or error.code == "service_unavailable"
if not transient or attempt == attempts - 1:
raise
time.sleep(2 ** attempt * 5)
def localize_folder(src: str = "images", out: str = "localized") -> None:
for path in sorted(Path(src).glob("*")):
if path.suffix.lower() not in {".jpg", ".jpeg", ".png", ".webp"}:
continue
for lang in LANGS:
target = Path(out) / lang / path.name
if target.exists():
continue # already done: re-running the script costs nothing
result = translate_with_retry(path, lang)
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(requests.get(result["translatedUrl"], timeout=60).content)
print(f"{path.name} → {lang}: {target}")
if __name__ == "__main__":
localize_folder()
You end up with localized/de/…, localized/fr/… and so on, mirroring the source folder. Ten images into four languages is 40 credits.
Errors worth handling
Error responses are JSON with success: false and a message in error. Once the request is authenticated they also carry a stable code, so you can branch without parsing English:
-
plan_required: the key's account does not include API access (Pro and above) or the option you asked for. -
insufficient_credits: top up or wait for the monthly reset. Stop the loop instead of retrying. -
invalid_image/invalid_request: wrong format, over 10 MB or 25 megapixels, or a bad language code. -
content_blocked: the image was refused by the safety check. Skip it. -
429orservice_unavailable: transient, retry with a backoff as above.
Review before you publish
Treat the output as a strong first draft. Check numbers, units and brand names on every image, and have a native speaker glance at anything legal or safety related. The script's folder layout makes that review easy: open localized/de and images side by side.
If you only need a handful of images, the same thing works without code at translatemyimage.com: drop an image, pick a language, download the result.
What kind of images do you need localized? Product photos, comics, UI screenshots? I would like to hear which cases break.
Top comments (0)