Skip to main content

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