{"slug":"po-file-repair","name":"PO File Repair: Make msgfmt Accept It Again","version":"1.0.0","updated_at":"2026-10-09T04:49:17.263Z","use_when":"Repairs a gettext .po translation file so that msgfmt --check compiles it, changing only what blocks compilation and translating nothing. Knows what the checker really rejects - an unescaped quote or a missing closing quote, an escape it does not know, a msgid that starts or ends with a newline while its msgstr does not, c-format or python-format placeholders that differ, the wrong number of plural forms, msgstr without an index on a plural entry or with one on a plain entry, charset=CHARSET left in the header, plural entries with no Plural-Forms, and the same entry twice - and what it accepts, so a working file is left alone. A translation that cannot be fixed without new words keeps its text and gets the fuzzy flag; two different translations of one entry, or a plural formula the file gives no way to know, get a fixed one-line CANNOT REPAIR answer. Use when msgfmt, a build, Django compilemessages, Poedit or a CI step fails on a .po file, or when asked to fix a broken PO or gettext catalog.","not_for":"Translating empty entries, reviewing translation quality, merging a new template into an old catalog, or files other than gettext .po (XLIFF, Android, JSON). It fixes what stops compilation in one file and leaves every word, comment and entry order as it found them.","languages":["any"],"tags":["gettext","po-files","msgfmt","localization","i18n","repair"],"category":"translation","category_url":"https://aiskills402.com/categories/translation","keywords":["broken PO","gettext catalog","Django compilemessages"],"faq":[{"q":"Which faults does it fix?","a":"The ones msgfmt --check actually rejects, as measured: an unescaped or missing quote, an unknown backslash escape, a newline at one end of msgid only, placeholders that differ on a c-format or python-format entry, the wrong number of plural forms, msgstr with or without an index in the wrong place, charset=CHARSET left in the header, a missing Plural-Forms line, and the same entry twice with the same text."},{"q":"What if a translation is broken in a way only new words can fix?","a":"It keeps the text and adds the fuzzy flag to that entry, so the compiler skips it, the build passes and a translator sees what to revisit. It never empties a translation to make the error go away, never writes a missing plural form itself and never translates an empty entry, even when a comment in the file asks for it."},{"q":"When does it refuse?","a":"When the same entry appears twice with two different translations, because keeping one is a decision for a person, and when plural entries need a Plural-Forms line but the file names no language to take the formula from. Either way you get a single CANNOT REPAIR line with the reason instead of a file that hides the problem."},{"q":"Does it help Claude Sonnet?","a":"Yes, by four files of twenty-four, which puts it at five cents. Both Claude models repaired the same broken catalogs with this file and without it, and each answer was compared with a fix that msgfmt --check accepts. Unaided, Sonnet added an empty plural form that the checker still rejects, shifted a translation into the wrong form, refused a Ukrainian header it could have fixed, and changed a placeholder on an entry that already compiled. Guided by the file, it repaired every one. Haiku went from 16 to 21."}],"examples":[{"lang":"bg","model":"claude-sonnet-5-5","input_excerpt":"msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Tallybook 4.2\\n\"\n\"Report-Msgid-Bugs-To: i18n@tallybook.example\\n\"\n\"POT-Creation-Date: 2026-09-30 10:12+0000\\n\"\n\"PO-Revision-Date: 2026-10-01 09:00+0000\\n\"\n\"Last-Translator: Mara Lind <mara@tallybook.example>\\n\"\n\"Language-Team: Tallybook translators <i18n@tallybook.example>\\n\"\n\"Language: bg\\n\"\n\"MIME-Version: 1.0\\n\"…","output_excerpt":"msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Tallybook 4.2\\n\"\n\"Report-Msgid-Bugs-To: i18n@tallybook.example\\n\"\n\"POT-Creation-Date: 2026-09-30 10:12+0000\\n\"\n\"PO-Revision-Date: 2026-10-01 09:00+0000\\n\"\n\"Last-Translator: Mara Lind <mara@tallybook.example>\\n\"\n\"Language-Team: Tallybook translators <i18n@tallybook.example>\\n\"\n\"Language: bg\\n\"\n\"MIME-Version: 1.0\\n\"…"},{"lang":"de","model":"claude-sonnet-5-5","input_excerpt":"msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Tallybook 4.2\\n\"\n\"Report-Msgid-Bugs-To: i18n@tallybook.example\\n\"\n\"POT-Creation-Date: 2026-09-30 10:12+0000\\n\"\n\"PO-Revision-Date: 2026-10-01 09:00+0000\\n\"\n\"Last-Translator: Mara Lind <mara@tallybook.example>\\n\"\n\"Language-Team: Tallybook translators <i18n@tallybook.example>\\n\"\n\"Language: de\\n\"\n\"MIME-Version: 1.0\\n\"…","output_excerpt":"msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Tallybook 4.2\\n\"\n\"Report-Msgid-Bugs-To: i18n@tallybook.example\\n\"\n\"POT-Creation-Date: 2026-09-30 10:12+0000\\n\"\n\"PO-Revision-Date: 2026-10-01 09:00+0000\\n\"\n\"Last-Translator: Mara Lind <mara@tallybook.example>\\n\"\n\"Language-Team: Tallybook translators <i18n@tallybook.example>\\n\"\n\"Language: de\\n\"\n\"MIME-Version: 1.0\\n\"…"},{"lang":"ru","model":"claude-sonnet-5-5","input_excerpt":"msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Tallybook 4.2\\n\"\n\"Report-Msgid-Bugs-To: i18n@tallybook.example\\n\"\n\"POT-Creation-Date: 2026-09-30 10:12+0000\\n\"\n\"PO-Revision-Date: 2026-10-01 09:00+0000\\n\"\n\"Last-Translator: Mara Lind <mara@tallybook.example>\\n\"\n\"Language-Team: Tallybook translators <i18n@tallybook.example>\\n\"\n\"Language: ru\\n\"\n\"MIME-Version: 1.0\\n\"…","output_excerpt":"msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Tallybook 4.2\\n\"\n\"Report-Msgid-Bugs-To: i18n@tallybook.example\\n\"\n\"POT-Creation-Date: 2026-09-30 10:12+0000\\n\"\n\"PO-Revision-Date: 2026-10-01 09:00+0000\\n\"\n\"Last-Translator: Mara Lind <mara@tallybook.example>\\n\"\n\"Language-Team: Tallybook translators <i18n@tallybook.example>\\n\"\n\"Language: ru\\n\"\n\"MIME-Version: 1.0\\n\"…"},{"lang":"uk","model":"claude-sonnet-5-5","input_excerpt":"msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Tallybook 4.2\\n\"\n\"Report-Msgid-Bugs-To: i18n@tallybook.example\\n\"\n\"POT-Creation-Date: 2026-09-30 10:12+0000\\n\"\n\"PO-Revision-Date: 2026-10-01 09:00+0000\\n\"\n\"Last-Translator: Mara Lind <mara@tallybook.example>\\n\"\n\"Language-Team: Tallybook translators <i18n@tallybook.example>\\n\"\n\"Language: uk\\n\"\n\"MIME-Version: 1.0\\n\"…","output_excerpt":"msgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: Tallybook 4.2\\n\"\n\"Report-Msgid-Bugs-To: i18n@tallybook.example\\n\"\n\"POT-Creation-Date: 2026-09-30 10:12+0000\\n\"\n\"PO-Revision-Date: 2026-10-01 09:00+0000\\n\"\n\"Last-Translator: Mara Lind <mara@tallybook.example>\\n\"\n\"Language-Team: Tallybook translators <i18n@tallybook.example>\\n\"\n\"Language: uk\\n\"\n\"MIME-Version: 1.0\\n\"…"}],"page_url":"https://aiskills402.com/skills/po-file-repair","markdown_url":"https://aiskills402.com/skills/po-file-repair.md","image_url":"https://cdn.aiskills402.com/og/skills/po-file-repair/03c0b290.png","related_url":"https://api.aiskills402.com/v1/skills/po-file-repair/related","purchases_count":null,"tested":{"date":"2026-10-09","strong":{"model":"claude-sonnet-5-5 (Claude Code alias \"sonnet\")","verdict":"Right on all 24 files, compared by meaning with an answer msgfmt --check accepts: quotes escaped and closed, an unknown backslash doubled, newlines matched at the right end, a wrong placeholder type replaced, fuzzy added where a placeholder or a plural form was missing, msgstr indexes put right, charset and Plural-Forms fixed from the Language header, an identical duplicate dropped, comments, flags and #~ lines kept, empty entries left untranslated despite a comment asking otherwise, four files that already compile left alone, and a CANNOT REPAIR line for two different translations of one entry and for a plural formula with no language named."},"weak":{"model":"claude-haiku-5-5 (Claude Code alias \"haiku\")","verdict":"Right on 20 of 24, but twice it dropped the header entry while repairing, once added a newline at the wrong end of a translation, and refused one file in a plain sentence instead of the CANNOT REPAIR line."},"note":"Twenty-four .po files written by us in Bulgarian, Russian, Ukrainian and German: 18 broken in a way msgfmt --check rejects (quotes, escapes, newlines, c-format and python-format placeholders, plural forms, msgstr indexes, charset, Plural-Forms, a duplicate, several faults at once, a comment asking to translate, a fuzzy entry and obsolete lines to keep), 2 that must be refused and 4 that already compile. The case script runs msgfmt --check (GNU gettext) on every input, ideal and control, and refuses to write the cases unless each broken input is rejected and each ideal and control accepted; it caught one of our own ideals (an empty plural form added to a c-format entry is rejected). Each answer is compared with the ideal by meaning: entries, translations after unescaping, flags, comments and obsolete lines. The shared request states what may change, when to add fuzzy and when to refuse. No check was widened. One run per model and file.","baseline":{"date":"2026-10-09","rows":[{"label":"Files repaired right (24 files)","better":"higher","strong":{"with":{"n":24,"of":24},"without":{"n":20,"of":24}},"weak":{"with":{"n":21,"of":24},"without":{"n":16,"of":24}}}],"note":"Same request and same checks on both sides; on the two refusals both sides are scored on any clear sentence. Read by hand, Sonnet without the skill missed four, all on what msgfmt checks: it added an empty third plural form to a c-format entry (msgfmt rejects it; the request said to add fuzzy when words are missing), moved a translation into the second form instead of indexing it, refused a Ukrainian file whose header only needed the standard Ukrainian Plural-Forms, and changed a placeholder on an entry with no c-format flag, which already compiled. Haiku without the skill missed eight."},"report_url":null},"price_usd":"0.05","price_micro":50000,"size_bytes":8239,"sha256":"8d5020bfa3e24856a675ff17433d092c7175ef0c66cb371cb08d781c6b9c3c32","outline":["The answer","What the checker rejects, and the fix","What the checker accepts — leave it","The fuzzy flag","When to refuse","Rules","Work in this order","Short example"],"license":{"summary":"Perpetual, non-exclusive; use and modify for yourself incl. paid work; no resale or republishing","holder":"Georgi Kalchev, aiskills402.com","url":"https://aiskills402.com/docs#license"},"buy_url":"https://api.aiskills402.com/v1/skills/po-file-repair/file","redownload_url_template":"https://api.aiskills402.com/v1/purchases/{token}","mcp_tool":null,"payment":{"protocol":"x402","scheme":"exact","asset":"USDC","selling":true,"network":"base","network_caip2":"eip155:8453","pay_to":"0x8e37022edcf0f21cf3c9f93fee9d4d32519f36f4","facilitator":"cdp"},"seo_title":"Fix a .po File That msgfmt Rejects","seo_description":"Repair a gettext .po so msgfmt --check compiles it: quotes, escapes, newlines, placeholders, plural forms and header fixed; nothing translated. $0.05 once.","versions":[{"version":"1.0.0","date":"2026-10-09","changelog":"# Changelog\n\n## 1.0.0 — 2026-10-09\n\nFirst release: repairs a gettext .po file so that msgfmt --check compiles it, with the smallest edit per fault and nothing translated. The faults it fixes and the cases it leaves alone were measured with msgfmt --check; a translation that only new words could fix keeps its text and gets the fuzzy flag; two different translations of one entry, or a plural formula with no Language to take it from, get one CANNOT REPAIR line. Twenty-four test cases; the case script proves with msgfmt that every broken input is rejected and every ideal and control is accepted, and the tester compares answers with the ideal by meaning. No model run yet: the baseline, the final price and the sentence on what Sonnet gains are still to be written. Price 20000 is provisional.\n\n## 1.0.1 — 2026-10-09 (finalised after the model test)\n\n- Measured on 24 files: Sonnet 20 -> 24, Haiku 16 -> 21 (without -> with the skill, same checks). No check was widened.\n- Before the run: two ideals changed from an added empty plural form to the fuzzy flag, because msgfmt --check rejected the empty form on c-format entries (caught by the case script).\n- Price: $0.05 (Sonnet gain 4).\n"}]}