translation-lib 0.1.1

A simple and efficient translation library for Rust
Documentation
//! # Translation Library
//!
//! 一个简单高效的Rust翻译库,支持文本和HTML翻译功能。
//!
//! ## 主要特性
//!
//! - **多格式支持**: 支持纯文本、HTML和DOM翻译
//! - **智能过滤**: 自动识别并跳过代码、URL、邮箱等不需要翻译的内容
//! - **并发翻译**: 基于tokio的异步并发处理
//! - **缓存机制**: 内置LRU缓存,避免重复翻译
//! - **错误处理**: 完善的错误处理和自动重试机制
//! - **语言检测**: 智能检测文本语言,避免不必要的翻译
//!
//! ## 快速开始
//!
//! ```no_run
//! use translation_lib::{Translator, TranslationConfig};
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//!     let config = TranslationConfig::new("YOUR_API_URL".to_string())
//!         .with_api_key("YOUR_API_KEY".to_string());
//!     let translator = Translator::new(config)?;
//!     
//!     // 文本翻译
//!     let result = translator.translate_text("Hello, world!", "zh").await?;
//!     println!("翻译结果: {}", result);
//!     
//!     // HTML翻译
//!     let html = r#"<p>Hello <span>World</span></p>"#;
//!     let translated_html = translator.translate_html(html, "zh").await?;
//!     println!("HTML翻译: {}", translated_html);
//!     
//!     Ok(())
//! }
//! ```
//!
//! ## 使用环境变量配置
//!
//! ```no_run
//! use translation_lib::translate_text_simple;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//!     // 设置环境变量: TRANSLATION_API_URL
//!     let result = translate_text_simple("Hello", "zh").await?;
//!     println!("快速翻译: {}", result);
//!     Ok(())
//! }
//! ```

pub mod config;
pub mod error;
pub mod translator;
pub mod types;

pub use config::TranslationConfig;
pub use error::{TranslationError, TranslationResult};
pub use translator::Translator;
pub use types::{TextAnalysis, TextType};

/// 便利函数:简单文本翻译
pub async fn translate_text_simple(text: &str, target_lang: &str) -> TranslationResult<String> {
    let config = TranslationConfig::from_env().unwrap_or_default();
    let translator = Translator::new(config)?;
    translator.translate_text(text, target_lang).await
}

/// 便利函数:简单HTML翻译
pub async fn translate_html_simple(html: &str, target_lang: &str) -> TranslationResult<String> {
    let config = TranslationConfig::from_env().unwrap_or_default();
    let translator = Translator::new(config)?;
    translator.translate_html(html, target_lang).await
}

/// 检查文本是否应该翻译
pub fn should_translate(text: &str, target_lang: &str) -> bool {
    let analysis = analyze_text(text);
    analysis.is_translatable && analysis.detected_lang.as_deref() != Some(target_lang)
}

/// 分析文本特征
pub fn analyze_text(text: &str) -> TextAnalysis {
    use types::*;

    if text.trim().is_empty() {
        return TextAnalysis {
            is_translatable: false,
            detected_lang: None,
            confidence: 0.0,
            text_type: TextType::Empty,
        };
    }

    // URL检测
    if text.starts_with("http") || text.contains("://") {
        return TextAnalysis {
            is_translatable: false,
            detected_lang: None,
            confidence: 1.0,
            text_type: TextType::Url,
        };
    }

    // 邮箱检测
    if text.contains('@') && text.split_whitespace().count() == 1 {
        return TextAnalysis {
            is_translatable: false,
            detected_lang: None,
            confidence: 1.0,
            text_type: TextType::Email,
        };
    }

    // 代码检测
    let code_chars = text.chars().filter(|&c| "{}[]();".contains(c)).count();
    let code_ratio = code_chars as f32 / text.len() as f32;
    if code_ratio > 0.1 {
        return TextAnalysis {
            is_translatable: false,
            detected_lang: None,
            confidence: code_ratio,
            text_type: TextType::Code,
        };
    }

    // 中文检测
    let chinese_chars = text.chars().filter(|c| is_chinese_char(*c)).count();
    let chinese_ratio = chinese_chars as f32 / text.chars().count() as f32;

    if chinese_ratio > 0.5 {
        return TextAnalysis {
            is_translatable: false,
            detected_lang: Some("zh".to_string()),
            confidence: chinese_ratio,
            text_type: TextType::Content,
        };
    }

    // 默认为可翻译的英文内容
    TextAnalysis {
        is_translatable: true,
        detected_lang: Some("en".to_string()),
        confidence: 1.0 - chinese_ratio,
        text_type: TextType::Content,
    }
}

/// 检查字符是否为中文字符
fn is_chinese_char(c: char) -> bool {
    matches!(c, '\u{4e00}'..='\u{9fff}' | '\u{3400}'..='\u{4dbf}' | '\u{f900}'..='\u{faff}')
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_chinese_char_detection() {
        assert!(is_chinese_char('中'));
        assert!(is_chinese_char('文'));
        assert!(!is_chinese_char('a'));
        assert!(!is_chinese_char('1'));
    }

    #[test]
    fn test_should_translate() {
        // 英文内容应该翻译
        assert!(should_translate("Hello world", "zh"));

        // 中文内容不应该翻译成中文
        assert!(!should_translate("你好世界", "zh"));

        // URL不应该翻译
        assert!(!should_translate("https://example.com", "zh"));

        // 邮箱不应该翻译
        assert!(!should_translate("test@example.com", "zh"));

        // 代码不应该翻译
        assert!(!should_translate("function() { return true; }", "zh"));
    }

    #[test]
    fn test_analyze_text() {
        // 英文文本
        let analysis = analyze_text("Hello world");
        assert!(analysis.is_translatable);
        assert_eq!(analysis.detected_lang, Some("en".to_string()));
        assert_eq!(analysis.text_type, TextType::Content);

        // 中文文本
        let analysis = analyze_text("你好世界");
        assert!(!analysis.is_translatable);
        assert_eq!(analysis.detected_lang, Some("zh".to_string()));
        assert_eq!(analysis.text_type, TextType::Content);

        // URL
        let analysis = analyze_text("https://example.com");
        assert!(!analysis.is_translatable);
        assert_eq!(analysis.text_type, TextType::Url);

        // 邮箱
        let analysis = analyze_text("test@example.com");
        assert!(!analysis.is_translatable);
        assert_eq!(analysis.text_type, TextType::Email);

        // 代码
        let analysis = analyze_text("function() { return true; }");
        assert!(!analysis.is_translatable);
        assert_eq!(analysis.text_type, TextType::Code);

        // 空文本
        let analysis = analyze_text("");
        assert!(!analysis.is_translatable);
        assert_eq!(analysis.text_type, TextType::Empty);
    }

    #[test]
    fn test_config_validation() {
        // 有效配置
        let config = TranslationConfig::default();
        assert!(config.validate().is_ok());

        // 无效配置 - 空URL
        let mut config = TranslationConfig::default();
        config.api_url = "".to_string();
        assert!(config.validate().is_err());

        // 无效配置 - 错误的URL格式
        let mut config = TranslationConfig::default();
        config.api_url = "invalid-url".to_string();
        assert!(config.validate().is_err());

        // 无效配置 - 零并发数
        let mut config = TranslationConfig::default();
        config.max_concurrent_requests = 0;
        assert!(config.validate().is_err());
    }
}