Multilingual threads aren't a translation problem — say 'write', not 'translate', and gate drafts with a local .length script.
Shipped 5 articles today. Each one gets a tweet thread in 6 languages (en / zh / ja / ko / ar / id), 4 tweets per language — 120 tweets total.
4 out of 5 needed trims on the Chinese thread. Raw numbers (printed by my script, format chars/limit):
zh[1]=160/140 OVER
zh[2]=164/140 OVER
zh[3]=152/140 OVER
...
id[3]=286/280 OVER
zh[2]=163/140 OVER
zh[3]=148/140 OVER
zh[2]=152/140 OVER
Same pattern every time: the English four tweets get drafted first (each 230–260 chars, pushing the 280 ceiling), then I ask Claude to "generate the Chinese version from these four." The Chinese version comes back over 140 almost every time.
This isn't a bug, it's the wrong word. The moment you say "translate," Claude starts going line-by-line — but a multilingual thread isn't a translation problem. Each language has to be written fresh against its own character budget. This post is about how to get Claude's first draft near the limit, so you don't burn 3–5 rounds trimming.
X's limit isn't "character count" — it's weighted characters:
- Latin letters, digits, punctuation: 1 weight each
- CJK chars (Chinese/Japanese/Korean), some emoji: 2 weight each
- Total cap per tweet: 280 weight
So in practice:
- English tweet cap: 280 chars
- Pure-Chinese tweet cap: 140 chars (140 × 2 = 280)
- Mixed text: counted per-character, 1 or 2 each
So "translate a 260-char English tweet into Chinese" doesn't give you a 260-char Chinese version — the Chinese version has ~130 chars of real room. All the punctuation, quotes, backticks from the English original still take space, and every Chinese character doubles. Literal translation almost guarantees an overrun.
When Claude sees "translate these 4 EN tweets to ZH," its first move is to preserve the English sentence structure and just swap the words:
The meaning is right, but:
- "并不相同" preserved (one char longer than just "不同")
- "已经点击了" preserved ("刚点完" is shorter)
- "并看着一个空白屏幕" preserved ("看着空白屏" is shorter)
Every preserved redundancy eats chars. Stack that across 4 tweets and you're easily 20–40 chars over budget, and now you're past the limit.
Compare the two phrasings:
What doesn't work:
"Translate these 4 English tweets to Chinese, Japanese, Korean, Arabic, Indonesian."
What does:
"For each of these 4 points, write a thread in ZH / JA / KO / AR / ID. Each language gets a fresh pass — same argument, adjusted for that language's tweet budget.
- ZH: ≤ 140 chars per tweet, hook-first, cut detail if needed
- other languages: ≤ 280 chars
- use each language's natural phrasing, don't preserve EN sentence structure
- each tweet must be self-contained but add 🧵 only on tweet 1"
The differences:
.lengthDon't eyeball it. Write a small script that runs on every draft:
tweets.each do |loc, list|
list.each_with_index do |t, i|
len = t.length
limit = loc == "zh" ? 140 : 280
status = len > limit ? "OVER" : "ok"
puts "#{loc}[#{i}]=#{len}/#{limit} #{status}"
end
end
Ruby's String#length returns Unicode codepoints — each CJK character counts as 1. X weights CJK at 2, but a Chinese-only tweet's limit is 140 codepoints (because 140 × 2 = 280 weighted), so .length maps directly to X's cap. Edge cases:
For the vast majority of CJK tweets, Ruby's .length is within ±1 of X's count. Using it as a local gate beats test-posting to X by 100×.
When Chinese tweets need trimming, these two char types go first:
Code references like `kamal exec` eat 1 char per backtick in a Chinese tweet — 2 chars per reference, zero semantic value. Fixes:
`kamal exec`"🧵 only on tweet 1. Other emojis in Chinese tweets — think twice. Each emoji basically costs 2 chars (1 codepoint + ZWJ modifiers in some cases).
'X') — saves 2 chars per pair vs. full-width (「X」)When generating multilingual threads, block these four:
Symptom: Chinese sentences with phrases like「并不相同」「已经点击了」「一个空白屏幕」that are clearly calques from English.
Block: write "don't preserve EN sentence structure" / "use natural ZH phrasing" directly in the prompt.
Symptom: Claude finishes and says "done," the output is over limit, and it doesn't notice.
Block: tell Claude to count each tweet's length itself ("count each tweet's char length and flag any over the limit"), or gate it locally with a script. I pick the script — Ruby beats Claude at counting.
Symptom: When Claude does count, it forgets emoji and full-width quotes carry extra weight.
Block: don't ask Claude "is this over limit" — run .length locally and then have it trim based on the numbers.
Symptom: sometimes every tweet gets 🧵, sometimes only tweet 1, sometimes it lands on tweet 4/4.
Block: pin it in the prompt — "🧵 only on tweet 1." Claude doesn't default-know thread visual conventions.
Six rules for getting a first-draft multilingual thread at-limit:
"cut detail if needed" tells Claude it's allowed to lose facts for length..length as gate. More accurate than Claude's own count, faster than test-posting.The real insight: Claude can write multilingual threads correctly — but its default reaction is "translate." Your job is to flip it into "compose independently per language" via the prompt. Add a .length gate and today's last article's Chinese thread was 140/140 on first draft. No trimming.