中文在地化

字數統計文字區

中英混排字數統計 React textarea 元件:字數/字元/位元組三種計法即時切換、上限進度環、接近與超限警示,IME 組字中凍結計數不誤計,可選硬性上限。

中文的「字數」和英文的 word count 不是同一回事:漢字每個字算一字,英文則是連續字母整段算一個 word;送進資料庫或舊系統時,又常以位元組計。這個 textarea 把三種計法放在右下角的切換籤裡,即時顯示「目前數字 / 上限」與一個 SVG 進度環——超過 warnAt 比例轉琥珀色、超限轉紅並抖動一下。注音、假名等輸入法組字期間計數會凍結,不會把組字字母算進去;開啟 hardLimit 時超限即阻止輸入,但會等組字結束才裁切,不會把正在組的字剪成半個。文字值與計數模式各自支援受控/非受控雙模式。

把這個效果裝進你的專案

在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。

npx webberui@latest add cjk-word-count-textarea

還沒有專案,或安裝遇到問題?看快速開始 →

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

60
0.8
編輯中的呼叫預設屬性
<CjkWordCountTextarea/>

完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。

安裝

npx webberui@latest add https://webberui.com/r/cjk-word-count-textarea.json

或在 components.json 設定 registries 後,改用 @webberui/cjk-word-count-textarea 安裝。

使用

import { CjkWordCountTextarea } from "@/components/ui/cjk-word-count-textarea";

<CjkWordCountTextarea
  label="貼文內容"
  maxCount={140}
  defaultMode="words"
  autoResize
  onChange={(text, counts) => {
    // counts 同時帶有三種計法的數字
    console.log(counts.words, counts.chars, counts.bytes);
  }}
/>

Props

Prop型別預設值說明
valuestring—受控文字值;未提供時為非受控模式
defaultValuestring""非受控模式的初始文字
onChange(text: string, counts: CjkCounts) => void—文字變動時回呼,第二參數為三種計法的即時計數
maxCountnumber—計數上限(依目前模式的數字);未提供時僅顯示計數、不畫進度環
mode"words" | "chars" | "bytes"—受控的計數模式,與 onModeChange 搭配使用
defaultMode"words" | "chars" | "bytes""words"非受控模式的初始計數模式
onModeChange(mode: CjkCountMode) => void—計數模式切換時回呼
warnAtnumber0.8進入琥珀色警示狀態的比例門檻(0–1)
rowsnumber4textarea 顯示行數
autoResizebooleanfalse高度是否隨內容自動調整(以 scrollHeight 計算)
labelstring—欄位標籤;未提供時以 placeholder 作為無障礙名稱
placeholderstring"輸入內容⋯"輸入框佔位提示文字
hardLimitbooleanfalse超過上限即阻止輸入;IME 組字中不截斷,組字結束才裁切
countOptionsCountCjkOptions—傳給 countCjk 的計數選項(全形標點、Big5 折算)
labelsCjkWordCountLabels—介面文字覆寫(模式籤與警示宣告)
classNamestring—透傳到最外層容器並以 cn() 併入

CjkCounts

欄位型別預設值說明
wordsnumber—中文式字數:漢字逐字計,拉丁序列以空白斷詞
charsnumber—字元數:以 Unicode code point 計
bytesnumber—位元組數:UTF-8 長度或依 bytesPerCjk 折算

CountCjkOptions

欄位型別預設值說明
countPunctuationbooleanfalsewords 模式是否把全形標點(,。!?…)也各計一字
bytesPerCjknumber—bytes 模式下每個 CJK 字元(含全形標點)固定折算的位元組數;未提供時依 UTF-8 實際長度

CjkWordCountLabels

欄位型別預設值說明
wordsstring"字數"words 模式籤文字
charsstring"字元"chars 模式籤文字
bytesstring"位元組"bytes 模式籤文字
modeGroupstring"計數模式"模式切換群組的無障礙名稱
nearLimitstring"接近上限"跨越 warnAt 門檻時的螢幕閱讀器宣告前綴
overLimitstring"已超過上限"超過上限時的螢幕閱讀器宣告前綴

另外具名匯出 countCjk(text, mode, opts) 與 clampCjk(text, max, mode, opts),方便在表單驗證或伺服器端用同一套規則計數與裁切;兩者都是純函式,不依賴瀏覽器 API。

細節

  • words 計法:漢字(含日文假名、韓文字)每個字元算一字;英文字母、數字等連續序列以空白或 CJK 字元斷詞,整段算一個 word;全形標點預設不計,countPunctuation: true 時逐個計入。例如「我買了 3 本 React 書」算 7。
  • chars 計法以 Unicode code point 計(Array.from),emoji 等 surrogate pair 不會被拆成兩個字元。
  • bytes 計法預設是 UTF-8 長度(漢字多為 3 bytes);台灣不少舊系統以 Big5 計(漢字 2 bytes),傳 countOptions={{ bytesPerCjk: 2 }} 即可折算。
  • 硬性上限用二分搜尋找出最長合法前綴:計數對前綴長度單調遞增,因此不必逐字重算。
  • autoResize 先把高度歸零再讀 scrollHeight,內容變少時高度也縮得回來。

可及性

  • label 以 htmlFor 關聯至 textarea;未提供 label 時以 placeholder 作為 aria-label
  • 計數文字透過 aria-describedby 關聯至 textarea;超限時設定 aria-invalid
  • 模式切換是 role="radiogroup",每個籤為 role="radio" 並帶 aria-checked,採 roving tabindex,方向鍵可循環切換,具 focus-visible 外框
  • 警示宣告位於 role="status"、aria-live="polite" 區域,只在跨越 warnAt 或超限的瞬間宣告一次,避免每個按鍵都播報;進度環與警示圖示對輔助科技隱藏
  • 使用者系統開啟「減少動態效果」時,停用超限抖動與模式籤滑動動畫,狀態直接切換