-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for localized validation errors and hints. Add an example…
… for testing localized validation support.
- Loading branch information
Showing
8 changed files
with
249 additions
and
44 deletions.
There are no files selected for viewing
27 changes: 27 additions & 0 deletions
27
examples/assets/localizations/en-US/validation-localized.ftl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
window-title = Localized validation example | ||
language-en-us = English (United States) | ||
language-es-es = Spanish (Spain) | ||
# | ||
# Translations specific to the example form | ||
# | ||
form-example-hinted-label = Hinted | ||
form-example-not-hinted-label = Not hinted | ||
# Specific validation reason | ||
form-input-invalid-non-whitespace-required = This field must have at least one non-whitespace character | ||
# | ||
# Translations applicable to all forms | ||
# | ||
|
||
# Hints | ||
form-hint-field-required = * Required | ||
# Generic buttons | ||
form-generic-submit-button = Submit | ||
form-generic-reset-button = Reset | ||
# Generic validation reasons | ||
form-generic-invalid-empty = This field cannot be empty. |
27 changes: 27 additions & 0 deletions
27
examples/assets/localizations/es-ES/validation-localized.ftl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
window-title = Ejemplo de validación localizada | ||
language-en-us = Inglés (Estados Unidos) | ||
language-es-es = Español (España) | ||
# | ||
# Traducciones específicas del formulario de ejemplo | ||
# | ||
form-example-hinted-label = Insinuado | ||
form-example-not-hinted-label = No insinuado | ||
# Razón de validación específica | ||
form-input-invalid-non-whitespace-required = Este campo debe tener al menos un carácter que no sea un espacio en blanco | ||
# | ||
# Traducciones aplicables a todos los formularios | ||
# | ||
|
||
# Sugerencias | ||
form-hint-field-required = * Requerido | ||
# Botones genéricos | ||
form-generic-submit-button = Enviar | ||
form-generic-reset-button = Restablecer | ||
# Razones genéricas de validación | ||
form-generic-invalid-empty = Este campo no puede estar vacío. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
//! An example that demonstrates localized validation messages and errors. | ||
//! | ||
//! Note how all the form elements, hints and validation error messages use `localize!` | ||
//! | ||
//! Refer to the `localization.rs` example for more details on how localization works in general. | ||
use unic_langid::LanguageIdentifier; | ||
use cushy::figures::units::Lp; | ||
use cushy::value::{Destination, Dynamic, IntoValue, Source, Validations, Value}; | ||
use cushy::widget::MakeWidget; | ||
use cushy::widgets::input::InputValue; | ||
use cushy::{localize, MaybeLocalized, Open, PendingApp}; | ||
use cushy::localization::Localization; | ||
|
||
// This example is based on the `validation.rs` example and should be kept in sync with it. | ||
|
||
fn form() -> impl MakeWidget { | ||
let text = Dynamic::default(); | ||
let validations = Validations::default(); | ||
|
||
localize!("form-example-hinted-label") | ||
.and( | ||
text.to_input() | ||
.validation(validations.validate(&text, validate_input)) | ||
.hint(localize!("form-hint-field-required")), | ||
) | ||
.and(localize!("form-example-not-hinted-label")) | ||
.and( | ||
text.to_input() | ||
.validation(validations.validate(&text, validate_input)), | ||
) | ||
.and( | ||
localize!("form-generic-submit-button") | ||
.into_button() | ||
.on_click(validations.clone().when_valid(move |_| { | ||
|
||
// Note: This is non-localized string is for developers, not users of the UI. | ||
println!( | ||
"Success! This callback only happens when all associated validations are valid" | ||
); | ||
})), | ||
) | ||
.and(localize!("form-generic-reset-button").into_button().on_click(move |_| { | ||
let _value = text.take(); | ||
validations.reset(); | ||
})) | ||
.into_rows() | ||
.pad() | ||
.width(Lp::inches(6)) | ||
.centered() | ||
.make_widget() | ||
} | ||
|
||
#[allow(clippy::ptr_arg)] // Changing &String to &str breaks type inference | ||
fn validate_input(input: &String) -> Result<(), Value<MaybeLocalized>> { | ||
if input.is_empty() { | ||
Err(localize!("form-generic-invalid-empty").into_value()) | ||
} else if input.trim().is_empty() { | ||
Err(localize!("form-input-invalid-non-whitespace-required").into_value()) | ||
} else { | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[derive(Default, Eq, PartialEq, Debug, Clone, Copy)] | ||
pub enum LanguageChoices { | ||
#[default] | ||
EnUs, | ||
EsEs, | ||
} | ||
|
||
impl LanguageChoices { | ||
pub fn to_locale(&self) -> LanguageIdentifier { | ||
match self { | ||
LanguageChoices::EnUs => "en-US".parse().unwrap(), | ||
LanguageChoices::EsEs => "es-ES".parse().unwrap(), | ||
} | ||
} | ||
} | ||
|
||
#[cushy::main] | ||
fn main(app: &mut PendingApp) -> cushy::Result { | ||
app.cushy().localizations().add_default( | ||
Localization::for_language( | ||
"en-US", | ||
include_str!("assets/localizations/en-US/validation-localized.ftl"), | ||
) | ||
.expect("valid language id"), | ||
); | ||
app.cushy().localizations().add( | ||
Localization::for_language( | ||
"es-ES", | ||
include_str!("assets/localizations/es-ES/validation-localized.ftl"), | ||
) | ||
.expect("valid language id"), | ||
); | ||
|
||
let dynamic_locale: Dynamic<LanguageChoices> = Dynamic::default(); | ||
|
||
ui(&dynamic_locale) | ||
.localized_in(dynamic_locale.map_each(LanguageChoices::to_locale)) | ||
.into_window() | ||
.titled(localize!("window-title")) | ||
.open(app)?; | ||
|
||
Ok(()) | ||
} | ||
|
||
fn language_selector(dynamic_locale: &Dynamic<LanguageChoices>) -> impl MakeWidget { | ||
let dynamic_language_selector = dynamic_locale | ||
.new_radio(LanguageChoices::EnUs) | ||
.labelled_by(localize!("language-en-us")) | ||
.and( | ||
dynamic_locale | ||
.new_radio(LanguageChoices::EsEs) | ||
.labelled_by(localize!("language-es-es")), | ||
) | ||
.into_rows() | ||
.contain(); | ||
|
||
dynamic_language_selector | ||
.make_widget() | ||
} | ||
|
||
fn ui(dynamic_locale: &Dynamic<LanguageChoices>) -> impl MakeWidget { | ||
language_selector(dynamic_locale) | ||
.and(form()) | ||
.into_rows() | ||
.centered() | ||
.make_widget() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.