Blog Locale Audit, Multilingual Quality Control
Audit a directory of multilingual blog content for completeness, consistency, hreflang correctness, meta-tag parity, and freshness.
How to use it
- Hit Copy SKILL.md — or use the Claude Code line below to get every file.
- Claude: ⋯ → Download .md, then Customize → Skills → Add → Upload skill.
ChatGPT: make a Project and paste it into Instructions.
Neither? Paste it at the top of a new chat — it works for that chat. - Describe your job in plain words. The AI follows the skill from there.
npx degit AgriciDaniel/claude-blog/skills/blog-locale-audit#main ~/.claude/skills/blog-locale-audit-2For one project only, change the path to .claude/skills/blog-locale-audit-2. This skill also uses hreflang-map.json — copying SKILL.md alone won't be enough. See the folder on GitHub.
Not working?
- Check which app you pasted it into — the steps above name the right one.
- Some skills need the paid tier of Claude or ChatGPT.
Paste into Claude, ChatGPT or Cursor.
Show the full text211 lines
Blog Locale Audit, Multilingual Quality Control
Audits a directory of multilingual blog content to ensure every language version is complete, consistent, correctly tagged, and SEO-optimized. Catches international content issues before they hurt rankings.
Adapted from
claude-blog-multilingualby Chris Mueller (Pro Hub Challenge, March 2026). Original: https://github.com/Chriss54/multilingual-int
Workflow
Phase 1: Discovery
- Resolve the target directory inside the project root/current working
directory. Reject symlinked directories and traversal outside the root,
then scan blog content and group posts by language using:
- Subdirectory names (
en/,de/,fr/). - Frontmatter
langandtranslatedFromfields. hreflang-map.jsonif present.
- Subdirectory names (
- Normalize detected language codes with the shared multilingual locale rules
used by
blog-translate,blog-localize, andblog-multilingual: ISO 639-1 language in lowercase, optional ISO 15924 script in title case, optional ISO 3166-1 Alpha-2 region in uppercase. Flag ambiguous language-only codes such ases,pt, andzhunless the content declares an explicit neutral mode. - Build a content matrix mapping which post exists in which languages. Use a
stable translation-group key before comparing slugs:
translationGroupId,sourceSlug, schematranslationOfWork.url, or the IDs and URLs inhreflang-map.json. Fall back to normalized source slug only when no stable key exists. - Detect the source language (most common
translatedFromtarget, or thesourceLanguagefield inhreflang-map.jsonif present).
Phase 2: Completeness Audit
Show which translations are missing:
### Translation coverage matrix
| Post (EN) | DE | FR | ES | JA |
|-----------|----|----|----|----|
| how-to-avoid-ai-slop | ok | ok | missing | missing |
| content-marketing-2026 | ok | missing | ok | missing |
Coverage: 60% (6 of 10 expected translations present)
Missing: 4 translations needed
Phase 3: Content Parity Audit
For every post that exists in multiple languages:
| Check | What | Severity |
|---|---|---|
| Section count | Same number of H2 and H3 sections | Critical |
| FAQ count | Same number of FAQ items | High |
| Image count | Same number of images | High |
| Chart count | Same number of charts (SVG figures) | High |
| Word count ratio | Within expected band for language pair (DE +20% to +30%, JA -20%, ES +10%) | Medium |
| Link count | Similar internal and external link counts | Medium |
| Evidence-backed claims | Same supported claims and citations across versions | Medium |
| Frontmatter parity | All required fields present per version | High |
Flag every significant deviation as an issue.
Phase 4: SEO Parity Audit
For every language version verify:
| Element | Check | Severity |
|---|---|---|
| Title tag | Present, localized, clear, and appropriate for the page | Critical |
| Meta description | Present, localized, accurate, and consistent with visible content | Critical |
lang attribute or frontmatter lang |
Present, valid Google-compatible hreflang or BCP 47 language tag | Critical |
| Canonical URL | Points to the same-language page, not the source-language page or x-default | Critical |
Schema inLanguage |
Matches lang |
High |
Schema translationOfWork |
Points to the source URL | High |
| Alt text | Translated (no English alt in non-EN posts) | High |
| Slug | Localized (no English slug in non-EN posts) | Medium |
| Tags | Localized | Medium |
| Keywords | Localized | Medium |
Phase 5: Hreflang Audit
If hreflang-tags.html, hreflang-sitemap.xml, or hreflang-map.json
exists in the directory:
| Check | What | Severity |
|---|---|---|
| Self-referencing | Each page references itself | Critical |
| Return tags | Every relationship is bidirectional | Critical |
| Canonical consistency | Every hreflang page canonicalizes to its same-language URL | Critical |
x-default |
Present, points to the unmatched-language fallback such as a language selector or default market page | Critical |
| Language codes | Valid Google-compatible hreflang tags: ISO 639-1 language plus optional ISO 15924 script or ISO 3166-1 Alpha-2 region | High |
| URL consistency | Same protocol, same trailing-slash convention | Medium |
| Completeness | Every language version represented | High |
If no hreflang files exist, report it as a critical gap and offer:
"Run /blog multilingual <topic> --languages ... to regenerate, or create
hreflang-tags.html manually."
If seo-hreflang from claude-seo is installed, suggest running it for
deeper validation.
Phase 6: Freshness Audit
For posts with translatedDate in frontmatter:
| Check | What | Severity |
|---|---|---|
| Source updated after translation | Source modified after translatedDate |
Critical |
| Source content drift | Stored source hash or source dateModified differs from current source |
Critical |
| Translation drift | Stored translation hash differs from current localized file | Medium |
| Translation older than 90 days | May need refresh | Medium |
lastUpdated mismatch across versions |
Versions out of sync | Medium |
Git or file mtime newer than translatedDate |
Content changed without frontmatter update | Warning |
When available, store and compare sourceHash, source dateModified,
translationHash, and Git mtime before relying only on translatedDate.
Emit actionable commands per stale file:
3 translations are stale:
- de/ki-trends-2026.md (source updated 2 days ago)
-> Run: /blog translate en/ai-trends-2026.md --to de
- fr/ki-trends-2026.md (source updated 2 days ago)
-> Run: /blog translate en/ai-trends-2026.md --to fr
- es/tendencias-ia-2026.md (translation > 90 days old)
-> Run: /blog translate en/ai-trends-2026.md --to es
Phase 7: Report
Output as markdown by default. If the user passes --html, also write the
report to locale-audit-report.html only inside the audited project root.
Escape all dynamic filenames, titles, URLs, and issue text with
html.escape(value, quote=True) before rendering HTML, and reject symlinked
or outside-root report paths.
## Multilingual content audit report
### Summary
- Posts audited: [N] across [N] languages
- Overall health: [score] / 100
- Critical issues: [N]
- Warnings: [N]
### Translation coverage
[Matrix from Phase 2]
### Issues found
#### Critical
- [Issue with file references]
#### Warnings
- [Issue with file references]
#### Passed
- [Checks that passed]
### Prioritized fixes
1. [Highest-impact action]
2. [...]
### Stale-translation alerts
[Runnable commands from Phase 6]
### Quick fixes
- Run `/blog translate <file> --to <missing-langs>` for [N] missing translations.
- Run `/blog multilingual` to regenerate hreflang assets.
- Run `/blog localize <file> --locale <code>` for weak cultural adaptations.
Error Handling
| Scenario | Action |
|---|---|
| Empty directory | "No blog posts found in [path]" |
| Only one language present | Report coverage, suggest target languages |
| No hreflang files | Flag as critical gap, offer regeneration |
| Unrecognized file format | Skip with a warning |
Cross-References
- Fill missing translations:
/blog translate <file> --to <missing-codes> - Deepen weak adaptations:
/blog localize <file> --locale <code> - Regenerate hreflang assets:
/blog multilingual <topic> --languages <codes>
| 1 | |
| 2 | name blog-locale-audit |
| 3 | description > |
| 4 | Audit a directory of multilingual blog content for completeness, consistency, |
| 5 | hreflang correctness, meta-tag parity, and freshness. Builds a translation |
| 6 | coverage matrix, flags stale translations, validates hreflang and schema, |
| 7 | and emits a prioritized report with runnable fix commands. |
| 8 | Use when user says "locale audit", "blog locale-audit", "check translations", |
| 9 | "multilingual audit", "translation check", "hreflang check", |
| 10 | "Uebersetzungen pruefen". |
| 11 | user-invokable true |
| 12 | argument-hint "<directory>" |
| 13 | license MIT |
| 14 | compatibility Standalone within claude-blog. Optional richer hreflang validation via claude-seo seo-hreflang. |
| 15 | metadata |
| 16 | author AgriciDaniel |
| 17 | version "2.2.0" |
| 18 | category blog |
| 19 | |
| 20 | |
| 21 | # Blog Locale Audit, Multilingual Quality Control |
| 22 | |
| 23 | Audits a directory of multilingual blog content to ensure every language |
| 24 | version is complete, consistent, correctly tagged, and SEO-optimized. |
| 25 | Catches international content issues before they hurt rankings. |
| 26 | |
| 27 | > Adapted from `claude-blog-multilingual` by Chris Mueller (Pro Hub Challenge, |
| 28 | > March 2026). Original: https://github.com/Chriss54/multilingual-int |
| 29 | |
| 30 | ## Workflow |
| 31 | |
| 32 | ### Phase 1: Discovery |
| 33 | |
| 34 | Resolve the target directory inside the project root/current working |
| 35 | directory. Reject symlinked directories and traversal outside the root, |
| 36 | then scan blog content and group posts by language using: |
| 37 | Subdirectory names (`en/`, `de/`, `fr/`). |
| 38 | Frontmatter `lang` and `translatedFrom` fields. |
| 39 | `hreflang-map.json` if present. |
| 40 | Normalize detected language codes with the shared multilingual locale rules |
| 41 | used by `blog-translate`, `blog-localize`, and `blog-multilingual`: ISO |
| 42 | 639-1 language in lowercase, optional ISO 15924 script in title case, |
| 43 | optional ISO 3166-1 Alpha-2 region in uppercase. Flag ambiguous |
| 44 | language-only codes such as `es`, `pt`, and `zh` unless the content |
| 45 | declares an explicit neutral mode. |
| 46 | Build a content matrix mapping which post exists in which languages. Use a |
| 47 | stable translation-group key before comparing slugs: `translationGroupId`, |
| 48 | `sourceSlug`, schema `translationOfWork.url`, or the IDs and URLs in |
| 49 | `hreflang-map.json`. Fall back to normalized source slug only when no |
| 50 | stable key exists. |
| 51 | Detect the source language (most common `translatedFrom` target, or the |
| 52 | `sourceLanguage` field in `hreflang-map.json` if present). |
| 53 | |
| 54 | ### Phase 2: Completeness Audit |
| 55 | |
| 56 | Show which translations are missing: |
| 57 | |
| 58 | |
| 59 | ### Translation coverage matrix |
| 60 | |
| 61 | | Post (EN) | DE | FR | ES | JA | |
| 62 | |-----------|----|----|----|----| |
| 63 | | how-to-avoid-ai-slop | ok | ok | missing | missing | |
| 64 | | content-marketing-2026 | ok | missing | ok | missing | |
| 65 | |
| 66 | Coverage: 60% (6 of 10 expected translations present) |
| 67 | Missing: 4 translations needed |
| 68 | |
| 69 | |
| 70 | ### Phase 3: Content Parity Audit |
| 71 | |
| 72 | For every post that exists in multiple languages: |
| 73 | |
| 74 | | Check | What | Severity | |
| 75 | |-------|------|----------| |
| 76 | | Section count | Same number of H2 and H3 sections | Critical | |
| 77 | | FAQ count | Same number of FAQ items | High | |
| 78 | | Image count | Same number of images | High | |
| 79 | | Chart count | Same number of charts (SVG figures) | High | |
| 80 | | Word count ratio | Within expected band for language pair (DE +20% to +30%, JA -20%, ES +10%) | Medium | |
| 81 | | Link count | Similar internal and external link counts | Medium | |
| 82 | | Evidence-backed claims | Same supported claims and citations across versions | Medium | |
| 83 | | Frontmatter parity | All required fields present per version | High | |
| 84 | |
| 85 | Flag every significant deviation as an issue. |
| 86 | |
| 87 | ### Phase 4: SEO Parity Audit |
| 88 | |
| 89 | For every language version verify: |
| 90 | |
| 91 | | Element | Check | Severity | |
| 92 | |---------|-------|----------| |
| 93 | | Title tag | Present, localized, clear, and appropriate for the page | Critical | |
| 94 | | Meta description | Present, localized, accurate, and consistent with visible content | Critical | |
| 95 | | `lang` attribute or frontmatter `lang` | Present, valid Google-compatible hreflang or BCP 47 language tag | Critical | |
| 96 | | Canonical URL | Points to the same-language page, not the source-language page or x-default | Critical | |
| 97 | | Schema `inLanguage` | Matches `lang` | High | |
| 98 | | Schema `translationOfWork` | Points to the source URL | High | |
| 99 | | Alt text | Translated (no English alt in non-EN posts) | High | |
| 100 | | Slug | Localized (no English slug in non-EN posts) | Medium | |
| 101 | | Tags | Localized | Medium | |
| 102 | | Keywords | Localized | Medium | |
| 103 | |
| 104 | ### Phase 5: Hreflang Audit |
| 105 | |
| 106 | If `hreflang-tags.html`, `hreflang-sitemap.xml`, or `hreflang-map.json` |
| 107 | exists in the directory: |
| 108 | |
| 109 | | Check | What | Severity | |
| 110 | |-------|------|----------| |
| 111 | | Self-referencing | Each page references itself | Critical | |
| 112 | | Return tags | Every relationship is bidirectional | Critical | |
| 113 | | Canonical consistency | Every hreflang page canonicalizes to its same-language URL | Critical | |
| 114 | | `x-default` | Present, points to the unmatched-language fallback such as a language selector or default market page | Critical | |
| 115 | | Language codes | Valid Google-compatible hreflang tags: ISO 639-1 language plus optional ISO 15924 script or ISO 3166-1 Alpha-2 region | High | |
| 116 | | URL consistency | Same protocol, same trailing-slash convention | Medium | |
| 117 | | Completeness | Every language version represented | High | |
| 118 | |
| 119 | If no hreflang files exist, report it as a critical gap and offer: |
| 120 | "Run `/blog multilingual <topic> --languages ...` to regenerate, or create |
| 121 | hreflang-tags.html manually." |
| 122 | |
| 123 | If `seo-hreflang` from claude-seo is installed, suggest running it for |
| 124 | deeper validation. |
| 125 | |
| 126 | ### Phase 6: Freshness Audit |
| 127 | |
| 128 | For posts with `translatedDate` in frontmatter: |
| 129 | |
| 130 | | Check | What | Severity | |
| 131 | |-------|------|----------| |
| 132 | | Source updated after translation | Source modified after `translatedDate` | Critical | |
| 133 | | Source content drift | Stored source hash or source `dateModified` differs from current source | Critical | |
| 134 | | Translation drift | Stored translation hash differs from current localized file | Medium | |
| 135 | | Translation older than 90 days | May need refresh | Medium | |
| 136 | | `lastUpdated` mismatch across versions | Versions out of sync | Medium | |
| 137 | | Git or file mtime newer than `translatedDate` | Content changed without frontmatter update | Warning | |
| 138 | |
| 139 | When available, store and compare `sourceHash`, source `dateModified`, |
| 140 | `translationHash`, and Git mtime before relying only on `translatedDate`. |
| 141 | |
| 142 | Emit actionable commands per stale file: |
| 143 | |
| 144 | |
| 145 | 3 translations are stale: |
| 146 | - de/ki-trends-2026.md (source updated 2 days ago) |
| 147 | -> Run: /blog translate en/ai-trends-2026.md --to de |
| 148 | - fr/ki-trends-2026.md (source updated 2 days ago) |
| 149 | -> Run: /blog translate en/ai-trends-2026.md --to fr |
| 150 | - es/tendencias-ia-2026.md (translation > 90 days old) |
| 151 | -> Run: /blog translate en/ai-trends-2026.md --to es |
| 152 | |
| 153 | |
| 154 | ### Phase 7: Report |
| 155 | |
| 156 | Output as markdown by default. If the user passes `--html`, also write the |
| 157 | report to `locale-audit-report.html` only inside the audited project root. |
| 158 | Escape all dynamic filenames, titles, URLs, and issue text with |
| 159 | `html.escape(value, quote=True)` before rendering HTML, and reject symlinked |
| 160 | or outside-root report paths. |
| 161 | |
| 162 | |
| 163 | ## Multilingual content audit report |
| 164 | |
| 165 | ### Summary |
| 166 | - Posts audited: [N] across [N] languages |
| 167 | - Overall health: [score] / 100 |
| 168 | - Critical issues: [N] |
| 169 | - Warnings: [N] |
| 170 | |
| 171 | ### Translation coverage |
| 172 | [Matrix from Phase 2] |
| 173 | |
| 174 | ### Issues found |
| 175 | #### Critical |
| 176 | - [Issue with file references] |
| 177 | |
| 178 | #### Warnings |
| 179 | - [Issue with file references] |
| 180 | |
| 181 | #### Passed |
| 182 | - [Checks that passed] |
| 183 | |
| 184 | ### Prioritized fixes |
| 185 | 1. [Highest-impact action] |
| 186 | 2. [...] |
| 187 | |
| 188 | ### Stale-translation alerts |
| 189 | [Runnable commands from Phase 6] |
| 190 | |
| 191 | ### Quick fixes |
| 192 | - Run `/blog translate <file> --to <missing-langs>` for [N] missing translations. |
| 193 | - Run `/blog multilingual` to regenerate hreflang assets. |
| 194 | - Run `/blog localize <file> --locale <code>` for weak cultural adaptations. |
| 195 | |
| 196 | |
| 197 | ## Error Handling |
| 198 | |
| 199 | | Scenario | Action | |
| 200 | |----------|--------| |
| 201 | | Empty directory | "No blog posts found in [path]" | |
| 202 | | Only one language present | Report coverage, suggest target languages | |
| 203 | | No hreflang files | Flag as critical gap, offer regeneration | |
| 204 | | Unrecognized file format | Skip with a warning | |
| 205 | |
| 206 | ## Cross-References |
| 207 | |
| 208 | Fill missing translations: `/blog translate <file> --to <missing-codes>` |
| 209 | Deepen weak adaptations: `/blog localize <file> --locale <code>` |
| 210 | Regenerate hreflang assets: `/blog multilingual <topic> --languages <codes>` |
| 211 |