Rust语言标签处理库langtag的使用,高效解析与验证BCP 47语言标签格式

Rust语言标签处理库langtag的使用,高效解析与验证BCP 47语言标签格式

CI Crate informations License Documentation

这个crate提供了对RFC5646(BCP47)定义的语言标签的实现。

使用方法

你可以轻松地从任何字符串解析新的语言标签:

use langtag::LangTag;

fn main() -> Result<(), langtag::InvalidLangTag<&'static str>> {
  let tag = LangTag::new("fr-FR")?;
  assert_eq!(tag.language().unwrap().primary(), "fr");
  assert!(tag == "Fr-fr"); // 比较是大小写不敏感的
  Ok(())
}

请注意LangTag::new不会复制给定的数据,而是仅仅借用它。LangTagBuf类型允许你拥有语言标签的所有权。一旦解析完成,你可以使用提供的函数探索语言标签的每个组成部分。

完整示例代码

以下是使用langtag库处理语言标签的更完整示例:

use langtag::{LangTag, LangTagBuf};

fn main() {
    // 示例1:基本解析和验证
    match LangTag::new("en-US") {
        Ok(tag) => {
            println!("Primary language: {}", tag.language().unwrap().primary());
            println!("Region: {}", tag.region().unwrap());
        }
        Err(e) => println!("Invalid language tag: {}", e),
    }

    // 示例2:拥有所有权的LangTagBuf
    let tag_buf = LangTagBuf::new("zh-CN").unwrap();
    println!("Chinese Simplified: {}", tag_buf);

    // 示例3:比较操作(大小写不敏感)
    let tag1 = LangTag::new("en-US").unwrap();
    let tag2 = LangTag::new("EN-us").unwrap();
    assert!(tag1 == tag2);

    // 示例4:提取所有组件
    if let Ok(tag) = LangTag::new("de-Latn-DE-1996") {
        println!("Language: {}", tag.language().unwrap());
        if let Some(script) = tag.script() {
            println!("Script: {}", script);
        }
        if let Some(region) = tag.region() {
            println!("Region: {}", region);
        }
        if let Some(variant) = tag.variant() {
            println!("Variant: {}", variant);
        }
    }
}

完整示例demo

基于上述示例,这里提供一个更全面的使用demo:

use langtag::{LangTag, LangTagBuf};

fn main() {
    // 1. 创建语言标签
    println!("--- 基本语言标签创建 ---");
    let simple_tag = LangTag::new("en").unwrap();
    println!("简单标签: {}", simple_tag);
    
    // 2. 带区域的标签
    let region_tag = LangTag::new("zh-CN").unwrap();
    println!("带区域的标签: {}", region_tag);
    
    // 3. 完整标签解析
    println!("\n--- 完整标签解析 ---");
    if let Ok(tag) = LangTag::new("sr-Latn-RS") {
        println!("完整标签: {}", tag);
        println!("主语言: {}", tag.language().unwrap().primary());
        println!("书写系统: {}", tag.script().unwrap());
        println!("地区: {}", tag.region().unwrap());
    }
    
    // 4. 标签比较
    println!("\n--- 标签比较 ---");
    let tag1 = LangTag::new("en-US").unwrap();
    let tag2 = LangTag::new("en-us").unwrap();
    println!("比较结果: {}", tag1 == tag2); // true
    
    // 5. 创建拥有所有权的标签
    println!("\n--- 拥有所有权的标签 ---");
    let owned_tag = LangTagBuf::new("fr-FR").unwrap();
    println!("法语(法国): {}", owned_tag);
    
    // 6. 错误处理
    println!("\n--- 错误处理 ---");
    match LangTag::new("invalid-tag") {
        Ok(tag) => println!("有效标签: {}", tag),
        Err(e) => println!("错误: {}", e),
    }
    
    // 7. 复杂标签处理
    println!("\n--- 复杂标签处理 ---");
    if let Ok(tag) = LangTag::new("hy-Latn-IT-arevela") {
        println!("复杂标签: {}", tag);
        println!("变体: {}", tag.variant().unwrap());
    }
}

许可证

采用以下任一许可证:

  • Apache License, Version 2.0
  • MIT license

贡献

除非您明确声明,否则根据Apache-2.0许可证定义,您有意提交的任何贡献将被视为双重许可如上所述,不附加任何其他条款或条件。


1 回复

Rust语言标签处理库langtag的使用:高效解析与验证BCP 47语言标签格式

介绍

langtag是Rust中一个专门用于处理BCP 47语言标签的库。BCP 47是定义语言标签格式的标准,用于标识人类语言、脚本、地区等信息,常见于国际化应用中。

该库提供:

  • 完整的BCP 47语言标签解析
  • 严格的格式验证
  • 便捷的组件访问方法
  • 符合RFC 5646和RFC 4647标准

安装

在Cargo.toml中添加依赖:

[dependencies]
langtag = "0.3"

基本用法

解析语言标签

use langtag::LanguageTag;

fn main() {
    // 解析en-US语言标签
    let tag: LanguageTag = "en-US".parse().unwrap();
    // 输出主要语言
    println!("Primary language: {}", tag.primary_language());
    // 输出地区(如果有)
    if let Some(region) = tag.region() {
        println!("Region: {}", region);
    }
}

验证语言标签

use langtag::LanguageTag;

// 验证语言标签是否有效
fn is_valid_tag(tag_str: &str) -> bool {
    tag_str.parse::<LanguageTag>().is_ok()
}

fn main() {
    // 验证中文标签
    println!("{}", is_valid_tag("zh-Hant-CN"));  // true
    // 验证无效标签(USA不是有效地区代码)
    println!("{}", is_valid_tag("en-USA"));     // false 
}

高级用法

构建语言标签

use langtag::{LanguageTag, Subtag};

fn main() {
    // 创建新的语言标签
    let mut tag = LanguageTag::new();
    // 设置主要语言
    tag.set_primary_language("zh").unwrap();
    // 设置脚本
    tag.set_script("Hant").unwrap();
    // 设置地区
    tag.set_region("TW").unwrap();
    
    // 输出构建的标签: zh-Hant-TW
    println!("{}", tag);
}

比较语言标签

use langtag::LanguageTag;

fn main() {
    // 解析两个相似但不完全相同的标签
    let tag1: LanguageTag = "en-US".parse().unwrap();
    let tag2: LanguageTag = "en-Latn-US".parse().unwrap();
    
    // 基本匹配比较(忽略次要差异)
    println!("{}", tag1.matches(&tag2));  // true
    
    // 严格相等比较
    println!("{}", tag1 == tag2);  // false
}

处理扩展和私有部分

use langtag::LanguageTag;

fn main() {
    // 解析包含扩展的语言标签
    let tag: LanguageTag = "de-Latn-DE-u-attr-co-phonebk".parse().unwrap();
    
    // 输出脚本信息
    println!("Script: {:?}", tag.script());
    // 输出所有扩展
    println!("Extensions: {:?}", tag.extensions());
    
    // 输出第一个扩展的单字符标识
    if let Some(ext) = tag.extensions().next() {
        println!("First extension: {}", ext.singleton());
    }
}

实际应用示例

语言标签过滤

use langtag::LanguageTag;

// 根据过滤条件筛选语言标签
fn filter_tags(tags: Vec<&str>, filter: &str) -> Vec<String> {
    let filter_tag: LanguageTag = filter.parse().unwrap();
    tags.into_iter()
        .filter_map(|t| t.parse::<LanguageTag>().ok())
        .filter(|t| t.matches(&filter_tag))
        .map(|t| t.to_string())
        .collect()
}

fn main() {
    let tags = vec!["en-US", "en-GB", "fr-FR", "es-ES"];
    // 筛选所有英文标签
    let filtered = filter_tags(tags, "en");
    println!("{:?}", filtered);  // ["en-US", "en-GB"]
}

最佳语言匹配

use langtag::{LanguageTag, LangugeTagFilter};

// 在支持的语言中找到与请求最匹配的语言
fn find_best_match(supported: &[&str], requested: &str) -> Option<String> {
    let requested_tag: LanguageTag = requested.parse().ok()?;
    let supported_tags: Vec<LanguageTag> = supported.iter()
        .filter_map(|s| s.parse().ok())
        .collect();
    
    requested_tag.best_match(supported_tags.iter())
        .map(|t| t.to_string())
}

fn main() {
    let supported = ["zh-CN", "zh-TW", "en-US"];
    // 查找中文香港的最佳匹配
    println!("{:?}", find_best_match(&supported, "zh-HK"));  // Some("zh-TW")
    // 查找法语(不支持)的匹配
    println!("{:?}", find_best_match(&supported, "fr-FR")); // None
}

完整示例:国际化应用中的语言处理

use langtag::{LanguageTag, LangugeTagFilter};
use std::collections::HashMap;

fn main() {
    // 模拟应用支持的语言资源
    let resources: HashMap<String, &str> = [
        ("en-US".to_string(), "Hello!"),
        ("en-GB".to_string(), "Hello!"),
        ("zh-CN".to_string(), "你好!"),
        ("zh-TW".to_string(), "你好!"),
        ("ja-JP".to_string(), "こんにちは!"),
    ].iter().cloned().collect();

    // 用户偏好语言列表(按优先级排序)
    let user_preferences = ["zh-HK", "en-US", "fr-FR"];
    
    // 找到最佳匹配的语言
    let supported_langs: Vec<&str> = resources.keys().map(|s| s.as_str()).collect();
    if let Some(best_match) = find_best_match(&supported_langs, &user_preferences.join(",")) {
        println!("最佳匹配语言: {}", best_match);
        println!("显示内容: {}", resources[&best_match]);
    } else {
        println!("没有匹配的语言,使用默认英语");
        println!("显示内容: {}", resources["en-US"]);
    }
}

// 扩展的最佳匹配函数,处理多个偏好语言
fn find_best_match(supported: &[&str], requested: &str) -> Option<String> {
    for lang in requested.split(',') {
        let requested_tag: LanguageTag = match lang.parse() {
            Ok(tag) => tag,
            Err(_) => continue,
        };
        
        let supported_tags: Vec<LanguageTag> = supported.iter()
            .filter_map(|s| s.parse().ok())
            .collect();
        
        if let Some(matched) = requested_tag.best_match(supported_tags.iter()) {
            return Some(matched.to_string());
        }
    }
    None
}

注意事项

  1. 解析失败时会返回Err,应妥善处理错误情况
  2. 语言标签比较有matches(基本匹配)和==(严格相等)两种方式
  3. 构建语言标签时,组件顺序必须符合BCP 47规范
  4. 地区代码应使用ISO 3166-1 alpha-2,脚本代码应使用ISO 15924

langtag库为Rust中处理语言标签提供了完整且符合标准的解决方案,适合需要国际化支持的应用程序使用。

回到顶部