category: html

網頁處理 RTL(Right To Left) 右到左的文字排版

on 2022-04-15

前言

在 web 的世界中有一個需要處理的是文字排版的呈現

因為從電腦到整個計算機科學的領域都是在拉丁語系為主宗的國家發展的

所以在介面的文字排版上自然以從左到右為主

但是在這世界上的文字排版有從左到右, 上到下, 右到左等呈現方式

所以需要依照需求或不同語系的文字上有相對應的處理, 尤其是在多語系的情況下

上到下的排版大多是書面用的排版, 相對在資訊領域比較少呈現, 且因為上到下的排版方式也大多是亞洲地區的國家, 在標準化的話語權不高

但也漸漸在 web 領域中有開始支援上到下的排版(writing-mode)

且在資訊領域也大多是用左到右的呈現, 所以影響相對小

額外補充一下大部分的方塊文字像是中文、日文、韓文等其實左到右、右到左、上到下都可以書寫與呈現

但在部份語系像是

  • 阿拉伯文
  • 烏爾都文
  • 庫德文
  • 波斯文
  • 希伯來文

上述文字排版就固定都是從右到左

除了希伯來文以外都是從阿拉伯文字延伸的, 就像是英文、德文、法文都是拉丁文延伸出來一樣

至於為何在世界各地的文字排版會有差異其實主要是起源於早期該文字書寫的方式與被書寫在什麼材質上面為主

右到左主要起源於早期是文字是刻在石板上面, 左手拿鑿子, 右手拿錘子, 這樣從右到左是相對順手的

中文早期大多從上到下從右到左的排列主要起源於以前是在竹片上捲成冊的(竹簡)

在單片竹片上面書寫自然是上到下, 那捲成冊之後在閱讀時為了方便持握, 會是左手拿著竹簡, 右手打開來看內容, 自然就變成了右到左來閱讀

不過現在大多也因為資訊科學的發展後在電子載體上就沿用左到右的排版(前面提到電腦乃至資訊科學都是在拉丁語系的國家發展出來的)

不過在亞洲地區的國家部分書面印刷還是有保留上到下右到左的排版

不過有點扯遠了

實作 RTL(Right To Left) 的方式

這次主要是要處理 RTL 的問題

但其實在實作上也很好解

HTML tag 有個屬性是 dir 只要設定是 rtl 或是 ltr 就可以了

那麼要在什麼情況下設定這個東西?

大致上會在兩種情況設定

1. 多語系且有右到左語系

網站是多語系的網站且有右到左語系提供切換呈現

所以只要在使用者決定好語系時針對該語系時需要呈現 RTL 的 tag 做處理就可以了

以下有個例子是 Apple 的官網就是如此設計的

Apple 的官網就是在切換成不同語系時就設定不同的 dir 值來處理

2. UGC(user generated content) 有右到左語系的內容

像是討論區或是可以讓使用者輸入內容呈現的部分這部分也要注意處理

這樣的話呈現內容就不會是我們能夠先掌控的

不像一般網站如果要呈現多語系, 那網站內容都會是自己的多語系檔與自己判斷多語系

這情況就可以使用 dir 的第三種設定 auto

瀏覽器會依照 tag 裡面的文字來判斷語系是 RTL 的語系時就自動轉成右到左, 如果是一般 LTR 的語系需維持左到右

這個方法很實用但也有個小缺點

就是當內容一開始是 LTR 的文字時就會讓整個內容是 LTR

像是以下情況

<p dir="auto">This content هذه الفقرة باللغة العربية ، لذا يجب الانتقال من اليمين إلى اليسار.</p>

會有點尷尬, 也不太確定這樣的呈現是不是可行的

但是可以用另一個方式就是用 regex 來確認內容是不是有 RTL 的字元

找了一下網路上整理出來的

function IsRTL (s) {
  const rtlChars = '\u0591-\u07FF\u200F\u202B\u202E\uFB1D-\uFDFD\uFE70-\uFEFC'
  const rtlDirCheck = new RegExp('[' + rtlChars + ']')
  return rtlDirCheck.test(s)
}

這個 function 可以判斷內容是否含有 RTL 的字元

但這個也不適合有混合了 RTL 和 LTR 的內容

總之還是得自己評估採用哪種方式比較好

dir - HTML: HyperText Markup Language | MDN

Refer - 縱書與橫書

Read more

HTML form submit same name in php, nodejs, golang

on 2020-10-24

一直以來大部分時間都在用 PHP 開發

所以也用 PHP 來處理 HTML Form

所以都下意識地認為 <input type="checkbox" name="game[]" value="FGO">

這樣的 name="game[]" 的處理方式是正規的處理 HTML Form 的多選的方式

也疑惑為何大多的 HTML Form 的教學甚至 MDN 都沒有提到這件事

就在某一天我在檢視到同事寫的 code 時

發現同事用 JavaScript 處理

硬爬出來自己組字串送出去

我才想起這令人感到恐懼的事情

因為公司同事是寫 golang 的專門, 就算前端不熟

應該也不至於連這樣概念都沒有就用硬爬的方式處理

所以再調整的同時也跟同事確認後

我也真正的直視這問題

到底要怎麼處理表單中多選的資料?

PHP 寫久的人大多都知道要用上面列出的方法

name="game[]"

就是 game + []

但是當我認真地尋找關於這個問題時

意外地發現了一篇 stackoverflow 的問答

Several Checkboxes sharing the same name

其實 W3C 根本沒有管你 name="" 重複要如何處理

以下是 PHP, nodejs, golang 的原生方式來測試的結果

PHP

以下問答有提供了 PHP doc 說明 PHP 如何處理多選

PHP Multiple Checkbox Array

在 name 後面添加 [] 即可處理把相同的 name 組成 Array

沒有加的話是會取最後一個

POST & GET 都是如此處理

example:

<input type="checkbox" name="game[]" value="FGO" />
<input type="checkbox" name="game[]" value="GirlsFrontline" />
<?php
$_GET['game'];
$_POST['game'];

nodejs

GET 和 POST 用的方式不一樣

基本上都會用到 querystring module

querystring module 主要都在處理 querystring 相關的功能

主要靠它 parse querystring format(ex: key=calue&key2=value2)

所以 nodejs 會自動把同樣的 name 併在一起組成 array

GET

會需要用 url module

url module 主要是要拿掉 GET 的 url 的 querystring 再丟給 querystring 處理

example:

<input type="checkbox" name="game" value="FGO" />
<input type="checkbox" name="game" value="GirlsFrontline" />
const url = require("url");
const querystring = require("querystring");

const thisUrl = new URL(req.url, "http://" + req.headers.host);
const query = new URLSearchParams(thisUrl.search).toString(); // 這一段是要把 `?key=calue&key2=value2` => `key=calue&key2=value2`

querystring.parse(query)["game"];

POST

把接進來的 body 組起來再使用 querystring parse

example:

var formData = "";
req.on("data", function (data) {
  formData += data;
});
req.on("end", function () {
  console.log(querystring.parse(formData)["game"]);
});

URL | Node.js v15.0.1 Documentation

Query string | Node.js v15.0.1 Documentation

golang

import http 就可以處理 GET 和 POST 的情況

大致上的處理方式和行為和 nodejs 一樣(只差在語言的寫法不一樣)

也是會自動把同樣的 name 併在一起

組成 slice

GET

request struct 裡面有 URL 有 function Query 可以拿到所有的 querystring

example:

<input type="checkbox" name="game" value="FGO" />
<input type="checkbox" name="game" value="GirlsFrontline" />
package main

import (
    "fmt"
	"net/http"
)

func getHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.URL.Query()["game"])
}

func main() {
    http.HandleFunc("/get", getHandler)
}

POST

request 有一個 parseForm 的 function 可以抓取 HTTP POST, PUT, and PATCH 的 body

example:

package main

import (
    "fmt"
	"net/http"
)

func postHandler(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    fmt.Println(r.Form["game"])
}

func main() {
    http.HandleFunc("/post", postHandler)
}

http - parseForm

最後總結

只有 PHP 要在相同的 name 後面添加 [] 才可以在 PHP 端收到的 name 併在一起

nodejs 和 golang 都只要是 name 一樣就可以了

針對這三種語言都有寫了 demo code

只要三種語言的環境裝好

都可以直接起 local server 來試驗看看

html_form_multiple_name

Read more