Free

Letting Claude Translate One Article Into 19 Languages

One article in 19 languages: file structure, translation prompt, 5 real traps, how to POST them all at once.


Anthropic's and OpenAI's official docs are English only. That's a massive gap in the global AI-developer content ecosystem. how2claude is filling it — every article ships in 19 languages at once: zh / en / zh-TW / ja / ko / es / pt-BR / id / vi / tr / ar / fr / de / it / ru / uk / pl / he / th.

Hiring humans for this is a nonstarter (20 translations per article, thousands of dollars each). Letting Claude do it drives marginal cost close to zero — but tossing "translate to Japanese" over the fence gets you machine-translation-flavored prose with the author's voice stripped out. What follows is the complete workflow for doing this right: file structure, how to write the translation prompt, where Claude will definitely break, how to POST 19 languages plus per-language tweets to an API in one shot.


The core decision: translation vs. localization

Two very different things.

Translation moves meaning from language A to B — what Google Translate does. Reads "correct" but "flat."

Localization preserves the author's register, rhythm, and technical level, rewriting in the target language's own "tech-blog voice." Reads like it was written natively.

Translation is a 5-minute job for Claude; localization needs the prompt to spell out:

  • Who the target reader is (developers, technical teams)
  • A reference register ("sounds like which blog")
  • Terms that must stay in the source (API names, framework names, commands, URLs)
  • Where recomposition is allowed (break long sentences, swap metaphors for ones native readers will recognize)

how2claude's /write-article command bakes a style requirement per language:

Language file Style requirement
ja.md Tech-blog voice, natural Japanese
ko.md Tech-blog voice, natural Korean
zh-TW.md Taiwanese usage, traditional characters
ar.md Modern Standard Arabic (written)
id.md Standard Indonesian

Looks like a one-liner. But for Claude it's pivotal — without it the default is "textbook translation."

File structure: 19 languages live in one directory

One directory per draft, 20 files:

docs/drafts/let-claude-translate-articles/
├── meta.json        # title + summary for all 19 languages
├── zh.md            # Chinese (source, written first)
├── en.md            # English (first batch of translations)
├── zh-TW.md
├── ja.md
├── ko.md
├── ...(14 more)
└── th.md

meta.json:

{
  "category_slug": "use-cases",
  "series_slug": "writing",
  "free": true,
  "title": {
    "zh": "让 Claude 把一篇文章翻译成 19 种语言",
    "en": "Letting Claude Translate One Article Into 19 Languages",
    "ja": "Claude に 1 本の記事を 19 言語に翻訳させる",
    "...": "..."
  },
  "summary": {
    "zh": "一篇文章 19 种语言:文件结构、翻译 prompt、5 个真实翻车点、如何一次 POST。",
    "...": "..."
  }
}

Each .md file's first line is # title, the rest is body. Title doesn't come from the md first line — it comes from meta.json, so each language's title is a polished short phrase instead of the article's opening sentence that may have stretched during translation.

The flow: write zh, then radiate out to 18 languages

Two flows to avoid:

Wrong: chain translation. zh → en → ja → ko → ...
Problem: ja is based on en, ko on ja — every hop bleeds a little meaning. By th (Thai) you've got fourth-hand information.

Right: radial translation. Finalize zh → generate en / ja / ko / ar / id / ... all directly from the source.
Every language comes straight from the original, no intermediate hops.

Prompt shape (packaged inside /write-article):

Here's a tech blog article about X, Chinese source:
[full zh.md]

Translate to these languages, with these per-language requirements:
- ja.md: tech-blog voice, natural Japanese
- ko.md: tech-blog voice, natural Korean
- ar.md: Modern Standard Arabic (written)
...

Rules:
1. Preserve the author's direct, professional, slightly self-deprecating register
2. Don't translate code blocks, commands, API names, URLs
3. Titles, subheadings, paragraph flow may be recomposed
4. Produce a separate title per language — short, with a hook, not a literal translation

Translate 18 languages in parallel, one Claude session. One key detail: do it in the same session / same model version — voice drifts across sessions and model generations.

5 places it actually breaks

1. Tweet character limits vary by language

X counts CJK characters (Chinese/Japanese/Korean) more strictly. I had Claude generate tweets with this constraint:

  • Chinese tweets ≤ 140 characters
  • Other languages ≤ 280 characters

First time through, three Chinese tweets ran long (160, 164, 152). Claude had translated the English tweets literally — an English tweet at 260 chars turns into Chinese right at the limit.

Rule: Chinese tweets must be rewritten, not translated. Requirements: 140-char cap, must have a hook, some detail loss allowed.

2. zh-TW is not character conversion

zhzh-TW invites laziness: pipe it through a simplified-to-traditional converter. Characters match, vocabulary doesn't:

zh zh-TW (wrong) zh-TW (right)
文件 文件 檔案
信息 信息 資訊
软件 軟件 軟體
视频 視頻 影片

When asking Claude for zh-TW, say both: "Taiwanese usage, traditional characters." "Traditional only" yields a character conversion.

3. RTL languages (ar, he) break layout

Arabic and Hebrew read right-to-left. Rails-side you need <html dir="rtl"> and Tailwind's rtl: variants. But the translation itself has pitfalls:

  • Code blocks stay in English, but when an Arabic paragraph wraps a code block, the browser needs unicode-bidi: isolate or the code gets "sucked" into the RTL flow
  • Mixed punctuation: Arabic uses ، instead of ,, ؟ instead of ?. Claude defaults to English punctuation; spell it out
  • Number direction: Arabic writes digits left-to-right (2026) inside a right-to-left paragraph. Browsers handle this, but Claude often mixes it up in raw text

4. Claude defaults to "flattening" the author's voice

The original's casual phrases ("knock out in one shot," "stepped in it," "ate the fall") Claude translates into Japanese/Korean as formal written forms (「一気に実装」「落とし穴」「失敗」) — meaning matches, tone evaporates.

Fix: spell it out — "preserve the direct, slightly self-deprecating, informal tech-blog voice; don't academize." Even then, some languages (German, Russian) will land a touch more "formal" than the source — the languages themselves have a written-register bias.

5. Placeholders and variables don't translate

Strings in code sit in two buckets — UI text (translate) and placeholders/variable names (don't):

t("pricing.page_title")        # don't translate (i18n key)
"user_id"                      # don't translate (variable name)
"Monthly subscription"         # shown as an example in the text → don't translate

For anything adjacent to code, "don't translate" is the safer default. Translating i18n locale files (config/locales/xx.yml) is a separate job.

Publishing: one POST, 19 languages

/publish-article POSTs the draft to how2claude.com/api/articles:

{
  "category_slug": "use-cases",
  "series_slug": "writing",
  "free": true,
  "thread": true,
  "title":   { "zh": "...", "en": "...", "ja": "...", ... },
  "summary": { "zh": "...", "en": "...", ... },
  "content": { "zh": "<md>", "en": "<md>", ... },
  "tweets":  { "en": ["...", "..."], "zh": [...], ... }
}

One request is ~300KB (19 languages × ~15KB). Server splits into DB tables:

  • articles table, title / summary / content are jsonb keyed by locale
  • x_queue_tweets table, per locale + account, dispatched into a scheduling queue

Tweets only ship to locales with a connected X account (currently en/zh/ja/ko/ar/id — 6). Languages without an account send []. Querying accounts:

Account.all.each { |a| puts "#{a.locale}: #{a.name}" }
# en: @how2claude
# zh: @howtoclaude
# ja: @how2claude_ja
# ko: @how2claude_ko
# ar: @how2claude_ar
# id: @how2claude_id

Checklist

Letting Claude translate one article into 19 languages — full checklist:

  1. Finalize zh (or en) first, then radiate the other 18 translations from that source. Don't chain.
  2. Per-language style requirement goes in the prompt. "Natural Japanese" isn't enough — say "tech-blog voice, natural Japanese."
  3. Translate title and summary separately, don't pull from the body's first paragraph. Keep all 19 titles/summaries centralized in meta.json.
  4. List what doesn't translate explicitly: code blocks, commands, API names, URLs, i18n keys, variable names. What translates: explanatory text, metaphors, paragraph structure.
  5. Tweets must be rewritten, not translated. 140 chars for Chinese, 280 for the rest. English-to-Chinese literal translation runs over the limit.
  6. For zh-TW say "Taiwanese usage + traditional characters"; otherwise you'll get a character conversion and vocabulary will stay mainland.
  7. RTL languages (ar, he) need dir="rtl" + Tailwind rtl: variants on the Rails side. Mixed punctuation and code-block isolation need handling separately.
  8. POST all languages in one shot — 19 content + 19 title/summary + tweets for every connected account, one request to the backend.

The real bottleneck isn't translation quality — Claude-4-class models translate into Japanese/Korean/Arabic/Russian at near-native quality. The bottleneck is whether you're willing to put one article in front of readers across 19 languages. Technically it takes 5 minutes. Content-wise it takes a willingness to serve 200x the audience at once.