Skip to content

Rename to Claude Translator and unlock any translation provider - #2

Merged
alexburan merged 1 commit into
mainfrom
feat/claude-translator-multi-provider
Aug 25, 2026
Merged

Rename to Claude Translator and unlock any translation provider#2
alexburan merged 1 commit into
mainfrom
feat/claude-translator-multi-provider

Conversation

@alexburan

Copy link
Copy Markdown
Contributor

Why

Two follow-ups from the last release, plus the launch blocker.

  1. One name. Repo claude-translator + skill static-site-localization was a split I introduced and it was wrong. Everything now says Claude Translator; "static-site localization" survives as the descriptive subtitle, because it is the phrase people search when they have never heard of us.
  2. Provider lock-in. translate.mjs hardcoded Google's endpoint, so "why can I only use Gemini?" was the first question any reviewer would ask.

Providers

scripts/providers/ holds three adapters — anthropic, gemini, openai — each owning only how to build a request and how to read a response. Everything that made translate.mjs worth keeping stayed put and now applies to all of them: network-error retry, HTTP backoff, halving a batch that was refused or truncated, placeholder validation, memory checkpointing.

The openai adapter is the one that matters. /v1/chat/completions is spoken by OpenAI, Azure, Groq, DeepSeek, Mistral, OpenRouter, Together and Fireworks — and by Ollama, LM Studio and vLLM:

{ "provider": "openai", "apiBaseUrl": "http://localhost:11434/v1", "model": "qwen2.5:14b" }

No key, no quota, nothing leaves the machine. Anything not on that list is a .mjs file with two exports — see the new references/providers.md.

Claude is now the default, on claude-haiku-4-5. That is ~10x the previous Gemini default, and references/throughput-and-cost.md now says so plainly with a per-provider table and the one-line change back. Burying it would be worse than the cost.

Five things that would have bitten

Silent breakage of existing configs A 1.0/1.1 config pins a Gemini model and has no provider key — the new default would have sent a Gemini model id to Anthropic. The provider is now inferred from the model id, so those configs are untouched. Only a config naming neither gets the new default.
Paying for reasoning we don't want Claude tiers don't take the same parameters. Haiku rejects output_config.effort; the tiers that accept it run adaptive thinking by default, which a translation run has no use for but is billed for. The adapter carries a capability table and pins low effort where it applies.
"It just fails on Ollama" Small models emit fenced JSON and often reject response_format outright. The parser strips fences; the JSON mode degrades json_schemajson_object → prompt-only instead of failing.
Canonical URLs pointing at a language model apiBaseUrl is deliberately a different key from baseUrl (the site's origin). Sharing one would have meant pointing at a local model also rewrote every canonical, hreflang and sitemap URL.
Gate 2 failing on every page The generator tag changed and gate 2 matches its prefix. It now accepts the 1.1 name too, so a locale directory built by the previous release still verifies after upgrading.

Verification

20 contract tests (npm test — no network, no key, now in CI) assert each adapter's exact URL, headers, body shape, usage normalisation and split-triggering behaviour. Beyond that:

  • All six gates pass with the new tag, and gate 2 still fails on injected markup, a foreign generator tag, a duplicate of our own, and an old-plus-new pair
  • Provider resolution exercised across six configs: legacy Gemini config, bare config, claude-* inference, explicit local openai, unknown provider, missing key
  • A custom adapter and a deliberately picky OpenAI-compatible server drove full runs over real HTTP — including the double JSON-mode fallback, with placeholders intact
  • Full pipeline end to end: extract → translate → review → build → verify → audit

⚠️ No live call was made to Anthropic, Google or OpenAI — there are no credentials on the machine this was built on. Request shapes are asserted against their documented contracts, not against the live services. Worth one real run per provider before tagging a release.

Two names for one thing (repo claude-translator, skill
static-site-localization) was a split I introduced and it was wrong. The
product is Claude Translator throughout: skill, package, generator tag and
landing-page URL. "static-site localization" survives as the descriptive
subtitle, because it is the phrase people search when they have never heard
of us.

Providers
The translation step no longer assumes Google. scripts/providers/ holds three
adapters — anthropic, gemini, openai — each owning only how to build a request
and how to read a response. Everything that made translate.mjs worth keeping
stayed put and now applies to all of them: network-error retry, HTTP backoff,
halving a batch that was refused or truncated, placeholder validation,
memory checkpointing.

The openai adapter is the one that matters most. /v1/chat/completions is
spoken by OpenAI, Azure, Groq, DeepSeek, Mistral, OpenRouter, Together and
Fireworks — and by Ollama, LM Studio and vLLM, so setting apiBaseUrl to
localhost runs the whole pipeline on your own hardware with no key and no
request leaving the machine. Anything not on that list is a .mjs file with
two exports; references/providers.md documents the interface.

Claude is now the default, on claude-haiku-4-5 — the tier priced for bulk
segment translation. It is roughly 10x the previous Gemini default, which
references/throughput-and-cost.md now states plainly with a per-provider
table and the one-line change back. Burying that would be worse than the
cost itself.

Things that would have bitten
- A config from 1.0/1.1 pins a Gemini model and has no provider key. Changing
  the default would have sent a Gemini model id to Anthropic. The provider is
  now inferred from the model id, so those configs are untouched; only a
  config naming neither gets the new default.
- Claude tiers do not take the same parameters. Haiku rejects
  output_config.effort outright, and the tiers that accept it run adaptive
  thinking by default — a translation run would have quietly paid for
  reasoning it has no use for. The adapter carries a capability table and
  pins low effort where it applies.
- Small and local models emit fenced JSON and often reject response_format
  entirely. The parser strips fences; the JSON mode degrades
  json_schema -> json_object -> prompt only rather than failing the run.
- apiBaseUrl is a separate key from baseUrl. Sharing one would have meant
  pointing at a local model also rewrote every canonical and hreflang URL.
- The generator tag changed, and verify.mjs gate 2 matches its prefix. Gate 2
  now accepts the 1.1 name too, so a locale directory built by the previous
  release still verifies after upgrading.

Cost is reported only when a rate is genuinely known — from config or the
provider's own table — instead of a hardcoded guess at somebody else's price
list. Token counts are always printed.

Verified without spending anything: 20 contract tests assert each adapter's
exact request shape, usage normalisation and split-triggering behaviour
(npm test, no network, now in CI). Beyond that, all six gates pass with the
new tag and still fail on injected markup, a foreign generator tag, a
duplicate of our own, and an old-plus-new pair; provider resolution was
exercised across six configs; and a custom adapter plus a deliberately picky
OpenAI-compatible server drove full runs over real HTTP, including the
double JSON-mode fallback with placeholders intact.

No live call was made to Anthropic, Google or OpenAI — no credentials on this
machine. The request shapes are asserted against their documented contracts,
not against the live services.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@alexburan
alexburan merged commit ceffd10 into main Aug 25, 2026
1 check passed
@alexburan
alexburan deleted the feat/claude-translator-multi-provider branch August 25, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant