yew_i18n/
lib.rs

1#![doc(
2    html_logo_url = "https://github.com/next-rs/yew-i18n/assets/62179149/a46bdd6b-2d70-4fa6-ae95-02f6303ec7a3",
3    html_favicon_url = "https://github.com/next-rs/yew-i18n/assets/62179149/eaaebb8f-95bb-45ab-87e8-f278b583a0c7"
4)]
5
6//! # Yew I18n - Documentation
7//!
8//! Welcome to the official Yew I18n documentation. This library
9//! provides internationalization support for your Yew applications.
10//!
11//! ## Usage
12//!
13//! To use the Yew I18n library, add the following dependency to your `Cargo.toml` file:
14//!
15//! ```sh
16//! cargo add yew-i18n
17//! ```
18//!
19//! To integrate the library into your Yew application, you can use the `I18nProvider` component.
20//! Here's a simple example of how to use it:
21//!
22//! ```rust,no_run
23//! use yew::prelude::*;
24//! use std::collections::HashMap;
25//! use yew_i18n::{I18nProvider, YewI18nProviderConfig, use_translation};
26//!
27//! // Your Yew component structure here...
28//!
29//! #[function_component]
30//! pub fn MyI18nComponent() -> Html {
31//!     // Your component logic here...
32//!
33//!     let translation = use_translation();
34//!
35//!     html! {
36//!         <I18nProvider
37//!             supported_languages={vec!["en", "fr"]}
38//!             translations={HashMap::new()}
39//!         >
40//!             <div />
41//!             // Your components that need translation here...
42//!         </I18nProvider>
43//!     }
44//! }
45//! ```
46//!
47//! For more detailed information, check the [examples] provided in the library.
48//!
49//! [examples]: https://github.com/next-rs/yew-i18n/tree/main/examples
50//!
51//! ## Configuration
52//!
53//! Yew I18n allows you to configure the supported languages and translations through the
54//! `YewI18nProviderConfig` structure. You can also use the `YewI18n` struct to handle
55//! translation-related operations programmatically. Refer to the respective documentation
56//! for detailed configuration options.
57//!
58//! ```rust,no_run
59//! use yew::prelude::*;
60//! use yew_i18n::{YewI18nProviderConfig, YewI18nConfig, YewI18n, I18nProvider};
61//! use std::collections::HashMap;
62//!
63//! let i18n_provider_config = YewI18nProviderConfig {
64//!     supported_languages: vec!["en", "fr"],
65//!     translations: HashMap::new(),
66//!     children: html! { /* Your child components here... */ },
67//! };
68//!
69//! let i18n_provider_component = html! {
70//!     <I18nProvider ..i18n_provider_config />
71//! };
72//!
73//! let supported_languages = vec!["en", "fr"];
74//! let translations = HashMap::new();
75//!
76//! let i18n = YewI18n::new(YewI18nConfig { supported_languages, translations: translations.clone()}, translations);
77//! assert!(i18n.is_ok());
78//! ```
79//!
80//! ## Translation
81//!
82//! Yew I18n provides a hook function `use_translation` to easily access the translation context
83//! within your components. You can use this hook to retrieve translations for keys in your
84//! components.
85//!
86//! ```rust,no_run
87//! use yew::prelude::*;
88//! use yew_i18n::use_translation;
89//!
90//! #[function_component]
91//! pub fn MyTranslatableComponent() -> Html {
92//!     // Your component logic here...
93//!
94//!     let i18n = use_translation();
95//!     let greeting = i18n.t("greeting");
96//!
97//!     html! {
98//!         <div>{ greeting }</div>
99//!     }
100//! }
101//! ```
102//!
103//! ## Contribution
104//!
105//! If you encounter any issues or have suggestions for improvements, feel free to contribute
106//! to the [GitHub repository](https://github.com/next-rs/yew-i18n). We appreciate your feedback
107//! and involvement in making Yew I18n better!
108//!
109//! ## Acknowledgments
110//!
111//! Special thanks to the Yew community and contributors for such an amazing framework.
112//!
113
114use serde_json::Value;
115use std::collections::HashMap;
116use yew::prelude::*;
117
118/// Configuration for the YewI18n module, specifying supported languages and translations.
119#[derive(Debug, Clone, PartialEq)]
120pub struct YewI18nConfig {
121    /// List of supported languages in the application.
122    pub supported_languages: Vec<&'static str>,
123    /// Translations for different languages, represented as a mapping from language codes to JSON values.
124    pub translations: HashMap<String, serde_json::Value>,
125}
126
127/// Configuration for the YewI18nProvider component.
128#[derive(Debug, Clone, PartialEq, Properties)]
129pub struct YewI18nProviderConfig {
130    /// List of supported languages. Defaults to English and French if not specified.
131    #[prop_or_else(|| vec!["en", "fr"])]
132    pub supported_languages: Vec<&'static str>,
133    /// Translations for different languages, represented as a mapping from language codes to JSON values.
134    #[prop_or_default]
135    pub translations: HashMap<String, serde_json::Value>,
136    /// The child components to be wrapped with the YewI18n context.
137    pub children: Html,
138}
139
140/// The YewI18n struct representing the state and methods for internationalization.
141#[derive(Clone, PartialEq)]
142pub struct YewI18n {
143    /// Configuration for YewI18n, specifying supported languages and translations.
144    pub config: YewI18nConfig,
145    /// The current language code for translations.
146    current_language: String,
147    /// Translations for different languages, represented as a mapping from language codes to JSON values.
148    translations: HashMap<String, serde_json::Value>,
149}
150
151impl YewI18n {
152    /// Creates a new instance of YewI18n.
153    ///
154    /// # Arguments
155    ///
156    /// * `config` - Configuration for YewI18n.
157    /// * `translations` - Translations for different languages.
158    ///
159    /// # Returns
160    ///
161    /// A Result containing the initialized YewI18n instance or an error message.
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// use yew_i18n::{YewI18n, YewI18nConfig};
167    /// use std::collections::HashMap;
168    ///
169    /// let supported_languages = vec!["en", "fr"];
170    /// let translations = HashMap::new();
171    ///
172    /// let result = YewI18n::new(YewI18nConfig { supported_languages, translations: translations.clone()}, translations);
173    /// assert!(result.is_ok());
174    /// ```
175    pub fn new(
176        config: YewI18nConfig,
177        translations: HashMap<String, serde_json::Value>,
178    ) -> Result<Self, String> {
179        let current_language = config
180            .supported_languages
181            .get(0)
182            .cloned()
183            .ok_or_else(|| "You must add at least one supported language".to_string())?;
184
185        Ok(YewI18n {
186            config,
187            current_language: current_language.to_string(),
188            translations,
189        })
190    }
191
192    /// Sets the current language for translations.
193    ///
194    /// # Arguments
195    ///
196    /// * `language` - The language code to set.
197    ///
198    /// # Returns
199    ///
200    /// A Result indicating success or an error message if the language is not supported.
201    ///
202    /// # Examples
203    ///
204    /// ```
205    /// use yew_i18n::{YewI18n, YewI18nConfig};
206    /// use std::collections::HashMap;
207    ///
208    /// let supported_languages = vec!["en", "fr"];
209    /// let translations = HashMap::new();
210    ///
211    /// let mut i18n = YewI18n::new(YewI18nConfig { supported_languages, translations: translations.clone()}, translations).unwrap();
212    /// assert!(i18n.set_translation_language("fr").is_ok());
213    /// ```
214    pub fn set_translation_language(&mut self, language: &str) -> Result<(), String> {
215        if self.config.supported_languages.contains(&&language) {
216            self.current_language = language.to_string();
217            Ok(())
218        } else {
219            Err(format!("Language '{}' is not supported", language))
220        }
221    }
222
223    /// Retrieves a translated string for a given key.
224    ///
225    /// # Arguments
226    ///
227    /// * `key` - The translation key.
228    ///
229    /// # Returns
230    ///
231    /// The translated string or an error message if the key is not found.
232    ///
233    /// # Examples
234    ///
235    /// ```
236    /// use yew_i18n::{YewI18n, YewI18nConfig};
237    /// use std::collections::HashMap;
238    /// use serde_json::json;
239    ///
240    /// let supported_languages = vec!["en", "fr"];
241    /// let mut translations = HashMap::new();
242    /// translations.insert("en".to_string(), json!({ "greeting": "Hello" }));
243    /// translations.insert("fr".to_string(), json!({ "greeting": "Bonjour" }));
244    ///
245    /// let i18n = YewI18n::new(YewI18nConfig { supported_languages, translations: translations.clone()}, translations).unwrap();
246    /// assert_eq!(i18n.t("greeting"), "Hello");
247    /// ```
248    pub fn t(&self, key: &str) -> String {
249        self.translations
250            .get(&self.current_language)
251            .and_then(|language_json| language_json.get(key))
252            .map_or_else(
253                || {
254                    Err(format!(
255                        "Unable to find the key '{}' in the language '{}'",
256                        key, self.current_language
257                    ))
258                },
259                |value| match value {
260                    Value::String(s) => Ok(s.clone()),
261                    _ => Ok(value.to_string()),
262                },
263            )
264            .unwrap_or_else(|err| err)
265    }
266}
267
268/// Yew component for providing the YewI18n context to its children.
269#[function_component(I18nProvider)]
270pub fn i18n_provider(props: &YewI18nProviderConfig) -> Html {
271    let i18n = YewI18n::new(
272        YewI18nConfig {
273            supported_languages: props.supported_languages.clone(),
274            translations: props.translations.clone(),
275        },
276        props.translations.clone(),
277    )
278    .expect("Failed to initialize YewI18n");
279
280    let ctx = use_state(|| i18n);
281
282    html! {
283        <ContextProvider<YewI18n> context={(*ctx).clone()}>{ props.children.clone() }</ContextProvider<YewI18n>>
284    }
285}
286
287#[hook]
288pub fn use_translation() -> YewI18n {
289    use_context::<YewI18n>().expect("No I18n context provided")
290}