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