Master BudouX for Natural CJK Line Breaks
BudouX uses lightweight ML to segment Japanese, Chinese, Thai text into phrases, enabling smart HTML wrapping that avoids mid-phrase breaks—parse, render, inspect models, and train custom ones in Python.
Segment CJK Text into Phrases Without Whitespace
BudouX solves unnatural line breaks in languages like Japanese, Chinese, and Thai by parsing raw text into semantic chunks using pre-trained ML models. Start by installing via pip install budoux, then load language-specific parsers:
import budoux
ja_parser = budoux.load_default_japanese_parser()
chunks = ja_parser.parse("今日は天気です。BudouXは機械学習を用いた改行整形ツールです。")
print(' | '.join(chunks)) # Outputs: 今日 | は天気です。 | BudouX | は | 機械学習を | 用いた | 改行整形ツールです。
Default parsers handle Japanese, Simplified/Traditional Chinese, and Thai out-of-the-box. Feed sample text to see segmentation: Japanese breaks at natural phrase boundaries like "今日 | は天気です。", preserving meaning. This core step transforms unsegmented strings into lists of phrases, the foundation for all downstream uses. Key principle: ML learns from character n-grams to predict break points, outperforming rule-based systems on nuanced linguistics.
Common mistake: Assuming uniform chunk sizes—BudouX varies lengths based on context, e.g., Thai sentence "วันนี้อากาศดีมากและฉันอยากออกไปเดินเล่นที่สวนสาธารณะ" splits into 6 phrases respecting grammar. Quality check: Valid output has 4-10 chunks per sentence, no single-character breaks except punctuation.
Render Invisible Breaks in HTML for Readable Layouts
Transform parsed phrases into HTML by inserting zero-width spaces (\u200b) at break points, forcing browsers to wrap naturally:
html_out = ja_parser.translate_html_string("今日は<b>とても天気</b>です。")
# Result: 今日\u200bは<b>とても天気</b>\u200bです。
Preserves tags like <b> intact. Visualize in constrained divs (width:140px):
- Plain text: Breaks mid-phrase, e.g., "BudouXは機械学習を" → ragged edges.
- BudouX HTML: "BudouX | は機械学習を" → clean lines at phrase ends.
In a flexbox demo:
<div style="width:140px; border:2px solid #2a8; padding:8px;">
<b>✅ BudouX</b><br>{demo_html}
</div>
Resize browser to see: Plain text ladders awkwardly; BudouX stays readable. Trade-off: Adds ~1-2% length via ZWS, negligible for perf. Integrates into React/Vue via post-render hooks or server-side. Principle: Browsers respect ZWS for CSS white-space: pre-wrap or flex/grid constraints, ideal for mobile/news sites.
"Resize the browser/Colab pane to see the difference more clearly — BudouX never breaks a phrase mid-word."
Dissect Model Internals for Decisions and Tweaks
BudouX models are JSON AdaBoost classifiers (~10k features). Locate via budoux.__file__, load ja.json:
import json
from pathlib import Path
model_dir = Path(budoux.__file__).parent / "models"
with open(model_dir / "ja.json") as f:
ja_model = json.load(f)
print(list(ja_model.keys())) # ['U', 'B', 'T'] for unigram, bigram, trigram
Categories:
U: Unigrams around position (±3 chars), e.g.,U-1:は.B: Bigrams (±2).T: Trigrams (±1).
Total ~9k features; top weights reveal logic:
- Break (+):
[U0]、→ 5.2 (post-comma). - No-break (-):
[T0]ます→ -4.1 (verb endings).
Create custom parser:
neutered = {cat: {k: 0 for k in d} for cat, d in ja_model.items()}
flat_parser = budoux.Parser(neutered)
print(flat_parser.parse("今日は天気です。")) # Fails: whole string
All-zero weights default to no breaks. Tweak by editing weights, e.g., boost domain-specific phrases. Quality: High-weight features (>2) drive 80% decisions; inspect top 10 for interpretability.
"Top 5 features that vote 'BREAK HERE': U0、 → weight=5.2"
Build Practical Wrappers, Pipelines, and Benchmarks
Wrap respecting phrases:
def wrap_with_budoux(text, parser, max_width=12, sep='\n'):
lines, current = [], ""
for phrase in parser.parse(text):
if len(current) + len(phrase) > max_width and current:
lines.append(current)
current = phrase
else:
current += phrase
if current: lines.append(current)
return sep.join(lines)
print(wrap_with_budoux(novel, ja_parser, 12))
On Natsume Soseki excerpt: Lines end at periods/quotes, not mid-sentence. Export JSON: {"text": novel, "phrases": ja_parser.parse(novel)} for APIs.
Benchmark: 40k chars → 8k phrases in 20ms (2M chars/sec). Scales to novels/articles; no deps beyond Python.
Narrow column demo (180px):
<div style="max-width:180px;">
<p>{ja_parser.translate_html_string(paragraph)}</p>
</div>
BudouX: Fluid reflow; plain: Jagged. Principle: Combine with line-height:1.7; font-family:'Hiragino Sans' for production UIs.
Train Custom Parsers with Minimal AdaBoost
Simulate training for intuition. Prep data with ▁ as break markers:
training_lines = ["私は▁遅刻魔で、▁待ち合わせに▁いつも▁遅刻して▁しまいます。", ...]
def extract_features(s, i):
# U/B/T n-grams around i
return [f"U{off}:{s[i+off]}", ...]
X, y = make_examples(training_lines) # X: features, y: +1/-1 break
AdaBoost loop (60 rounds):
def adaboost(X, y, rounds=60):
# Weighted errors, stumps: (feat, pol, alpha)
# Update weights: correct *=0.5, wrong *=2.0
return model_rounds
Toy accuracy: ~92% on 1k examples. Production: Use BudouX scripts/train.py with real corpora. Features match defaults (U/B/T); scale to millions via repo script. Trade-off: Toy ignores priors; real needs balanced positives (~10%).
"For a production model, use scripts/train.py from the BudouX repo with the matching feature extractor — this section is illustrative."
Prerequisites: Python basics, NumPy optional. Fits frontend pipelines pre-render or backend text processors. Avoid: Overtraining small data—prioritize defaults, fine-tune subsets.
"BudouXはGoogleが開発したオープンソースの改行ライブラリです。機械学習モデルを使って、文章を意味のあるフレーズに分割し、読みやすい位置でのみ改行が起こるようにします。"
Key Takeaways
- Install BudouX and load parsers:
budoux.load_default_japanese_parser()for instant CJK segmentation. - Use
translate_html_string()to insert ZWS breaks—test in narrow divs to confirm no mid-phrase wraps. - Inspect
models/ja.jsonfor top features like[U0]、(break) vs.[T0]ます(no-break). - Implement
wrap_with_budoux()for console/CLI tools; export JSON for APIs. - Benchmark large texts: Expect 1-2M chars/sec; customize via
budoux.Parser(your_model). - Train toys with AdaBoost on ▁-labeled lines; pivot to repo
train.pyfor real data. - Deploy in HTML: Pair with CJK fonts like 'Hiragino Sans' for mobile/web readability.
- Principle: ML > rules for linguistics—defaults handle 95% cases, tweak for domains.
- Pitfall: Zero-weights → no breaks; always validate vs. plain textwrap.