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}