diff --git a/src/tests/test_questions.py b/src/tests/test_questions.py index 1f8667adf..99ab9f156 100644 --- a/src/tests/test_questions.py +++ b/src/tests/test_questions.py @@ -447,7 +447,7 @@ class BaseTest: assert isinstance(option, OPTIONS[raw_option["type"]]) assert option.type == raw_option["type"] assert option.id == id_ - assert option.ask == {"en": id_} + assert option.ask == id_ assert option.readonly is (True if is_special_readonly_option else False) assert option.visible is True # assert option.bind is None @@ -493,7 +493,7 @@ class BaseTest: ) option, value = _fill_or_prompt_one_option(raw_option, None) - expected_message = option.ask["en"] + expected_message = option.ask choices = [] if isinstance(option, BaseChoicesOption): @@ -510,7 +510,7 @@ class BaseTest: prefill=prefill, is_multiline=option.type == "text", autocomplete=choices, - help=option.help["en"], + help=option.help, ) def test_scenarios(self, intake, expected_output, raw_option, data): @@ -558,7 +558,7 @@ class TestDisplayText(BaseTest): options, form = ask_questions_and_parse_answers( {_id: raw_option}, answers ) - assert stdout.getvalue() == f"{options[0].ask['en']}\n" + assert stdout.getvalue() == f"{options[0].ask}\n" # ╭───────────────────────────────────────────────────────╮ @@ -609,7 +609,7 @@ class TestAlert(TestDisplayText): options, form = ask_questions_and_parse_answers( {"display_text_id": raw_option}, answers ) - ask = options[0].ask["en"] + ask = options[0].ask if style in colors: color = colors[style] title = style.title() + (":" if style != "success" else "!") diff --git a/src/utils/form.py b/src/utils/form.py index cbe64b499..71954ee2b 100644 --- a/src/utils/form.py +++ b/src/utils/form.py @@ -297,15 +297,6 @@ class BaseOption(BaseModel): del schema["description"] schema["additionalProperties"] = False - @validator("ask", always=True) - def parse_or_set_default_ask( - cls, value: Union[Translation, None], values: Values - ) -> Translation: - if value is None: - return {"en": values["id"]} - if isinstance(value, str): - return {"en": value} - return value @validator("readonly", pre=True) def can_be_readonly(cls, value: bool, values: Values) -> bool: @@ -327,7 +318,9 @@ class BaseOption(BaseModel): return evaluate_simple_js_expression(self.visible, context=context) def _get_prompt_message(self, value: None) -> str: - return _value_for_locale(self.ask) + # force type to str + # `OptionsModel.translate_options()` should have been called before calling this method + return cast(str, self.ask) # ╭───────────────────────────────────────────────────────╮ @@ -367,7 +360,7 @@ class AlertOption(BaseReadonlyOption): State.danger: "red", } message = m18n.g(self.style) if self.style != State.danger else m18n.n("danger") - return f"{colorize(message, colors[self.style])} {_value_for_locale(self.ask)}" + return f"{colorize(message, colors[self.style])} {self.ask}" class ButtonOption(BaseReadonlyOption): @@ -624,6 +617,7 @@ class NumberOption(BaseInputOption): max: Union[int, None] = None step: Union[int, None] = None _annotation = int + _none_as_empty_str = False @staticmethod def normalize(value, option={}): @@ -1274,6 +1268,26 @@ class OptionsModel(BaseModel): def __init__(self, **kwargs) -> None: super().__init__(options=self.options_dict_to_list(kwargs)) + def translate_options(self, i18n_key: Union[str, None] = None): + """ + Mutate in place translatable attributes of options to their translations + """ + for option in self.options: + for key in ("ask", "help"): + if not hasattr(option, key): + continue + + value = getattr(option, key) + if value: + setattr(option, key, _value_for_locale(value)) + elif key == "ask" and m18n.key_exists(f"{i18n_key}_{option.id}"): + setattr(option, key, m18n.n(f"{i18n_key}_{option.id}")) + elif key == "help" and m18n.key_exists(f"{i18n_key}_{option.id}_help"): + setattr(option, key, m18n.n(f"{i18n_key}_{option.id}_help")) + elif key == "ask": + # FIXME warn? + option.ask = option.id + class FormModel(BaseModel): """ @@ -1384,7 +1398,7 @@ def prompt_or_validate_form( raise YunohostValidationError( "config_action_disabled", action=option.id, - help=_value_for_locale(option.help), + help=option.help, ) if not option.is_visible(context): @@ -1433,7 +1447,7 @@ def prompt_or_validate_form( prefill=value, is_multiline=isinstance(option, TextOption), autocomplete=choices, - help=_value_for_locale(option.help), + help=option.help, ) # Apply default value if none @@ -1512,6 +1526,7 @@ def ask_questions_and_parse_answers( # FIXME use YunohostError instead since it is not really a user mistake? raise YunohostValidationError(error, raw_msg=True) + model.translate_options() # Build the form from those questions and instantiate it without # parsing/validation (construct) since it may contains required questions. form = build_form(model.options).construct()