在實作多國語言的Web服務時,會需要去讀取客戶端傳送來的HTTP請求標頭中的Accept-Language
欄位,來判斷客戶端偏好使用的語言。然而,Rocket框架並未內建解析Accept-Language
欄位的功能,開發者只能自行去撰寫程式來想辦法用Accept-Language
欄位的原始資料去做客戶端的語言判斷,不但麻煩又很不安全。
一個Accept-Language
欄位的值可能如以下這樣:
zh-TW,zh;q=0.7,en-US;q=0.6,en;q=0.5
以上的值,代表客戶端想要優先使用台灣的繁體中文,如果沒有的話就使用任意地區的中文,如果還是沒有的話就使用美國的英文,如果又沒有的話就使用任意地區的英文。
accept-language Request Guard for Rocket Framework
「accept-language Request Guard for Rocket Framework」是筆者開發出來的套件,將可以讀取Accept-Language
欄位並且進行語言判斷的功能寫成請求守衛,一勞永逸。使用accept-language
這個crate來進行Accept-Language
欄位值的解析,並轉成unic-langid
這個crate提供的LanguageIdentifier
結構體的實體來儲存。
Crates.io
Cargo.toml
使用方法
在Rocket框架啟動前,先建立出LanguageIdentifier
結構實體的陣列,用來表示這個Rocket應用程式支援的語言。接著再把這個LanguageIdentifier
陣列,註冊成Rocket的應用程式狀態。language_region_pairs
巨集能以很小的開支(overhead)來建立Vec<LanguageIdentifier>
結構實體。
在Rocket框架的路由處理程序中,藉由放置rocket_accept_language
這個crate所提供的AcceptLanguage
請求守衛,來解析HTTP請求標頭中的Accept-Language
欄位。AcceptLanguage
的結構實體提供了get_appropriate_language_region
方法,可以傳入一個LanguageIdentifier
陣列切片,根據Rocket應用程式有支援的語言來判斷出最符合HTTP請求的語言。
get_appropriate_language_region
方法會回傳unic-langid
這個crate提供的提供的Language
和Region
結構體的實體。透過language
巨集和region
巨集,可以用常數建立出Language
和Region
結構實體並儲存起來,方便重用。
以下是一個使用範例:
#[macro_use]
extern crate rocket;
use rocket::State;
use rocket_accept_language::{
language, language_region_pairs, region,
unic_langid::subtags::{Language, Region},
AcceptLanguage, LanguageIdentifier,
};
const LANGUAGE_ZH: Language = language!("zh");
const LANGUAGE_EN: Language = language!("en");
const LANGUAGE_JP: Language = language!("jp");
const REGION_CN: Region = region!("cn");
const REGION_TW: Region = region!("tw");
struct SupportLanguages {
language_identifiers: Vec<LanguageIdentifier>,
}
impl SupportLanguages {
fn as_language_identifiers(&self) -> &[LanguageIdentifier] {
&self.language_identifiers
}
}
#[get("/")]
fn hello(
accept_language: &AcceptLanguage,
support_languages: &State<SupportLanguages>,
) -> &'static str {
let (language, region) = accept_language
.get_appropriate_language_region(support_languages.as_language_identifiers())
.unwrap_or((LANGUAGE_EN, None));
match language {
LANGUAGE_ZH => match region.unwrap_or(REGION_TW) {
REGION_CN => "哈罗!",
_ => "哈囉!",
},
LANGUAGE_JP => "ハロー",
_ => "Hello!",
}
}
#[launch]
fn rocket() -> _ {
let support_languages = SupportLanguages {
language_identifiers: language_region_pairs!["zh-TW", "zh_CN", "jp", "en"],
};
rocket::build().manage(support_languages).mount("/", routes![hello])
}