use dioxus::prelude::*;
use crate::{I18nError, I18nRuntime, global};
pub fn use_init_i18n_from(init: impl FnOnce() -> I18nRuntime + 'static) -> I18nSession {
use_context_provider(move || I18nSession::new(init()))
}
#[must_use]
pub fn use_i18n() -> I18nSession {
consume_context()
}
#[derive(Clone, Copy)]
pub struct I18nSession {
selected_locale: Signal<String>,
runtime: Signal<I18nRuntime>,
}
impl I18nSession {
#[must_use]
pub fn new(runtime: I18nRuntime) -> Self {
let initial_locale = runtime.initial_locale().to_string();
global::install_global_runtime(runtime.clone());
Self {
selected_locale: Signal::new(initial_locale),
runtime: Signal::new(runtime),
}
}
#[must_use]
pub fn language(&self) -> String {
self.selected_locale.read().clone()
}
pub fn set_language(&mut self, locale: impl Into<String>) {
let locale = locale.into();
self.selected_locale.set(locale.clone());
global::set_global_locale(locale);
}
#[must_use]
pub fn available_locales(&self) -> Vec<String> {
self.runtime.read().available_locales().to_vec()
}
pub fn try_translate(&self, key: &str) -> Result<String, I18nError> {
self.try_translate_in(&self.language(), key)
}
pub fn try_translate_in(&self, locale: &str, key: &str) -> Result<String, I18nError> {
self.runtime.read().translate(locale, key)
}
fn unwrap_translation(key: &str, result: Result<String, I18nError>) -> String {
result.unwrap_or_else(|error| panic!("Failed to translate {key}: {error}"))
}
#[must_use]
pub fn translate(&self, key: &str) -> String {
Self::unwrap_translation(key, self.try_translate(key))
}
#[must_use]
pub fn translate_in(&self, locale: &str, key: &str) -> String {
Self::unwrap_translation(key, self.try_translate_in(locale, key))
}
pub fn try_translate_with_args(
&self,
key: &str,
patterns: &[&str],
values: &[String],
) -> Result<String, I18nError> {
self.try_translate_with_args_in(&self.language(), key, patterns, values)
}
pub fn try_translate_with_args_in(
&self,
locale: &str,
key: &str,
patterns: &[&str],
values: &[String],
) -> Result<String, I18nError> {
self.runtime
.read()
.translate_with_args(locale, key, patterns, values)
}
pub fn try_translate_kv_args(
&self,
key: &str,
args: &[(&str, String)],
) -> Result<String, I18nError> {
self.try_translate_kv_args_in(&self.language(), key, args)
}
pub fn try_translate_kv_args_in(
&self,
locale: &str,
key: &str,
args: &[(&str, String)],
) -> Result<String, I18nError> {
self.runtime
.read()
.translate_with_kv_args(locale, key, args)
}
#[must_use]
pub fn translate_with_args(&self, key: &str, patterns: &[&str], values: &[String]) -> String {
Self::unwrap_translation(key, self.try_translate_with_args(key, patterns, values))
}
#[must_use]
pub fn translate_with_args_in(
&self,
locale: &str,
key: &str,
patterns: &[&str],
values: &[String],
) -> String {
Self::unwrap_translation(
key,
self.try_translate_with_args_in(locale, key, patterns, values),
)
}
#[must_use]
pub fn translate_kv_args(&self, key: &str, args: &[(&str, String)]) -> String {
Self::unwrap_translation(key, self.try_translate_kv_args(key, args))
}
#[must_use]
pub fn translate_kv_args_in(&self, locale: &str, key: &str, args: &[(&str, String)]) -> String {
Self::unwrap_translation(key, self.try_translate_kv_args_in(locale, key, args))
}
}
#[cfg(test)]
mod tests {
use dioxus::prelude::*;
use crate::I18nSession;
use crate::runtime::test_runtime;
#[test]
fn locale_switch_updates_translation_state() {
let runtime = test_runtime();
let mut dom = VirtualDom::new(|| rsx! { div {} });
let _ = dom.rebuild_to_vec();
dom.in_scope(ScopeId::APP, || {
let mut i18n = I18nSession::new(runtime);
assert_eq!(i18n.translate("greeting"), "Hello");
i18n.set_language("zh-CN");
assert_eq!(i18n.translate("greeting"), "你好");
});
}
}