iced_code_editor/
i18n.rs

1//! Internationalization support for the code editor.
2//!
3//! This module provides translation support for UI text in the search dialog.
4//! Currently supports English, French, and Spanish.
5//!
6//! # Using rust-i18n
7//!
8//! The translations are available in YAML files in the `locales` directory.
9//! The `rust-i18n` crate is integrated and can be used directly via the `t!` macro:
10//!
11//! ```ignore
12//! use iced_code_editor::t;
13//!
14//! // Use translations directly
15//! let text = t!("search.placeholder");
16//! ```
17
18/// Supported languages for the code editor UI.
19///
20/// # Examples
21///
22/// ```
23/// use iced_code_editor::Language;
24///
25/// let lang = Language::English;
26/// assert_eq!(lang, Language::default());
27/// ```
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
29pub enum Language {
30    /// English language
31    #[default]
32    English,
33    /// French language
34    French,
35    /// Spanish language
36    Spanish,
37    /// German language
38    German,
39    /// Italian language
40    Italian,
41    /// Portuguese (Brazilian) language
42    PortugueseBR,
43    /// Portuguese (European) language
44    PortuguesePT,
45    /// Simplified Chinese language
46    ChineseSimplified,
47}
48
49impl Language {
50    /// Returns the locale code for this language.
51    ///
52    /// # Examples
53    ///
54    /// ```
55    /// use iced_code_editor::Language;
56    ///
57    /// assert_eq!(Language::English.to_locale(), "en");
58    /// assert_eq!(Language::French.to_locale(), "fr");
59    /// assert_eq!(Language::Spanish.to_locale(), "es");
60    /// assert_eq!(Language::German.to_locale(), "de");
61    /// assert_eq!(Language::Italian.to_locale(), "it");
62    /// assert_eq!(Language::PortugueseBR.to_locale(), "pt-BR");
63    /// assert_eq!(Language::PortuguesePT.to_locale(), "pt-PT");
64    /// ```
65    #[must_use]
66    pub const fn to_locale(self) -> &'static str {
67        match self {
68            Self::English => "en",
69            Self::French => "fr",
70            Self::Spanish => "es",
71            Self::German => "de",
72            Self::Italian => "it",
73            Self::PortugueseBR => "pt-BR",
74            Self::PortuguesePT => "pt-PT",
75            Self::ChineseSimplified => "zh-CN",
76        }
77    }
78}
79
80/// Provides translated text strings for UI elements.
81///
82/// This struct contains all UI text translations used in the search dialog,
83/// including placeholders, tooltips, and labels.
84///
85/// # Examples
86///
87/// ```
88/// use iced_code_editor::{Language, Translations};
89///
90/// let translations = Translations::new(Language::French);
91/// assert_eq!(translations.search_placeholder(), "Rechercher...");
92/// ```
93#[derive(Debug, Clone, Copy, Default)]
94pub struct Translations {
95    language: Language,
96}
97
98impl Translations {
99    /// Creates a new `Translations` instance with the specified language.
100    ///
101    /// This sets the global rust-i18n locale to the specified language.
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// use iced_code_editor::{Language, Translations};
107    ///
108    /// let translations = Translations::new(Language::Spanish);
109    /// assert_eq!(translations.language(), Language::Spanish);
110    /// ```
111    #[must_use]
112    pub fn new(language: Language) -> Self {
113        rust_i18n::set_locale(language.to_locale());
114        Self { language }
115    }
116
117    /// Returns the current language.
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// use iced_code_editor::{Language, Translations};
123    ///
124    /// let translations = Translations::new(Language::French);
125    /// assert_eq!(translations.language(), Language::French);
126    /// ```
127    #[must_use]
128    pub const fn language(&self) -> Language {
129        self.language
130    }
131
132    /// Sets the language for translations.
133    ///
134    /// This updates the global rust-i18n locale.
135    ///
136    /// # Examples
137    ///
138    /// ```
139    /// use iced_code_editor::{Language, Translations};
140    ///
141    /// let mut translations = Translations::new(Language::English);
142    /// translations.set_language(Language::Spanish);
143    /// assert_eq!(translations.language(), Language::Spanish);
144    /// ```
145    pub fn set_language(&mut self, language: Language) {
146        self.language = language;
147        rust_i18n::set_locale(language.to_locale());
148    }
149
150    /// Returns the placeholder text for the search input field.
151    ///
152    /// # Examples
153    ///
154    /// ```
155    /// use iced_code_editor::{Language, Translations};
156    ///
157    /// let en = Translations::new(Language::English);
158    /// assert_eq!(en.search_placeholder(), "Search...");
159    ///
160    /// let fr = Translations::new(Language::French);
161    /// assert_eq!(fr.search_placeholder(), "Rechercher...");
162    /// ```
163    #[must_use]
164    pub fn search_placeholder(&self) -> String {
165        rust_i18n::t!("search.placeholder", locale = self.language.to_locale())
166            .into_owned()
167    }
168
169    /// Returns the placeholder text for the replace input field.
170    ///
171    /// # Examples
172    ///
173    /// ```
174    /// use iced_code_editor::{Language, Translations};
175    ///
176    /// let es = Translations::new(Language::Spanish);
177    /// assert_eq!(es.replace_placeholder(), "Reemplazar...");
178    /// ```
179    #[must_use]
180    pub fn replace_placeholder(&self) -> String {
181        rust_i18n::t!("replace.placeholder", locale = self.language.to_locale())
182            .into_owned()
183    }
184
185    /// Returns the label text for the case sensitive checkbox.
186    ///
187    /// # Examples
188    ///
189    /// ```
190    /// use iced_code_editor::{Language, Translations};
191    ///
192    /// let fr = Translations::new(Language::French);
193    /// assert_eq!(fr.case_sensitive_label(), "Sensible à la casse");
194    /// ```
195    #[must_use]
196    pub fn case_sensitive_label(&self) -> String {
197        rust_i18n::t!(
198            "settings.case_sensitive_label",
199            locale = self.language.to_locale()
200        )
201        .into_owned()
202    }
203
204    /// Returns the tooltip text for the previous match button.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// use iced_code_editor::{Language, Translations};
210    ///
211    /// let en = Translations::new(Language::English);
212    /// assert_eq!(en.previous_match_tooltip(), "Previous match (Shift+F3)");
213    /// ```
214    #[must_use]
215    pub fn previous_match_tooltip(&self) -> String {
216        rust_i18n::t!(
217            "search.previous_match_tooltip",
218            locale = self.language.to_locale()
219        )
220        .into_owned()
221    }
222
223    /// Returns the tooltip text for the next match button.
224    ///
225    /// # Examples
226    ///
227    /// ```
228    /// use iced_code_editor::{Language, Translations};
229    ///
230    /// let es = Translations::new(Language::Spanish);
231    /// assert_eq!(es.next_match_tooltip(), "Siguiente coincidencia (F3 / Enter)");
232    /// ```
233    #[must_use]
234    pub fn next_match_tooltip(&self) -> String {
235        rust_i18n::t!(
236            "search.next_match_tooltip",
237            locale = self.language.to_locale()
238        )
239        .into_owned()
240    }
241
242    /// Returns the tooltip text for the close search dialog button.
243    ///
244    /// # Examples
245    ///
246    /// ```
247    /// use iced_code_editor::{Language, Translations};
248    ///
249    /// let fr = Translations::new(Language::French);
250    /// assert_eq!(fr.close_search_tooltip(), "Fermer la recherche (Échap)");
251    /// ```
252    #[must_use]
253    pub fn close_search_tooltip(&self) -> String {
254        rust_i18n::t!(
255            "search.close_tooltip",
256            locale = self.language.to_locale()
257        )
258        .into_owned()
259    }
260
261    /// Returns the tooltip text for the replace current match button.
262    ///
263    /// # Examples
264    ///
265    /// ```
266    /// use iced_code_editor::{Language, Translations};
267    ///
268    /// let en = Translations::new(Language::English);
269    /// assert_eq!(en.replace_current_tooltip(), "Replace current match");
270    /// ```
271    #[must_use]
272    pub fn replace_current_tooltip(&self) -> String {
273        rust_i18n::t!(
274            "replace.current_tooltip",
275            locale = self.language.to_locale()
276        )
277        .into_owned()
278    }
279
280    /// Returns the tooltip text for the replace all matches button.
281    ///
282    /// # Examples
283    ///
284    /// ```
285    /// use iced_code_editor::{Language, Translations};
286    ///
287    /// let es = Translations::new(Language::Spanish);
288    /// assert_eq!(es.replace_all_tooltip(), "Reemplazar todo");
289    /// ```
290    #[must_use]
291    pub fn replace_all_tooltip(&self) -> String {
292        rust_i18n::t!("replace.all_tooltip", locale = self.language.to_locale())
293            .into_owned()
294    }
295}
296
297#[cfg(test)]
298mod tests {
299    use super::*;
300
301    #[test]
302    fn test_default_language() {
303        let translations = Translations::default();
304        assert_eq!(translations.language(), Language::English);
305    }
306
307    #[test]
308    fn test_new_with_language() {
309        let translations = Translations::new(Language::French);
310        assert_eq!(translations.language(), Language::French);
311    }
312
313    #[test]
314    fn test_set_language() {
315        let mut translations = Translations::new(Language::English);
316        translations.set_language(Language::Spanish);
317        assert_eq!(translations.language(), Language::Spanish);
318    }
319
320    #[test]
321    fn test_english_translations() {
322        let t = Translations::new(Language::English);
323        assert_eq!(t.search_placeholder(), "Search...");
324        assert_eq!(t.replace_placeholder(), "Replace...");
325        assert_eq!(t.case_sensitive_label(), "Case sensitive");
326        assert_eq!(t.previous_match_tooltip(), "Previous match (Shift+F3)");
327        assert_eq!(t.next_match_tooltip(), "Next match (F3 / Enter)");
328        assert_eq!(t.close_search_tooltip(), "Close search dialog (Esc)");
329        assert_eq!(t.replace_current_tooltip(), "Replace current match");
330        assert_eq!(t.replace_all_tooltip(), "Replace all matches");
331    }
332
333    #[test]
334    fn test_french_translations() {
335        let t = Translations::new(Language::French);
336        assert_eq!(t.search_placeholder(), "Rechercher...");
337        assert_eq!(t.replace_placeholder(), "Remplacer...");
338        assert_eq!(t.case_sensitive_label(), "Sensible à la casse");
339        assert_eq!(t.previous_match_tooltip(), "Résultat précédent (Maj+F3)");
340        assert_eq!(t.next_match_tooltip(), "Résultat suivant (F3 / Entrée)");
341        assert_eq!(t.close_search_tooltip(), "Fermer la recherche (Échap)");
342        assert_eq!(
343            t.replace_current_tooltip(),
344            "Remplacer l'occurrence actuelle"
345        );
346        assert_eq!(t.replace_all_tooltip(), "Tout remplacer");
347    }
348
349    #[test]
350    fn test_spanish_translations() {
351        let t = Translations::new(Language::Spanish);
352        assert_eq!(t.search_placeholder(), "Buscar...");
353        assert_eq!(t.replace_placeholder(), "Reemplazar...");
354        assert_eq!(t.case_sensitive_label(), "Distinguir mayúsculas");
355        assert_eq!(
356            t.previous_match_tooltip(),
357            "Coincidencia anterior (Mayús+F3)"
358        );
359        assert_eq!(
360            t.next_match_tooltip(),
361            "Siguiente coincidencia (F3 / Enter)"
362        );
363        assert_eq!(t.close_search_tooltip(), "Cerrar búsqueda (Esc)");
364        assert_eq!(
365            t.replace_current_tooltip(),
366            "Reemplazar coincidencia actual"
367        );
368        assert_eq!(t.replace_all_tooltip(), "Reemplazar todo");
369    }
370
371    #[test]
372    fn test_german_translations() {
373        let t = Translations::new(Language::German);
374        assert_eq!(t.search_placeholder(), "Suchen...");
375        assert_eq!(t.replace_placeholder(), "Ersetzen...");
376        assert_eq!(t.case_sensitive_label(), "Groß-/Kleinschreibung");
377        assert_eq!(
378            t.previous_match_tooltip(),
379            "Vorheriger Treffer (Umschalt+F3)"
380        );
381        assert_eq!(t.next_match_tooltip(), "Nächster Treffer (F3 / Enter)");
382        assert_eq!(t.close_search_tooltip(), "Suchdialog schließen (Esc)");
383        assert_eq!(t.replace_current_tooltip(), "Aktuellen Treffer ersetzen");
384        assert_eq!(t.replace_all_tooltip(), "Alle ersetzen");
385    }
386
387    #[test]
388    fn test_italian_translations() {
389        let t = Translations::new(Language::Italian);
390        assert_eq!(t.search_placeholder(), "Cerca...");
391        assert_eq!(t.replace_placeholder(), "Sostituisci...");
392        assert_eq!(t.case_sensitive_label(), "Distingui maiuscole");
393        assert_eq!(
394            t.previous_match_tooltip(),
395            "Risultato precedente (Maiusc+F3)"
396        );
397        assert_eq!(t.next_match_tooltip(), "Risultato successivo (F3 / Invio)");
398        assert_eq!(
399            t.close_search_tooltip(),
400            "Chiudi finestra di ricerca (Esc)"
401        );
402        assert_eq!(
403            t.replace_current_tooltip(),
404            "Sostituisci risultato corrente"
405        );
406        assert_eq!(t.replace_all_tooltip(), "Sostituisci tutto");
407    }
408
409    #[test]
410    fn test_portuguese_br_translations() {
411        let t = Translations::new(Language::PortugueseBR);
412        assert_eq!(t.search_placeholder(), "Pesquisar...");
413        assert_eq!(t.replace_placeholder(), "Substituir...");
414        assert_eq!(t.case_sensitive_label(), "Diferenciar maiúsculas");
415        assert_eq!(
416            t.previous_match_tooltip(),
417            "Correspondência anterior (Shift+F3)"
418        );
419        assert_eq!(
420            t.next_match_tooltip(),
421            "Próxima correspondência (F3 / Enter)"
422        );
423        assert_eq!(
424            t.close_search_tooltip(),
425            "Fechar diálogo de pesquisa (Esc)"
426        );
427        assert_eq!(
428            t.replace_current_tooltip(),
429            "Substituir correspondência atual"
430        );
431        assert_eq!(t.replace_all_tooltip(), "Substituir tudo");
432    }
433
434    #[test]
435    fn test_portuguese_pt_translations() {
436        let t = Translations::new(Language::PortuguesePT);
437        assert_eq!(t.search_placeholder(), "Pesquisar...");
438        assert_eq!(t.replace_placeholder(), "Substituir...");
439        assert_eq!(t.case_sensitive_label(), "Diferenciar maiúsculas");
440        assert_eq!(
441            t.previous_match_tooltip(),
442            "Correspondência anterior (Shift+F3)"
443        );
444        assert_eq!(
445            t.next_match_tooltip(),
446            "Próxima correspondência (F3 / Enter)"
447        );
448        assert_eq!(
449            t.close_search_tooltip(),
450            "Fechar diálogo de pesquisa (Esc)"
451        );
452        assert_eq!(
453            t.replace_current_tooltip(),
454            "Substituir correspondência actual"
455        );
456        assert_eq!(t.replace_all_tooltip(), "Substituir tudo");
457    }
458
459    #[test]
460    fn test_language_switching() {
461        let mut t = Translations::new(Language::English);
462        assert_eq!(t.search_placeholder(), "Search...");
463
464        t.set_language(Language::French);
465        assert_eq!(t.search_placeholder(), "Rechercher...");
466
467        t.set_language(Language::Spanish);
468        assert_eq!(t.search_placeholder(), "Buscar...");
469
470        t.set_language(Language::German);
471        assert_eq!(t.search_placeholder(), "Suchen...");
472
473        t.set_language(Language::Italian);
474        assert_eq!(t.search_placeholder(), "Cerca...");
475
476        t.set_language(Language::PortugueseBR);
477        assert_eq!(t.search_placeholder(), "Pesquisar...");
478
479        t.set_language(Language::PortuguesePT);
480        assert_eq!(t.search_placeholder(), "Pesquisar...");
481
482        t.set_language(Language::ChineseSimplified);
483        assert_eq!(t.search_placeholder(), "搜索...");
484    }
485}