#![cfg(feature = "translation")]
use std::sync::Arc;
use finance_query::translation::{self, Lang, Translatable, TranslationBackend};
#[test]
fn test_lang_parsing_and_normalization() {
let lang = Lang::parse("zh-TW").unwrap();
assert_eq!(lang.code(), "zh-Hant"); assert!(!lang.is_english());
assert!(Lang::parse("en-US").unwrap().is_english());
assert_eq!(Lang::parse("pt_BR").unwrap().code(), "pt");
}
#[test]
fn test_invalid_lang_tags_error() {
assert!(Lang::parse("").is_err());
assert!(Lang::parse("1234").is_err());
}
#[tokio::test]
async fn test_providers_invalid_lang_fails_fast() {
let result = finance_query::Providers::builder()
.lang("1234")
.build()
.await;
assert!(result.is_err());
}
struct Profile {
sector: Option<String>,
}
impl Translatable for Profile {
fn visit_translatable(&mut self, visit: &mut dyn FnMut(&mut String)) {
if let Some(s) = &mut self.sector {
visit(s);
}
}
}
#[tokio::test]
async fn test_dictionary_terms_translate_without_backend() {
let mut p = Profile {
sector: Some("Technology".into()),
};
translation::translate(&mut p, "ja").await.unwrap();
assert_eq!(p.sector.as_deref(), Some("テクノロジー"));
}
#[tokio::test]
async fn test_english_target_is_a_no_op() {
let mut p = Profile {
sector: Some("Technology".into()),
};
translation::translate(&mut p, "en-US").await.unwrap();
assert_eq!(p.sector.as_deref(), Some("Technology"));
}
struct MyBackend;
#[async_trait::async_trait]
impl TranslationBackend for MyBackend {
fn id(&self) -> &'static str {
"my-backend"
}
async fn translate_batch(
&self,
texts: &[String],
target: &Lang,
) -> finance_query::Result<Vec<String>> {
let _ = target;
Ok(texts.to_vec())
}
}
#[allow(dead_code)]
fn _register_custom_backend() {
translation::set_backend(Arc::new(MyBackend));
}
#[allow(dead_code)]
async fn _verify_builder_examples() -> Result<(), Box<dyn std::error::Error>> {
use finance_query::{Capability, Provider, Providers, SearchOptions, Ticker, Tickers, finance};
let ticker = Ticker::builder("7203.T").lang("ja").build().await?;
let _quote = ticker.quote::<finance_query::format::Raw>().await?;
let tickers = Tickers::builder(["AAPL", "NVDA"])
.lang("de")
.build()
.await?;
let _news = tickers.news().await?;
let providers = Providers::builder()
.route(Capability::QUOTE, [Provider::Yahoo])
.lang("ja")
.build()
.await?;
let ticker = providers.ticker("7203.T").build().await?;
let _quote = ticker.quote::<finance_query::format::Raw>().await?;
let _en = providers.ticker("AAPL").lang("en-US").build().await?;
let mut results = finance::search("toyota", &SearchOptions::default()).await?;
translation::translate(&mut results, "ja").await?;
Ok(())
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_ticker_lang_translates_dictionary_fields() {
use finance_query::Ticker;
let ticker = Ticker::builder("7203.T").lang("ja").build().await.unwrap();
let quote = ticker.quote::<finance_query::format::Raw>().await.unwrap();
if let Some(sector) = "e.sector_disp {
println!("sector_disp (ja): {sector}");
assert!(
!sector.is_ascii(),
"expected a translated (non-ASCII) sector, got '{sector}'"
);
}
}
#[tokio::test]
#[ignore = "requires network access"]
async fn test_standalone_search_translation() {
use finance_query::{SearchOptions, finance};
let mut results = finance::search("toyota", &SearchOptions::default())
.await
.unwrap();
translation::translate(&mut results, "ja").await.unwrap();
println!("translated search results: {} quotes", results.quotes.len());
}