@streamdown/cjk
Improved handling of Chinese, Japanese, and Korean text.
The @streamdown/cjk plugin improves handling of CJK (Chinese, Japanese, Korean) text with proper emphasis formatting and autolink handling. This is particularly important for AI-generated content, where language models naturally place emphasis markers around phrases that include or end with punctuation.
- Correct emphasis formatting near ideographic punctuation (bold, italic, strikethrough)
- Splits autolinks at CJK punctuation boundaries to prevent URLs from swallowing trailing punctuation
- Uses
remark-cjk-friendlyandremark-cjk-friendly-gfm-strikethroughfor proper parsing
Install
npm install @streamdown/cjkUsage
import { cjk } from '@streamdown/cjk';
<Streamdown plugins={{ cjk }}>
{markdown}
</Streamdown>For advanced configuration, use createCjkPlugin:
import { Streamdown } from "streamdown";
import { createCjkPlugin } from "@streamdown/cjk";
const cjk = createCjkPlugin();
export default function Page() {
return (
<Streamdown plugins={{ cjk }}>
{markdown}
</Streamdown>
);
}The Problem
The CommonMark/GFM specification has a limitation where emphasis markers (** or *) adjacent to ideographic punctuation marks occasionally fail to be recognized. This causes formatting to break in CJK text:
**この文は太字になりません(This won't be bolded)。**この文のせいで(It is due to this sentence)。Without CJK-friendly parsing, the text above would render as plain text instead of bold because the closing ** appears next to the Japanese period.
Supported Features
Bold Text with Punctuation
Works correctly with all ideographic punctuation marks:
**日本語の文章(括弧付き)。**この文が後に続いても大丈夫です。
**中文文本(带括号)。**这句子继续也没问题。
**한국어 구문(괄호 포함)**을 강조.Japanese: 日本語の文章(括弧付き)。この文が後に続いても大丈夫です。
Chinese: 中文文本(带括号)。这句子继续也没问题。
Korean: 한국어 구문(괄호 포함)을 강조.
Italic Text with Punctuation
*これは斜体のテキストです(括弧付き)。*この文が後に続いても大丈夫です。
*这是斜体文字(带括号)。*这句子继续也没问题。
*이 텍스트(괄호 포함)*는 기울임꼴입니다.Japanese: これは斜体のテキストです(括弧付き)。この文が後に続いても大丈夫です。
Chinese: 这是斜体文字(带括号)。这句子继续也没问题。
Korean: 이 텍스트(괄호 포함)는 기울임꼴입니다.
Strikethrough with Punctuation
Streamdown includes remark-cjk-friendly-gfm-strikethrough for proper strikethrough support:
~~削除されたテキスト(括弧付き)。~~この文は正しいです。
~~删除的文字(带括号)。~~这个句子是正确的。
~~이 텍스트(괄호 포함)~~를 삭제합니다.Japanese: 削除されたテキスト(括弧付き)。この文は正しいです。
Chinese: 删除的文字(带括号)。这个句子是正确的。
Korean: 이 텍스트(괄호 포함)를 삭제합니다。
Mixed Content
CJK and English text work seamlessly together:
**重要提示(Important Notice):**请注意。Result: 重要提示(Important Notice):请注意。
Supported Punctuation
The plugin handles all common ideographic punctuation marks:
- Parentheses:
() - Brackets:
【】「」〈〉 - Periods:
。. - Commas:
,、 - Questions:
? - Exclamations:
! - Colons:
:
Why This Matters for AI
Language models generate markdown naturally, often placing emphasis markers around phrases that include punctuation. Without CJK-friendly parsing, AI-generated content in Chinese, Japanese, or Korean would have broken formatting.
- ❌ Without CJK support:
- The model writes:
**この用語(読み方など)**について説明します。- The user sees: **この用語(読み方など)**について説明します。 (not bold!)
- The model writes:
- ✅ With CJK support:
- The model writes:
**この用語(読み方など)**について説明します。- The user sees: この用語(読み方など)について説明します。 (properly bolded!)
- The model writes:
Autolink Boundary Handling
The CJK plugin also prevents autolinks from swallowing trailing CJK punctuation. When a URL ends with CJK punctuation characters, the plugin splits the link so the punctuation appears as regular text.
Example:
Check out https://example.com。这是一个链接。Without CJK support, the trailing 。 would be included in the URL. With the plugin, the link ends at https://example.com and the period is rendered as text.
Supported boundary characters:
。.,、?!:;()【】「」『』〈〉《》
Plugin API
The CJK plugin provides remark plugins in a specific order for proper integration:
interface CjkPlugin {
// Plugins that run BEFORE remarkGfm (e.g., remark-cjk-friendly)
remarkPluginsBefore: Pluggable[];
// Plugins that run AFTER remarkGfm (e.g., autolink boundary, strikethrough)
remarkPluginsAfter: Pluggable[];
// @deprecated - Use remarkPluginsBefore and remarkPluginsAfter instead
remarkPlugins: Pluggable[];
}Streamdown automatically handles the plugin ordering. If integrating manually, ensure:
remarkPluginsBeforeruns beforeremarkGfm(modifies emphasis handling)remarkPluginsAfterruns afterremarkGfm(enhances autolinks and strikethrough)