中文文字工作者應該都聽過這個排版準則:
中英文交界處必須增加一個空格,避免視覺上過於壅擠[1]。
在部分的軟體或系統中,會自動墊上這樣的空間(包含你現在正看的這篇文章,以及諸如蘋果與Google的作業系統[2][3]);然而,並不是所有的讀寫介面都內建有這樣的功能。根據筆者實測,在2020年3月,Facebook的iPhone應用會自動墊上這個空間;但是在Web中則沒有——前者亦不確定是來自iPhone的作業系統,抑或是APP本身的功能。
圖片:上圖中中英文沒有空間,顯得擁擠;下圖中則添加了半行空格。你覺得有差嗎⋯⋯可以確定的是,有人覺得有差。
所以⋯⋯我們要手動補上空白嗎
儘管這個空白是很常見的需求。然而,就筆者的觀點來看。在文本原始檔中「手動」添加空格,並不是一個理想的做法。其原因如下:
- 非常容易疏漏。
- 不同字體可能有不同適合的空間。
- 在文本的原始碼中加入非必要的空格會讓管理複雜化,增加修訂、編輯與校對的負擔。
- 以上,如果文本需要長期維護與修改,則花費的精力與效果不成比例。
讓程式來做吧!
但如果我們還是希望達成最舒適的排版,要怎麼做呢?
個人覺得最好的作法就是在閱讀的輸出端,利用程式自動墊上這個空間。
以本篇部落格為例,當訪客在讀取頁面時,筆者啟動了一段JavaScript,會將頁面中部分區塊的內容進行自動化處理。也因此,部落格中所有的文章,中英文間都會自動墊上一個半形空格。
該功能的實作如下:
function getPaddedCnEnString(htmlStr, padding) { // ASCII 33~126 const EN_ALPHA = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; const EN_SET = new Set(EN_ALPHA.split("")); const CN_MARKS = "。,、;:「」『』()—-?!——⋯⋯〈〉・《》_~"; const CN_MARKS_SET = new Set(CN_MARKS.split("")); const CN_RANGE_MIN = 0x3400; const CN_RANGE_MAX = 0x9FFC; // utility function const isCn = (char) => ((char.charCodeAt(0) >= CN_RANGE_MIN && char.charCodeAt(0) <= CN_RANGE_MAX) || CN_MARKS_SET.has(char)); const isEn = (char) => EN_SET.has(char); // process let result = ""; let thisIdx = 0; while (thisIdx <= htmlStr.length) { const thisChar = htmlStr.charAt(thisIdx); let nextIdx = thisIdx + 1; let nextChar = htmlStr.charAt(nextIdx); // copy contents in html tags (do no processing) if (thisChar === "<") { while (nextIdx <= htmlStr.length && htmlStr[nextIdx - 1] !== ">") { nextIdx++; } result += htmlStr.substring(thisIdx, nextIdx); thisIdx = nextIdx; continue } else if (nextChar === "<") { while (nextIdx <= htmlStr.length && htmlStr[nextIdx - 1] !== ">") { nextIdx++; } } result += htmlStr.substring(thisIdx, nextIdx); nextChar = htmlStr.charAt(nextIdx); // compare with next char and pad space if necessary if (isCn(thisChar) && isEn(nextChar)) { // 中文_abc result += padding; } else if (isEn(thisChar) && isCn(nextChar)) { // abc_中文 result += padding; } // continue thisIdx = nextIdx; } return result; }
- 函式的輸入為:
htmlStr
:字串,可包含HTML標籤(HTML標籤中的屬性不會被處理)padding
:你可以使用半形空白" "
或是例如<span>
標籤,再透過CSS來調整)
- 函式使用Unicode判斷字元屬於中文還是英文,並將台灣官方的標準標點符號視做中文[4]。以此為判斷,自動在中英文間加上空間。
舉例來說,如果你的文章位於<div id="post">
的標籤裡,那麼你可以這麼做[5]
$(document.onload).ready(function () { const element = $("#post"); const newHTML = getPaddedCnEnString(element.html()," "); element.html(newHTML); })
根據筆者簡單的測試,一份原始碼長5000字左右的markdown文章,以其HTML格式進行轉換,可以控制在5毫秒以內;且不妨礙原本內容的讀取;顯然不會造成太大的效能負擔。
以上,獻給排版的偏執狂們。
註解
[1] 儘管就筆者所知,這樣的準則並沒有被明文記載在各大軟體廠的設計文件裡。此外,筆者也反對將這樣的標準用於判斷寫作者是否專業;亦不建議用手動處理這個程序:"The space bar is not a design tool"。
[2] 出處:知乎|https://www.zhihu.com/question/19587406
[3] 非正規的指南例如:Tunghsiao Liu|中文文案排版指北|GitHub。這份文件在2020年3月時累計了7500個GitHub星星。
[4] 在Liu的指南中主張中文標點與英文間不需額外墊上空格。這其實很主觀,但我們的函示仍然提供了設定的參數。
[5] 文中預設使用jQuery在前端執行轉換。如果你可以控制後端的原始碼,也可以將轉換放在伺服器端進行。另外,如果放在前端且你有使用其他JS工具去處理HTML(比如說程式碼標註工具),那麼你可能需要注意執行的先後順序;或是採用適當的CSS Selector去跳過不需要處理的區塊。