A $1.35 fine-tune taught an open 4B model that CRA is ARC in French. It still writes NIS for SIN.
An open 4B model translated "a business number from the CRA" and wrote RRCQ, an agency that does not exist. I fine-tuned it on Tinker for about $1.35 on Government of Canada translations. On held-out canada.ca pages it now writes the government's own French acronym 66.9% of the time, against 50.8%. Every flag it raises cites its source, and it runs on my laptop.

There is no RRCQ
Every page on my site has a French twin. I build things on the side (send me a brief, get a working demo back in 48 hours), and the site ships in English and fr-CA, so every new page means a translation. So I gave an open 4B model a line any Canadian small-business page would have: "you need a business number (BN) from the CRA". It wrote "auprès du RRCQ". There is no RRCQ. The Canada Revenue Agency is the ARC in French, and in Canada's open data the government's translators wrote ARC all 31 times the pair shows up.

Who it is for
A small business pays a bilingual freelancer per page to put its site into French. The client won't read the French closely, but they will notice CRA where it should say ARC. With an open model on the freelancer's own laptop there is no per-word bill, and the client's text never leaves the machine. Sans calque gives that freelancer French from the fine-tune next to the untouched base model, plus a review list that flags three things: an English acronym left in the French, a missing French acronym the government uses (ARC for CRA, NAS for SIN, from 466 pairs learned from open data), and a term the Translation Bureau's Termium Plus says to avoid.

Nothing is fixed silently. On "most small businesses use the calendar year as their fiscal year", the untouched model writes année fiscale and the fine-tune writes année financière. Both models write "comptes à payer" for accounts payable, which Termium also flags, and the check catches it on both. A flagged sentence lands on the review list, and the "French, ready to send" box says how many blocks to check before it goes to the client.

Trained on the government's own open text
The data is all open. Every dataset on Canada's Open Government Portal has its title and description in both languages, under the Open Government Licence. I pulled all 48,116 records, dropped the ones whose French the portal marks as machine-translated, and paired paragraphs and sentences only when both sides split the same way. That gave 82,455 pairs. The fine-tune is two runs on Tinker: Qwen3.5-4B, LoRA rank 32, 8,000 pairs, 2 epochs, about 13 minutes each. Run 1 was a random sample. Run 2 drew 2,000 of its pairs from the 10,752 that contain a known acronym, because run 1 still wrote "de la RC" for CRA. Estimated cost, from Tinker's per-token price and the real token count: $1.26 and $1.35. The training script refuses to start if the estimate goes over budget, because the credit was a one-time $10.
Graded by the government's translators
I scored everything on canada.ca small-business pages, whose French was written by Government of Canada translators: 300 random sentences, plus all 147 that touch a Termium "avoid" concept. None of those sentences were in training. The government's French acronym came out right 36.0% of the time from Gemma 3 4B, 50.8% from the untouched Qwen3.5-4B, 55.8% from run 1 and 66.9% from run 2. English acronyms left in the French went from 41 to 10. The acronym gain holds up: run 2 beats the base model in all 1,000 bootstrap resamples of the 146 held-out sentences that contain a government French acronym. Overall quality does not: on chrF++ (55.32 against 56.09) run 2 is ahead in 92% of resamples, and I had set 95% as the bar before running it.

It still makes mistakes
It is not always right, and the base model sometimes is. On "you can apply for your SIN", the base model writes "numéro d'assurance sociale (NAS)", which is correct. Run 2 writes "NIS". The acronym check catches it, because the government wrote NAS in 10 of 10 pairs. Run 2 also wrote "Conserver" where an instruction needs "Conservez", and none of my checks catch that one. Run 2 isn't better everywhere either: it leaves more English acronyms and more Termium terms than run 1. I serve it anyway because it gets the acronyms a client notices, and the box still says to read the French before you send it.

The checks got checked too
My first Termium check flagged the government's own translators 21 times out of 25. The label it was counting, "avoid, see observation", means avoid in some contexts only, so I dropped it. What is left flags the human translation once, for "année fiscale", which Termium calls a calque and canada.ca still uses. I left that one in. A script, check_claims.py, rebuilds every number in the README from the results files and fails if the prose and the data disagree. It also fails if a model output quoted in the docs is not in the recorded outputs.
The weights are mine
I exported the fine-tune from Tinker, converted it to a 2.59 GB 4-bit file, and it runs in Ollama on my laptop's 8 GB GPU at 32.5 tokens a second. The same web page runs against it: I loaded it with every request outside my laptop blocked, and it still translated, fonts included, with zero requests out. That took three dead ends: Ollama no longer accepts LoRA adapters, its own quantiser needs a Mac, and an 8-bit file ran at 0.8 tokens a second. Shrinking to 4-bit can cost something, so I scored the laptop copies on the same test. For run 1 it did (chrF++ 54.96 against 55.65 in the cloud). For run 2 it did not: 56.43 against 56.09, and 67.8% of the government's acronyms against 66.9%. Statistically those are the same model, so I read it as nothing lost, not as the laptop being better.

My first user was my own site
I pasted the English "How I work" page from this site into the live app: 41 blocks. The fine-tune raised 8 flags. 7 were noise for this kind of text: MCP, LLM, RAG, FHIR, SDK and a NICE guideline code, all acronyms my own French page keeps in English. The acronym rule learned how the government writes, and tech jargon does not follow it. The 8th was real. My sentence says the logs held data "the AI had already claimed was PHI-free", and the French came back as "lesquels avaient déjà été déclarés exempts", which means "which had already been declared free". The AI is gone from the sentence, and it was the whole point of it. So I built what that told me to build: a "keep in English" list per client, and a "Fetch the page" box so a page comes in by its address. Same page, fetched by the app, with those terms on the list: 8 flags became 1, and the one left is the real catch.

What is not claimed
The headline number is graded by text I did not write: Government of Canada translators on held-out canada.ca pages, which is the only reason it is worth anything. It is still narrow. The gain is in acronyms and terms; overall translation quality is not measurably better, and I set the bar for saying so before I looked. The checks only know what the government's open data and Termium know, so they miss errors like "Conserver" for "Conservez", and they over-flag tech acronyms until a client's keep-in-English list is set. The model learned from government catalogue text, so it has no particular flair for marketing copy. And the recipe (fine-tune on a government's open bilingual text, then check every term against its term bank) should carry to places like the EU's IATE, but I have only measured it here.
Sans calque: English to Canadian French from an open 4B model I fine-tuned for $1.35 on government translations, with every flag citing where its rule came from
View the projectRelated reading

A bingo card of what is out near your town this October, ranked by an open model on a laptop
Around Hawkesbury, Ontario, iNaturalist holds about 3,000 research-grade records in total, and the most-logged species across every October on record has four sightings. Too few to rank a small town on its own. Nature Bingo borrows from 48 neighbouring towns: TabPFN, an open-weight model running on my laptop, learns ten years of Octobers and ranks the 16 things most likely out this October. In a blind test on October 2025 its card beat each town's last-October list in 23 towns, tied 14 and lost 11. Then I took Hawkesbury's card outside on my phone and walked it.

My mom has published 710 pieces in 16 years. Nothing she wrote links to 526 of them.
In May my mom sent me a poster she had made: a graveyard at dusk, six tombstones carrying titles of her own work, and her line across the top, don't treat your content like a graveyard, treat it like a library. She asked for everything she has written in one place, and something to point her back at it when she starts something new. I built her a social media scheduler instead. This weekend I built the thing she actually asked for: one page, 710 pieces, and an open model that runs inside her browser tab so her unpublished drafts never leave her laptop. She graded it without knowing, because the answer key is every link she made by hand between 2011 and 2026.