I needed a translation pipeline that preserves technical terminology and tone, not just literal word swaps. Building a small LLM-powered translator on Oxlo.ai gives me request-based pricing that stays flat even when I feed it entire documentation pages. In this guide I will walk through the CLI tool I shipped, from first API call to glossary-aware batch translation.
What you'll need
- Python 3.10 or higher
- The OpenAI SDK:
pip install openai - An Oxlo.ai API key from https://portal.oxlo.ai
Step 1: Verify connectivity
Before adding logic, I confirm the client can reach Oxlo.ai and generate text. This short script sends a single request so I can catch key or network issues early.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Translate 'Hello world' to Spanish."},
],
)
print(response.choices[0].message.content)
Step 2: Write the system prompt
The system prompt is the only configuration the translator needs. It sets the role, output format, and rules for handling markdown and glossaries.
SYSTEM_PROMPT = """You are a professional translator.
Translate the user-supplied text into the requested language.
Preserve all markdown formatting, code blocks, and URLs exactly.
If a glossary is provided, use the exact translation given for those terms.
Return only a JSON object with this shape:
{"translation": "translated text here"}"""
Step 3: Build the core function
I wrap the API call in a function that accepts source text, target language, and an optional glossary dict. I use JSON mode so the response is easy to parse reliably. A quick smoke test at the bottom confirms the loop is closed.
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
SYSTEM_PROMPT = """You are a professional translator.
Translate the user-supplied text into the requested language.
Preserve all markdown formatting, code blocks, and URLs exactly.
If a glossary is provided, use the exact translation given for those terms.
Return only a JSON object with this shape:
{"translation": "translated text here"}"""
def translate(text: str, target_lang: str, glossary: dict | None = None) -> str:
glossary_block = ""
if glossary:
glossary_block = "\nGlossary (use exactly):\n" + "\n".join(
f"{k} -> {v}" for k, v in glossary.items()
)
user_message = (
f"Translate the following text into {target_lang}."
f"{glossary_block}\n\nText:\n{text}"
)
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
response_format={"type": "json_object"},
temperature=0.2,
)
result = json.loads(response.choices[0].message.content)
return result["translation"]
if __name__ == "__main__":
sample = "Oxlo.ai offers flat per-request pricing for LLM inference."
print(translate(sample, "Spanish"))
Step 4: Add batch processing for files
Real documents are larger than a few sentences. I split on double newlines to keep paragraphs intact, translate each chunk, and stream results to an output file. Because Oxlo.ai charges per request, not per token, long paragraphs do not inflate cost. See https://oxlo.ai/pricing for plan details.
import json
import sys
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
SYSTEM_PROMPT = """You are a professional translator.
Translate the user-supplied text into the requested language.
Preserve all markdown formatting, code blocks, and URLs exactly.
If a glossary is provided, use the exact translation given for those terms.
Return only a JSON object with this shape:
{"translation": "translated text here"}"""
def translate(text: str, target_lang: str, glossary: dict | None = None) -> str:
glossary_block = ""
if glossary:
glossary_block = "\nGlossary (use exactly):\n" + "\n".join(
f"{k} -> {v}" for k, v in glossary.items()
)
user_message = (
f"Translate the following text into {target_lang}."
f"{glossary_block}\n\nText:\n{text}"
)
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
response_format={"type": "json_object"},
temperature=0.2,
)
result = json.loads(response.choices[0].message.content)
return result["translation"]
def translate_file(input_path: str, output_path: str, target_lang: str, glossary: dict | None = None):
with open(input_path, "r", encoding="utf-8") as f:
paragraphs = f.read().split("\n\n")
translated = []
for i, para in enumerate(paragraphs, 1):
if not para.strip():
translated.append("")
continue
print(f"Translating paragraph {i}/{len(paragraphs)}...", file=sys.stderr)
translated.append(translate(para, target_lang, glossary))
with open(output_path, "w", encoding="utf-8") as f:
f.write("\n\n".join(translated))
if __name__ == "__main__":
translate_file("docs/readme.md", "docs/readme_es.md", "Spanish")
Step 5: Inject a glossary
Technical docs have terms that generic translators mishandle. I load a JSON glossary and pass it into every request so brand names and domain jargon stay consistent.
import json
import sys
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
SYSTEM_PROMPT = """You are a professional translator.
Translate the user-supplied text into the requested language.
Preserve all markdown formatting, code blocks, and URLs exactly.
If a glossary is provided, use the exact translation given for those terms.
Return only a JSON object with this shape:
{"translation": "translated text here"}"""
def translate(text: str, target_lang: str, glossary: dict | None = None) -> str:
glossary_block = ""
if glossary:
glossary_block = "\nGlossary (use exactly):\n" + "\n".join(
f"{k} -> {v}" for k, v in glossary.items()
)
user_message = (
f"Translate the following text into {target_lang}."
f"{glossary_block}\n\nText:\n{text}"
)
response = client.chat.completions.create(
model="qwen-3-32b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
response_format={"type": "json_object"},
temperature=0.2,
)
result = json.loads(response.choices[0].message.content)
return result["translation"]
def translate_file(input_path: str, output_path: str, target_lang: str, glossary: dict | None = None):
with open(input_path, "r", encoding="utf-8") as f:
paragraphs = f.read().split("\n\n")
translated = []
for i, para in enumerate(paragraphs, 1):
if not para.strip():
translated.append("")
continue
print(f"Translating paragraph {i}/{len(paragraphs)}...", file=sys.stderr)
translated.append(translate(para, target_lang, glossary))
with open(output_path, "w", encoding="utf-8") as f:
f.write("\n\n".join(translated))
def load_glossary(path: str) -> dict:
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
if __name__ == "__main__":
glossary = load_glossary("glossary.json")
translate_file(
input_path="docs/api_reference.md",
output_path="docs/api_reference_fr.md",
target_lang="French",
glossary=glossary,
)
A sample glossary.json looks like this:
{
"inference": "inférence",
"cold start": "démarrage à froid",
"LLM": "LLM"
}
Run it
With the script and glossary saved, I execute the final version against a short markdown file.
python translator.py
Input paragraph in docs/api_reference.md:
Oxlo.ai is a developer-first AI inference platform with request-based pricing.
One flat cost per API request regardless of prompt length.
Output written to docs/api_reference_fr.md:
Oxlo.ai est une plateforme d'inférence IA destinée aux développeurs avec une tarification basée sur les requêtes.
Un coût fixe par requête API quelle que soit la longueur du prompt.
Next steps
Wire this script into a GitHub Action so documentation stays synchronized across languages on every commit. You could also add a human review stage that feeds corrected terms back into the glossary JSON, then reruns the Oxlo.ai pipeline to improve consistency over time.
Top comments (0)