Communitygithub.com

timerise-ai/blog-markdown

エージェントスキル:ファイルベースの多言語Markdownブログを構築 — ロケールごとに分割された記事、translationKeyで結合されたスラッグ、スラッグ単位のタグページ、関連記事、カバーアート、RSS、サイトマップ、hreflang、CIコンテンツバリデーター — Next.js App Router、CMS不要

blog-markdown とは?

このスキルは、Next.js App Routerを使用して、ファイルベースの多言語Markdownブログを構築するための包括的なガイドです。記事の識別子としてスラッグではなく`translationKey`を使用する設計思想に基づき、SEOに最適化されたロケール固有のスラッグを実現しつつ、hreflang、言語切り替え、関連記事、クロスロケールリダイレクトなどの機能を効率的に実装します。スキルは、コンテンツの読み込み(メモ化、YAMLパースエラーへのフォールバック処理)、タグページ、RSSフィード、サイトマップ、JSON-LD構造化データ、CIでのコンテンツバリデーションなど、実運用に必要な実装パターンを詳細なコードリファレンスとともに提供します。特に、YAMLパース時のグレイマター(gray-matter)のキャッシュ問題や、ファイル読み込みのパフォーマンス最適化など、実践的な知見が豊富に含まれています。

対応~Claude Code~Codex CLI~Cursor
npx skills add timerise-ai/blog-markdown

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Markdownブログ(多言語、タグ付き)

コンテンツはファイルであり、データベースの行ではありません。全体の設計は一つのアイデアに基づいています。 記事の識別子はtranslationKeyであり、スラッグではありません。 スラッグはSEOのためにローカライズされ、言語ごとに異なります。キーが、3つのファイルを1つの記事にし、hreflang、言語スイッチャー、関連記事、クロスロケールリダイレクトを可能にします。このフィールドを正しく設定すれば、残りはすべてルックアップです。

このモジュールを出荷したエンジニアによって執筆されました。監査対象となった以前の実装は、多言語で静的に生成されたマーケティングブログであり、サイトマップとSEOサーフェスを備えていました。以下の事実とルールは、コンテンツレイヤースイートとビルドが検証するものです。references/provenance.mdに記録があります。

使用する場合

  • コンテンツがリポジトリ内のMarkdownとして作成され、アプリとともにデプロイされる
  • 2つ以上の言語があり、言語ごとに異なるスラッグを使用する
  • タグページ、関連記事、フィード、またはhreflangがスコープ内にある
  • 全体をビルド時に静的に生成したい

使用しない場合

代わりにこれ代わりにこれを使用
ホスト型CMSが信頼できる情報源であるそのCMSのSDKを使用。references/content-model.mdのみを保持
ドキュメントサイト/ヘルプセンター(カテゴリ、サイドバー、検索)ヘルプセンタースキル — 異なるナビゲーションモデル
10記事未満の単一言語ブログインラインでfs + gray-matterを使用。これではオーバーヘッドが大きい
ユーザーが送信したMarkdownのレンダリングサニタイズレンダラーを使用。references/rendering.mdのXSSに関する注意を参照

アーキテクチャ

content/blog/<locale>/<localized-slug>.md   # フロントマター + 本文
        |
        v
 parseFrontmatter  --YAML、不正な引用符がある場合はラインパーサーにフォールバック
        |
        v
   getAllPosts(locale)  <-- 本番環境ではロケールごとにメモ化
        |
        +--> translationKey ----> 代替URL / hreflang / 言語切り替え
        +--> tags -> タグのSLUG ---> タグページ(識別子はラベルではなくスラッグ)
        +--> related (EN slugs) -> 関連記事、ENフォールバック付き
        |
        v
  /blog   /blog/[slug]   /blog/tag/[slug]   sitemap.xml   feed.xml

重要な事実

  1. 関連記事はデフォルトロケールで一度だけ、デフォルトロケールのスラッグで作成されます。 翻訳ファイルはrelatedを完全に省略しても、translationKeyを通じて解決された正しい関連記事を取得できるため、すべての翻訳で同じ関連セクションがレンダリングされます。フォールバックテストがこれを保証します。
  2. タグの識別子はラベルではなく、スラッグです。 "AI Agents"と"AI agents"は同じURLにスラッグ化されます。スラッグでグループ化し、スラッグで投稿をマッチングするため、すべてのスペルバリアントの投稿が1つのタグページに表示されます。2つのテストがこれを保証します。
  3. gray-matterは、引用符文字を含む二重引用符で囲まれたYAMLスカラーでエラーをスローします — 翻訳された抜粋に含まれるタイポグラフィ引用符が原因です。フォールバックパーサーがないと、ビルドがその1つのファイルで停止します。行単位のフォールバックにより読み込みが継続されます。ライブラリに対して検証済みであり、テストでカバーされています。
  4. gray-matterは、失敗したパースを空の結果としてキャッシュします。 パース前にキャッシュエントリを書き込むため、エラーをスローしたファイルの2回目以降のパースはすべて{}を返します — エラーもフロントマターもありません。常にmatter(contents, {})を呼び出してください。空のオブジェクトを渡すと、キャッシュが無効になります。
  5. ページごとに、ロケールごとにO(投稿数 x ロケール数)で読み込まれます。メモ化しない場合。 ロケールごとにメモ化することで、各コンテンツファイルがビルドごとに1回だけ読み込まれます。generateMetadataで代替URLを解決することは、以前の実装で測定された71倍のファイル読み込み増幅となります。
  6. getPostSlugsは.mdをフィルタリングする必要があります。 このフィルタリングにより、.DS_Storeが.DS_Store.mdとして読み込まれ、ENOENTエラーになるのを防ぎます。テストがこれを保証します。
  7. タグページにはクロスロケールの識別子がありません。 タグはロケールごとの自由テキストであるため、言語スイッチャーはタグURLで404になるURLを構築するのではなく、ブログインデックスにフォールバックする必要があります。

厳格なルール

クロスロケールのルックアップをスラッグで行わないでください。 スラッグは意図的にローカライズされています。 translationKeyが結合列であり、ロケール間でのスラッグの一致は偶然であり、関係ではありません。

getPostBySlugで暗黙的に下書きの可視性を決定しないでください。 ローダーで下書きをフィルタリングしてください。dynamicParams = falseは副作用としてのみそれらを非表示にし、フラグが変更されるとすぐにその効果がなくなります。下書きテストがフィルタを保持します。

「5分で読める」などの定数をレンダリングしないでください。 本文から計算してください。split1回で済み、読了時間テストがそれを保持します。

読み込み時にラベルからタグページのURLを導出し、レンダリング時にURLからラベルを導出する場合、衝突チェックなしに行わないでください。 そのラウンドトリップは、すべてのラベルが一意にスラッグ化されている場合にのみ可逆的です。

クイックスタート

  1. ロケール契約とフロントマタースキーマを設定 — content-model.md
  2. ローダーを構築(メモ化、.mdフィルタリング、フォールバックパーサー) — content-loader.md
  3. ロケール、代替URL、クロスロケールリダイレクトを配線 — i18n-and-routing.md
  4. タググループ化とタグページを追加 — tags.md
  5. 3つのルート、メタデータ、JSON-LDを構築 — pages-and-seo.md
  6. 本文とカバーをレンダリング — rendering.md
  7. バリデーションスクリプトを追加し、運用上のギャップを読む — operations.md
  8. フィクスチャとテストを実行 — testing.md

既存のアプリに移植しますか?最初にadaptation.mdのシームテーブルを埋めてください — 10分で完了し、リネーム作業を節約できます。

リファレンスディレクトリ

シナリオトリガーキーワードリファレンス
フロントマターフィールド、下書き、翻訳キー、関連記事frontmatter, schema, translationKey, draft, related, excerptcontent-model.md
ファイル読み込み、キャッシュ、YAMLエラー、読了時間gray-matter, fs, cache, memoize, ENOENT, YAMLException, parsecontent-loader.md
ロケール、hreflang、代替URL、誤ったロケールへのリンクi18n, locale, hreflang, alternates, canonical, language switcher, redirecti18n-and-routing.md
タグURL、衝突、薄いタグページtag, slug, diacritics, collision, tag page, taxonomytags.md
ルート、メタデータ、JSON-LD、サイトマップ、RSSpage.tsx, generateStaticParams, generateMetadata, dynamicParams, sitemap, RSS, feed, BlogPostingpages-and-seo.md
MarkdownからReactへ、コードブロック、カバー画像react-markdown, remark-gfm, syntax highlighting, cover, motif, accent, XSSrendering.md
コンテンツ検証、ビルドコスト、エディタワークフローvalidation, CI check, build time, drafts, authoring, operatoroperations.md
フィクスチャと合格テストスイートtest, fixture, vitest, node:test, regression, asserttesting.md
ホストアプリへの適合:シーム、リネーム、プローブadapt, port, host, seam, rename, CMS, integrateadaptation.md
監査台帳:以前の実装と比較して何が変更され、保持され、追加されたかprovenance, deviations, fidelity, audit, ledgerprovenance.md

関連スキル