zfb
GitHub リポジトリ

検索したい単語を入力

いつでも検索バーを開ける

CJK フレンドリーな強調

CJK の表意文字やかなに隣接する太字・斜体マーカーが正しく解釈されるようにする。

CjkFriendlyPlugin はデフォルトで有効です。パース済みの Markdown AST を後処理し、CJK コンテンツに対して強調のフランキング規則を再トークン化します。同じ cjkFriendly ゲートは、GFM の bare URL autolink が隣接する CJK テキストを飲み込むことを防ぐ CjkAutolinkBoundaryPlugin も制御します。

なぜこれが必要か

CommonMark の強調フランキング規則は、CJK の表意文字やかなを 「非空白かつ非句読点」として扱います。これにより、CJK テキストに隣接する ** — たとえば **テスト。**テスト — は基本パーサーでは右フランキングとみなされず、 <strong> ではなくそのままのアスタリスクとして描画されてしまいます。

CjkFriendlyPlugin はパース済みの Markdown AST を後処理し、これらのケースを 再トークン化します。その結果は、日本語・中国語・韓国語のコンテンツサイトにおける 直感的な期待と一致します。

GFM autolink literal には関連する境界問題があります。詳細はhttps://example.com参照 のように CJK テキストに密着して URL を書くと、末尾の CJK の並びがリンクの一部として扱われることがあります。cjkFriendly が有効で、gfm.autolinkLiteral が on のとき、CjkAutolinkBoundaryPlugin は最初に隣接する CJK 文字の位置で autolink を分割します。

挙動

デフォルト: 常に有効。設定キーは不要です。

オプトアウト(まれ): 厳密な CommonMark / GFM 出力が必要で、かつコンテンツに CJK の強調や bare URL の境界ケースが含まれない場合に限り、zfb.config.tscjkFriendly: false を 設定します:

zfb.config.ts
import { defineConfig } from "zfb/config";

export default defineConfig({
  markdown: {
    cjkFriendly: false,
  },
});

スコープ

このオプションは次を処理します:

  • CJK 文字に隣接する **bold***italic*

  • gfm.autolinkLiteral が有効なときの、CJK 文字に隣接する GFM bare URL autolink 境界。

  • セカンダリパースサイトを経由するコンテンツ — :::include で取り込まれたファイルや、 ディレクティブレジストリによって再パースされるディレクティブ本体 — も、この設定に 従うようになりました(#2398)。

リテラルの *** の修復は、対応するパース済みインライン兄弟ノードをまたぐことができ、それらのノードはそのまま保持されます。たとえば:

知られる**[LA-2A](https://example.com)光学式コンプレッサー**と、**Neve 1073 EQ**という

最初の strong 範囲には保持された LA-2A リンクと 光学式コンプレッサー が含まれ、既存の続く strong 範囲には Neve 1073 EQ が含まれます。未対応または不透明なインラインコンテンツは境界のままです。

GFM の打ち消し線(~~foo~~)にはこのトグルは不要です — markdown-rs の GFM トークナイザーが ~~ のデリミタ連続を独立して処理するため、両方のモードで CJK の境界においても打ち消し線は正しく機能します。

HTML レンダリングパスにおけるディレクティブ本体の強調

cjkFriendly はマーカーを正しく再トークン化しますが、HTML レンダリングパス@takazudo/zfb-md-wasmrenderHtml — を通るコンテナ ディレクティブの本体は、インラインの書式を意図的に落とす lossy な文字列化処理で JSX 本体が再構築されます。つまり次のソースは:

:::note
これは**重要。**テスト
:::

このように描画されます:

<Note>これは重要。テスト</Note>

コンテンツは失われません — 本体テキストの文字はすべて残り、落ちるのは <strong> のラッパーだけです。同じマーカーを通常の本文に書いた場合は <p>これは<strong>重要。</strong>テスト</p> になります。

これは cjkFriendly に固有の挙動ではなく、collapsed(空行なし)のディレクティブ 本体に固有の挙動でもありません。cjkFriendly が off の ASCII でも同じで、 plain **bold** text:::note で囲むと <Note>plain bold text</Note> に なりますし、本体の前後に空行を入れても変わりません。cjkFriendly が決めるのは ** が強調としてトークン化されるかどうかだけで、その後それを平坦化するのは HTML パスの文字列化処理です。cjkFriendly: false ではそもそもトークン化されない ため、** はリテラルのまま残ります。

ページのビルドには影響しません。 zfb build はコンテンツを MDX/JSX の出力 パスでコンパイルし、そちらは本体を再帰的に描画するため、同じソースから <_components.strong>重要。</_components.strong> がネストして出力されます。

書式だけが失われるのは意図的な挙動です。一方このパスでのコンテンツの削除は バグとして扱われます。hardBreaks が有効なとき、ディレクティブ 本体内のハードブレークが完全に消えて前後の語が繋がってしまう問題がありました。 これは修正済みで(zfb#2401)、現在は <br /> として描画されます。

関連項目

Revision History

作成更新