AI Tools Compared

Code translation — converting a working codebase from one language to another — is one of the highest-stakes AI tasks. A wrong line can introduce a subtle bug that passes tests but fails in production. This guide tests the tools and workflows for language migration, focusing on the translations developers actually need.

The Translation Challenge

AI code translation isn’t just syntax substitution. The hard parts are:

Tools that do syntax translation produce code that compiles but reads like the source language. Good translation produces idiomatic target language code.

Tool 1: Claude for Full-File Translation

Claude handles the most complex translations because it understands idioms, not just syntax.

Python to Go Translation Test

Input (Python):

from dataclasses import dataclass
from typing import Optional, List
from datetime import datetime
import json

@dataclass
class Order:
    id: str
    customer_email: str
    items: List[dict]
    total: float
    created_at: datetime
    shipped_at: Optional[datetime] = None

    def is_shipped(self) -> bool:
        return self.shipped_at is not None

    def to_json(self) -> str:
        return json.dumps({
            "id": self.id,
            "customer_email": self.customer_email,
            "items": self.items,
            "total": self.total,
            "created_at": self.created_at.isoformat(),
            "shipped_at": self.shipped_at.isoformat() if self.shipped_at else None,
        })

def process_orders(orders: List[Order]) -> dict:
    shipped = [o for o in orders if o.is_shipped()]
    revenue = sum(o.total for o in orders)
    return {"shipped_count": len(shipped), "total_revenue": revenue}

Claude’s Go output:

package orders

import (
    "encoding/json"
    "time"
)

type Order struct {
    ID            string         `json:"id"`
    CustomerEmail string         `json:"customer_email"`
    Items         []map[string]any `json:"items"`
    Total         float64        `json:"total"`
    CreatedAt     time.Time      `json:"created_at"`
    ShippedAt     *time.Time     `json:"shipped_at,omitempty"`
}

func (o *Order) IsShipped() bool {
    return o.ShippedAt != nil
}

func (o *Order) ToJSON() ([]byte, error) {
    return json.Marshal(o)
}

type OrderSummary struct {
    ShippedCount int     `json:"shipped_count"`
    TotalRevenue float64 `json:"total_revenue"`
}

func ProcessOrders(orders []Order) OrderSummary {
    var shippedCount int
    var totalRevenue float64

    for _, o := range orders {
        if o.IsShipped() {
            shippedCount++
        }
        totalRevenue += o.Total
    }

    return OrderSummary{
        ShippedCount: shippedCount,
        TotalRevenue: totalRevenue,
    }
}

Key translation decisions Claude made correctly:

Tool 2: GPT-4o for JavaScript to TypeScript

TypeScript migration is the most common translation need for web teams. GPT-4o excels here:

// Input: JavaScript module
const express = require('express');
const db = require('./db');

const router = express.Router();

router.get('/users', async (req, res) => {
  try {
    const { page = 1, limit = 20, role } = req.query;
    const offset = (page - 1) * limit;

    let query = db('users').select('*');
    if (role) query = query.where({ role });

    const users = await query.limit(limit).offset(offset);
    const total = await db('users').count('id as count').first();

    res.json({
      users,
      pagination: { page: Number(page), limit: Number(limit), total: Number(total.count) }
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

GPT-4o TypeScript output:

import { Router, Request, Response } from 'express';
import type { Knex } from 'knex';
import db from './db';

interface User {
  id: number;
  email: string;
  name: string;
  role: 'admin' | 'member' | 'viewer';
  created_at: Date;
}

interface PaginationParams {
  page?: string;
  limit?: string;
  role?: string;
}

interface PaginatedResponse<T> {
  data: T[];
  pagination: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  };
}

const router = Router();

router.get('/users', async (req: Request<{}, {}, {}, PaginationParams>, res: Response) => {
  try {
    const page = parseInt(req.query.page ?? '1', 10);
    const limit = parseInt(req.query.limit ?? '20', 10);
    const { role } = req.query;
    const offset = (page - 1) * limit;

    let query = db<User>('users').select('*');
    if (role) query = query.where({ role });

    const [users, countResult] = await Promise.all([
      query.limit(limit).offset(offset),
      db('users').count<{ count: string }>('id as count').first(),
    ]);

    const total = parseInt(countResult?.count ?? '0', 10);

    const response: PaginatedResponse<User> = {
      data: users,
      pagination: {
        page,
        limit,
        total,
        totalPages: Math.ceil(total / limit),
      },
    };

    res.json(response);
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Unknown error';
    res.status(500).json({ error: message });
  }
});

export default router;

GPT-4o added: proper TypeScript generics, parallel database calls with Promise.all, safer error message extraction, and a totalPages field it inferred was useful. This is better than the original JavaScript.

Tool 3: GitHub Copilot for In-Editor Translation

Copilot’s inline translate works well for smaller functions. Open the target file, add a comment:

// Translate this Python function to idiomatic Go:
// def fibonacci(n: int) -> list[int]:
//     if n <= 0: return []
//     if n == 1: return [0]
//     fib = [0, 1]
//     while len(fib) < n:
//         fib.append(fib[-1] + fib[-2])
//     return fib

Copilot generates the Go implementation inline. This works well for function-by-function migration but doesn’t handle cross-file context.

Java to Kotlin Migration

The most common JVM migration uses IntelliJ’s built-in converter first, then AI to fix idioms:

# Step 1: Use IntelliJ's Java-to-Kotlin converter (Code > Convert Java File to Kotlin)
# It handles 80% of the conversion automatically

# Step 2: Use AI to fix non-idiomatic patterns
# Common patterns that need AI cleanup:
// IntelliJ produces (non-idiomatic):
fun getUser(id: String): User? {
    if (id == null) return null
    val user = repository.findById(id)
    if (user == null) return null
    return user
}

// Ask Claude to make it idiomatic Kotlin:
// "Make this Kotlin more idiomatic using Elvis operator, let, and takeIf"

// Claude output:
fun getUser(id: String?): User? = id?.let { repository.findById(it) }

Batch Translation Workflow

For translating entire directories:

# translate_codebase.py
import anthropic
from pathlib import Path
import sys

client = anthropic.Anthropic()

SOURCE_LANG = "Python"
TARGET_LANG = "Go"
SOURCE_EXT = ".py"
TARGET_EXT = ".go"

def translate_file(source_path: Path, target_path: Path) -> bool:
    source_code = source_path.read_text()

    response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=4096,
        messages=[{
            "role": "user",
            "content": f"""Translate this {SOURCE_LANG} file to idiomatic {TARGET_LANG}.

Rules:
- Use {TARGET_LANG} idioms and patterns, not {SOURCE_LANG} patterns
- Keep the same public API (function/method names and signatures where possible)
- Add proper error handling in the {TARGET_LANG} style
- Include package declaration and imports
- Do NOT include explanatory comments about translation choices

{SOURCE_LANG} source:
```{SOURCE_LANG.lower()}
{source_code}

Output only the {TARGET_LANG} code, no explanations.””” }] )

code = response.content[0].text
if f"```{TARGET_LANG.lower()}" in code:
    code = code.split(f"```{TARGET_LANG.lower()}")[1].split("```")[0].strip()

target_path.parent.mkdir(parents=True, exist_ok=True)
target_path.write_text(code)
return True

Translate all files in a directory

source_dir = Path(sys.argv[1]) target_dir = Path(sys.argv[2])

for source_file in source_dir.rglob(f”*{SOURCE_EXT}”): if “pycache” in str(source_file): continue

relative = source_file.relative_to(source_dir)
target_file = target_dir / relative.with_suffix(TARGET_EXT)

print(f"Translating {source_file} → {target_file}")
translate_file(source_file, target_file) ```

Accuracy by Translation Type

Translation Claude GPT-4o Copilot
Python → TypeScript Excellent Excellent Good
JavaScript → TypeScript Good Excellent Good
Python → Go Excellent Good Fair
Java → Kotlin Good Good Good
TypeScript → Python Good Good Fair

Neither tool produces code that compiles without any review. Plan for 15-30% of translated files needing fixes before tests pass.

Built by theluckystrike — More at zovo.one