Rust本地化处理库with_locals的使用:高效管理多语言和本地化资源的插件库

Rust本地化处理库with_locals的使用:高效管理多语言和本地化资源的插件库

基本示例:返回/生成引用本地变量的format_args

use ::core::fmt::Display;
use ::with_locals::with;

#[with('local)]
fn hex(n: u32) -> &'local dyn Display
{
    &format_args!("{:#x}", n)
}

上面的代码会被转换为:

use ::core::fmt::Display;

fn with_hex<R, F>(n: u32, f: F) -> R
where           F : FnOnce(&'_     dyn Display) -> R,
 // for<'local> F : FnOnce(&'local dyn Display) -> R,
{
    f(&format_args!("{:#x}", n))
}

这里的f: F被称为"continuation"(延续): 不是让函数返回/生成某个元素/对象,而是接收调用者想要对该元素执行的逻辑, 这样实际处理对象的是被调用者而不是调用者。

使用示例

with_hex(66, |s| {
    println!("{}", s);
})

嵌套使用示例

with_hex(1, |one| {
    with_hex(2, |two| {
        with_hex(3, |three| {
            // 嵌套层级过深...
        })
    })
})

使用#[with]宏可以简化:

#[with] let one = hex(1);
#[with] let two = hex(2); 
#[with] let three = hex(3);

完整示例代码

use ::core::fmt::Display;
use ::with_locals::with;

#[with('local)]
fn hex(n: u32) -> &'local dyn Display {
    &format_args!("{:#x}", n)
}

#[with]
fn hex_example() {
    let s: String = {
        println!("Hello, World!");
        #[with]
        let s_hex = hex(66);
        println!("s_hex = {}", s_hex); // 输出`s_hex = 0x42`
        let s = s_hex.to_string();
        assert_eq!(s, "0x42");
        s
    };
    assert_eq!(s, "0x42");
}

#[with]
fn main() {
    for n in 0.. {
        #[with]
        let s = hex(n);
        println!("{}", s);
        if n >= 5 {
            break;
        }
    };
}

特性方法示例

use ::with_locals::with;

trait ToStr {
    #[with('local)]
    fn to_str(self: &'_ Self) -> &'local str;
}

impl ToStr for u32 {
    #[with('local)]
    fn to_str(self: &'_ u32) -> &'local str {
        let mut x = *self;
        if x == 0 {
            return "0";
        }
        let mut buf = [b' '; 1 + 3 + 3 + 3]; // u32::MAX ~ 4_000_000_000
        let mut cursor = buf.len();
        while x > 0 {
            cursor -= 1;
            buf[cursor] = b'0' + (x % 10) as u8;
            x /= 10;
        }
        ::core::str::from_utf8(&buf[cursor..])
            .unwrap()
    }
}

#[with('special)]
fn main() {
    let s: &'special str = 42.to_str();
    assert_eq!(s, "42");
}

高级用法示例

use ::core::fmt::Display;
use ::with_locals::with;

#[with('local, continuation_name = return_)]
fn display_addr(addr: usize) -> &'local dyn Display {
    if addr == 0 {
        return_!(&"NULL");
    }
    with_hex(addr, |hex| {
        return_(&format_args!("0x{}", hex))
    })
}

#[with('local)]
fn hex(n: usize) -> &'local dyn Display {
    &format_args!("{:x}", n)
}

调试宏展开

在Cargo.toml中启用expand-macros特性:

[dependencies]
with_locals = { version = "...", features = ["expand-macros"] }

然后可以使用环境变量过滤特定展开:

WITH_LOCALS_DEBUG_FILTER=pattern cargo check

完整示例demo

以下是一个结合了上述特性的完整示例:

use ::core::fmt::Display;
use ::with_locals::with;

// 定义16进制格式化函数
#[with('local)]
fn hex(n: u32) -> &'local dyn Display {
    &format_args!("{:#x}", n)
}

// 定义数字转字符串特性
trait ToStr {
    #[with('local)]
    fn to_str(self: &'_ Self) -> &'local str;
}

impl ToStr for u32 {
    #[with('local)]
    fn to_str(self: &'_ u32) -> &'local str {
        let mut x = *self;
        if x == 0 {
            return "0";
        }
        let mut buf = [b' '; 10]; // 足够存储u32最大值
        let mut cursor = buf.len();
        while x > 0 {
            cursor -= 1;
            buf[cursor] = b'0' + (x % 10) as u8;
            x /= 10;
        }
        ::core::str::from_utf8(&buf[cursor..]).unwrap()
    }
}

#[with]
fn main() {
    // 使用hex函数
    #[with]
    let hex_value = hex(255);
    println!("Hex value: {}", hex_value); // 输出 Hex value: 0xff
    
    // 使用ToStr特性
    #[with]
    let str_value = 12345.to_str();
    println!("String value: {}", str_value); // 输出 String value: 12345
    
    // 组合使用
    #[with]
    let combined = hex(42);
    println!("Combined: {}", combined); // 输出 Combined: 0x2a
    
    // 嵌套使用
    #[with]
    let nested = {
        #[with]
        let a = hex(10);
        #[with]
        let b = hex(20);
        format!("{} + {} = {}", a, b, 10 + 20)
    };
    println!("Nested: {}", nested); // 输出 Nested: 0xa + 0x14 = 30
}

1 回复

Rust本地化处理库with_locals的使用指南

概述

with_locals是一个高效的Rust本地化处理库,专门设计用于管理多语言和本地化资源。它提供了简洁的API和强大的功能,帮助开发者轻松实现应用程序的国际化(i18n)和本地化(l10n)。

主要特性

  • 轻量级且高效的内存使用
  • 支持多种语言资源加载
  • 线程安全的本地化资源管理
  • 简单的键值对查找机制
  • 支持格式化字符串和动态参数

安装

在Cargo.toml中添加依赖:

[dependencies]
with_locals = "0.3"

基本使用方法

1. 初始化本地化资源

use with_locals::Localizations;

let mut locals = Localizations::new();
locals.add("en", "greeting", "Hello, {}!");
locals.add("es", "greeting", "¡Hola, {}!");
locals.add("fr", "greeting", "Bonjour, {}!");

2. 获取本地化字符串

// 获取英语问候语
let greeting = locals.get("en", "greeting").unwrap();
println!("{}", greeting.format(&["John"])); // 输出: Hello, John!

// 获取西班牙语问候语
let greeting = locals.get("es", "greeting").unwrap();
println!("{}", greeting.format(&["Juan"])); // 输出: ¡Hola, Juan!

3. 使用默认语言

locals.set_default_language("en");

// 当请求的语言不存在时回退到默认语言
let greeting = locals.get("de", "greeting").unwrap();
println!("{}", greeting.format(&["Hans"])); // 输出: Hello, Hans!

高级用法

从文件加载本地化资源

use with_locals::Localizations;
use std::path::Path;

let mut locals = Localizations::new();
locals.load_from_file(Path::new("locales/en.json")).unwrap();
locals.load_from_file(Path::new("locales/es.json")).unwrap();

假设en.json内容如下:

{
    "greeting": "Hello, {}!",
    "farewell": "Goodbye, {}!"
}

使用宏简化代码

use with_locals::{localizations, loc};

// 使用宏初始化
let locals = localizations! {
    "en" => {
        "greeting" => "Hello, {}!",
        "farewell" => "Goodbye, {}!"
    },
    "es" => {
        "greeting" => "¡Hola, {}!",
        "farewell" => "¡Adiós, {}!"
    }
};

// 使用宏获取本地化字符串
println!("{}", loc!(locals, "en", "greeting", "Alice"));

线程安全使用

use with_locals::Localizations;
use std::sync::Arc;
use std::thread;

let locals = Arc::new(localizations! {
    "en" => { "thread" => "Thread {} says hello!" },
    "fr" => { "thread" => "Le fil {} dit bonjour!" }
});

let handles: Vec<_> = (0..5).map(|i| {
    let locals = Arc::clone(&locals);
    thread::spawn(move || {
        let msg = locals.get("fr", "thread").unwrap();
        println!("{}", msg.format(&[&i.to_string()]));
    })
}).collect();

for handle in handles {
    handle.join().unwrap();
}

最佳实践

  1. 组织本地化文件:按语言代码组织JSON/YAML文件,如en.json, es.json
  2. 使用命名空间:为大型项目使用键前缀作为命名空间,如homepage.title
  3. 缓存常用查询:对于频繁访问的字符串,考虑在应用层缓存
  4. 错误处理:总是处理get可能返回的None情况

性能考虑

with_locals在设计上注重性能:

  • 使用高效的哈希映射存储本地化资源
  • 最小化字符串复制
  • 零成本抽象用于静态字符串

对于性能敏感的应用,建议在应用启动时预加载所有需要的本地化资源。

完整示例代码

use with_locals::{Localizations, localizations, loc};
use std::sync::Arc;
use std::thread;

fn main() {
    // 基本使用示例
    let mut locals = Localizations::new();
    locals.add("en", "welcome", "Welcome, {}!");
    locals.add("zh", "welcome", "欢迎, {}!");
    
    let welcome_msg = locals.get("zh", "welcome").unwrap();
    println!("{}", welcome_msg.format(&["张三"])); // 输出: 欢迎, 张三!

    // 使用宏示例
    let locals = localizations! {
        "en" => {
            "login" => "Login",
            "logout" => "Logout"
        },
        "zh" => {
            "login" => "登录",
            "logout" => "退出"
        }
    };
    
    println!("{}", loc!(locals, "zh", "login")); // 输出: 登录

    // 线程安全示例
    let shared_locals = Arc::new(localizations! {
        "en" => { "count" => "Count: {}" },
        "zh" => { "count" => "计数: {}" }
    });

    let mut handles = vec![];
    for i in 1..=3 {
        let locals = Arc::clone(&shared_locals);
        handles.push(thread::spawn(move || {
            let msg = locals.get("zh", "count").unwrap();
            println!("{}", msg.format(&[&i.to_string()]));
        }));
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

总结

with_locals库为Rust应用程序提供了简单而强大的本地化支持。通过其直观的API,开发者可以轻松实现多语言支持,同时保持代码的整洁和高效。无论是小型项目还是大型企业应用,with_locals都能满足您的国际化需求。

回到顶部