添加qiming-rcoder模块
This commit is contained in:
125
qiming-rcoder/crates/rcoder/src/handler/utils/locale.rs
Normal file
125
qiming-rcoder/crates/rcoder/src/handler/utils/locale.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
//! 语言检测工具
|
||||
//!
|
||||
//! 从 HTTP 请求中提取语言偏好
|
||||
//! 优先级:HTTP Header > 环境变量 > 默认值 (en-US)
|
||||
|
||||
use axum::http::HeaderMap;
|
||||
use shared_types::{SUPPORTED_LOCALES, parse_accept_language};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
/// 默认语言(硬编码回退值)
|
||||
const FALLBACK_LOCALE: &str = "en-US";
|
||||
|
||||
/// 缓存的环境变量默认语言
|
||||
static DEFAULT_LOCALE_CACHED: OnceLock<&'static str> = OnceLock::new();
|
||||
|
||||
/// 从环境变量获取默认语言(带缓存)
|
||||
///
|
||||
/// 环境变量名:DEFAULT_LOCALE
|
||||
/// 如果未设置或无效,返回硬编码默认值 "en-US"
|
||||
///
|
||||
/// 使用 OnceLock 缓存,只在首次调用时读取环境变量
|
||||
fn get_default_locale_from_env() -> &'static str {
|
||||
*DEFAULT_LOCALE_CACHED.get_or_init(|| {
|
||||
if let Ok(locale) = std::env::var("DEFAULT_LOCALE") {
|
||||
// 验证是否为支持的语言
|
||||
if SUPPORTED_LOCALES.contains(&locale.as_str()) {
|
||||
// 匹配已知的静态字符串,避免内存泄漏
|
||||
match locale.as_str() {
|
||||
"zh-CN" => return "zh-CN",
|
||||
"zh-TW" => return "zh-TW",
|
||||
"en-US" => return "en-US",
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
FALLBACK_LOCALE
|
||||
})
|
||||
}
|
||||
|
||||
/// 从 HTTP 请求头获取语言
|
||||
///
|
||||
/// 语言检测优先级:
|
||||
/// 1. HTTP Accept-Language Header
|
||||
/// 2. 环境变量 DEFAULT_LOCALE
|
||||
/// 3. 硬编码默认值 "en-US"
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `headers` - HTTP 请求头
|
||||
///
|
||||
/// # Returns
|
||||
/// 语言代码,如 "zh-CN", "en-US"
|
||||
pub fn get_locale_from_headers(headers: &HeaderMap) -> &'static str {
|
||||
// 1. 首先尝试从 HTTP Header 获取
|
||||
if let Some(header) = headers.get("Accept-Language").and_then(|v| v.to_str().ok()) {
|
||||
// parse_accept_language 返回的总是支持的语言或默认值
|
||||
return parse_accept_language(Some(header));
|
||||
}
|
||||
|
||||
// 2. 尝试从环境变量获取(已缓存)
|
||||
get_default_locale_from_env()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use axum::http::header::HeaderName;
|
||||
|
||||
#[test]
|
||||
fn test_get_locale_from_headers_zh_cn() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
HeaderName::from_static("accept-language"),
|
||||
"zh-CN".parse().unwrap(),
|
||||
);
|
||||
assert_eq!(get_locale_from_headers(&headers), "zh-CN");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_locale_from_headers_zh_tw() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
HeaderName::from_static("accept-language"),
|
||||
"zh-TW".parse().unwrap(),
|
||||
);
|
||||
assert_eq!(get_locale_from_headers(&headers), "zh-TW");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_locale_from_headers_zh_hk() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
HeaderName::from_static("accept-language"),
|
||||
"zh-HK".parse().unwrap(),
|
||||
);
|
||||
assert_eq!(get_locale_from_headers(&headers), "zh-TW");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_locale_from_headers_en_us() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
HeaderName::from_static("accept-language"),
|
||||
"en-US".parse().unwrap(),
|
||||
);
|
||||
assert_eq!(get_locale_from_headers(&headers), "en-US");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_locale_from_headers_none() {
|
||||
let headers = HeaderMap::new();
|
||||
// 无 Header 且无环境变量时,应返回默认值 en-US
|
||||
assert_eq!(get_locale_from_headers(&headers), "en-US");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_locale_from_headers_unsupported() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
HeaderName::from_static("accept-language"),
|
||||
"fr-FR".parse().unwrap(),
|
||||
);
|
||||
// 不支持的语言应返回默认值
|
||||
assert_eq!(get_locale_from_headers(&headers), "en-US");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user