Kristopher Baker iOS roots · Product systems · AI-assisted workflows
← All entries

2026.04.20 · 6 min read

JPResume: Turning a Western Resume Into a Japanese One

In the last couple of months, two different people asked if I could share my Japanese-style resume. Not my English one. The 履歴書 and 職務経歴書 — the formats Japanese companies actually ask for when you apply.

Both times, my answer was the same awkward “let me get back to you on that.”

My English resume was in decent shape. My Japanese one was a pile of half-finished notes, already out of date the moment I touched the English version. Rewriting both by hand every job cycle, in a second language, was not going to happen.

So I built JPResume, a small Swift CLI that turns a western-style resume into both Japanese formats and keeps them in sync.

Why a Japanese Resume Is a Different Problem

If you have only written western resumes, it is tempting to treat the Japanese versions as a translation job. They are not.

A 履歴書 (rirekisho) is a grid form: name, furigana, photo slot, education and work history with year/month columns, licenses, 志望動機, and 本人希望記入欄. There are conventions for marking your current role, ending the work history section with 「現在に至る」, and choosing western years or Japanese era dates. Some companies care about these details. Some do not. None of it is obvious the first time.

A 職務経歴書 (shokumukeirekisho) is the free-form counterpart. This is where the actual career story lives: 職務要約, role details, skills, achievements, and 自己PR. The phrasing conventions are their own skill. “Drove a 29.8% increase in sign-ups” reads naturally in English. In Japanese, something like 新規会員登録数の29.8%増加に寄与 fits better.

Then there is the alignment problem. Your English and Japanese resumes need to tell the same story, with the same dates, titles, and emphasis. When they drift, you usually notice at the worst possible moment.

That is the part a tool can help with.

How It Works

The pipeline is boring on purpose: deterministic where possible, LLM-assisted where useful, and reviewable in between.

flowchart LR
    parse[Parse]
    normalize["Normalize (LLM)"]
    repair[Repair]
    validate[Validate]
    generate["Generate (LLM)"]
    render[Render]

    parse --> normalize --> repair --> validate --> generate --> render

Parse is deterministic. It accepts markdown, DOCX, or PDF and produces a rough structured version. Markdown goes through a parser. DOCX and PDF go through text preprocessing, because once text comes out of those formats, the structure is mostly gone. Scanned PDFs fall back to Vision OCR.

Normalize is where the LLM first appears. It combines the parsed resume with jpresume_config.yaml — kanji name, furigana, address, education, certifications, and other Japan-specific ground truth — and produces a normalized structure. Dates become real integers. Bullets are classified as achievements or responsibilities. Skills are grouped into categories. The model is explicitly told not to invent anything. Ambiguous dates should be flagged with low confidence, not guessed.

Repair is deterministic again. It sorts roles, reconciles overlapping dates, and fixes is_current inconsistencies. Validate surfaces what remains: overlaps, low-confidence entries, suspicious gaps, or total years that look off.

Generate produces the rirekisho and shokumukeirekisho JSON. Render turns those into markdown and native PDFs using CoreGraphics and Hiragino Sans, so the Japanese typography looks intentional instead of falling back to an awkward default.

The shape is familiar if you have built AI-assisted tooling: keep control where you can, use the model where it helps, and put a human-readable artifact between stages.

The Workflow I Actually Use

The CLI can run end to end:

jpresume convert resume.md --provider claude-cli --format both

That works. But for most changes, I use the agent skill, because it supports the workflow I actually wanted: external mode.

In external mode, the LLM stages do not call a provider directly. They write a prompt bundle to disk and exit. An agent — Claude Code, Cursor, Codex, whatever you prefer — reads the bundle, produces the response JSON, writes it back, and re-runs the stage with --ingest.

The agent becomes the model.

That sounds like an implementation detail, but it changes the experience. The agent sees every prompt and every response. It can pause, ask me a question, fix a bad field, and re-ingest without restarting the whole pipeline. When it normalizes a role, I can see exactly how it interpreted the dates before anything downstream depends on them. If something is wrong, the fix is a JSON edit, not a full re-prompt.

This is the part I would have gotten wrong as a one-shot tool. For a first draft, one-shot generation is fine. For a document you might actually send to a company, the review loop is the feature.

The Honest Limits

JPResume has really only been tested on one resume: mine.

That resume has a specific shape. One country of origin. A software engineering career. Company names Japanese recruiters are likely to recognize. Relatively clean dates. A JLPT certification that maps neatly to standard phrasing. It does not cover every edge case: career switches, international graduate programs, long intentional gaps, unusual role titles, or industries where Japanese resume conventions differ from tech.

I would not be surprised if the first outside resume exposes something the normalizer handles poorly, or a phrasing pattern the generator does not produce gracefully. The pipeline is designed to make those fixes cheap — edit an artifact, re-run one stage — but I have not seen those cases yet. My sample size is one.

So if you try it and something looks wrong, or you know a resume shape it should handle better, I would genuinely like to hear about it. Issues, pull requests, or notes are welcome. The install instructions and agent skill are both in the README.

What I Would Keep Even If the Tool Changes

The part I expect to outlast this implementation is the shape: deterministic parsing, strict no-invention normalization, a deliberate validation pause, and external mode so an agent can act as the model while still reviewing its own output.

That pattern works beyond Japanese resumes. It fits any document where fabricated fields are expensive and a fully automated one-shot is the wrong abstraction: regulatory filings, contracts, internal reports, anything with structure and consequences.

For now, JPResume turns a markdown resume into a 履歴書 and 職務経歴書 that I would actually hand to a company.

A couple of months ago, that was the part I could not do.