Prevent combined translations in strings.json (#91334)
This commit is contained in:
parent
42b0602190
commit
d26160a509
1 changed files with 49 additions and 31 deletions
|
@ -22,6 +22,7 @@ REMOVED = 2
|
||||||
|
|
||||||
RE_REFERENCE = r"\[\%key:(.+)\%\]"
|
RE_REFERENCE = r"\[\%key:(.+)\%\]"
|
||||||
RE_TRANSLATION_KEY = re.compile(r"^(?!.+[_-]{2})(?![_-])[a-z0-9-_]+(?<![_-])$")
|
RE_TRANSLATION_KEY = re.compile(r"^(?!.+[_-]{2})(?![_-])[a-z0-9-_]+(?<![_-])$")
|
||||||
|
RE_COMBINED_REFERENCE = re.compile(r"(.+\[%)|(%\].+)")
|
||||||
|
|
||||||
# Only allow translation of integration names if they contain non-brand names
|
# Only allow translation of integration names if they contain non-brand names
|
||||||
ALLOW_NAME_TRANSLATION = {
|
ALLOW_NAME_TRANSLATION = {
|
||||||
|
@ -116,6 +117,18 @@ def translation_key_validator(value: str) -> str:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def translation_value_validator(value: Any) -> str:
|
||||||
|
"""Validate that the value is a valid translation.
|
||||||
|
|
||||||
|
- prevents string with HTML
|
||||||
|
- prevents combined translations
|
||||||
|
"""
|
||||||
|
value = cv.string_with_no_html(value)
|
||||||
|
if RE_COMBINED_REFERENCE.search(value):
|
||||||
|
raise vol.Invalid("the string should not contain combined translations")
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
|
||||||
def gen_data_entry_schema(
|
def gen_data_entry_schema(
|
||||||
*,
|
*,
|
||||||
config: Config,
|
config: Config,
|
||||||
|
@ -127,24 +140,24 @@ def gen_data_entry_schema(
|
||||||
"""Generate a data entry schema."""
|
"""Generate a data entry schema."""
|
||||||
step_title_class = vol.Required if require_step_title else vol.Optional
|
step_title_class = vol.Required if require_step_title else vol.Optional
|
||||||
schema = {
|
schema = {
|
||||||
vol.Optional("flow_title"): cv.string_with_no_html,
|
vol.Optional("flow_title"): translation_value_validator,
|
||||||
vol.Required("step"): {
|
vol.Required("step"): {
|
||||||
str: {
|
str: {
|
||||||
step_title_class("title"): cv.string_with_no_html,
|
step_title_class("title"): translation_value_validator,
|
||||||
vol.Optional("description"): cv.string_with_no_html,
|
vol.Optional("description"): translation_value_validator,
|
||||||
vol.Optional("data"): {str: cv.string_with_no_html},
|
vol.Optional("data"): {str: translation_value_validator},
|
||||||
vol.Optional("data_description"): {str: cv.string_with_no_html},
|
vol.Optional("data_description"): {str: translation_value_validator},
|
||||||
vol.Optional("menu_options"): {str: cv.string_with_no_html},
|
vol.Optional("menu_options"): {str: translation_value_validator},
|
||||||
vol.Optional("submit"): cv.string_with_no_html,
|
vol.Optional("submit"): translation_value_validator,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
vol.Optional("error"): {str: cv.string_with_no_html},
|
vol.Optional("error"): {str: translation_value_validator},
|
||||||
vol.Optional("abort"): {str: cv.string_with_no_html},
|
vol.Optional("abort"): {str: translation_value_validator},
|
||||||
vol.Optional("progress"): {str: cv.string_with_no_html},
|
vol.Optional("progress"): {str: translation_value_validator},
|
||||||
vol.Optional("create_entry"): {str: cv.string_with_no_html},
|
vol.Optional("create_entry"): {str: translation_value_validator},
|
||||||
}
|
}
|
||||||
if flow_title == REQUIRED:
|
if flow_title == REQUIRED:
|
||||||
schema[vol.Required("title")] = cv.string_with_no_html
|
schema[vol.Required("title")] = translation_value_validator
|
||||||
elif flow_title == REMOVED:
|
elif flow_title == REMOVED:
|
||||||
schema[vol.Optional("title", msg=REMOVED_TITLE_MSG)] = partial(
|
schema[vol.Optional("title", msg=REMOVED_TITLE_MSG)] = partial(
|
||||||
removed_title_validator, config, integration
|
removed_title_validator, config, integration
|
||||||
|
@ -201,7 +214,7 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema:
|
||||||
"""Generate a strings schema."""
|
"""Generate a strings schema."""
|
||||||
return vol.Schema(
|
return vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Optional("title"): cv.string_with_no_html,
|
vol.Optional("title"): translation_value_validator,
|
||||||
vol.Optional("config"): gen_data_entry_schema(
|
vol.Optional("config"): gen_data_entry_schema(
|
||||||
config=config,
|
config=config,
|
||||||
integration=integration,
|
integration=integration,
|
||||||
|
@ -220,40 +233,43 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema:
|
||||||
vol.Optional("selector"): cv.schema_with_slug_keys(
|
vol.Optional("selector"): cv.schema_with_slug_keys(
|
||||||
{
|
{
|
||||||
"options": cv.schema_with_slug_keys(
|
"options": cv.schema_with_slug_keys(
|
||||||
cv.string_with_no_html, slug_validator=translation_key_validator
|
translation_value_validator,
|
||||||
|
slug_validator=translation_key_validator,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
slug_validator=vol.Any("_", cv.slug),
|
slug_validator=vol.Any("_", cv.slug),
|
||||||
),
|
),
|
||||||
vol.Optional("device_automation"): {
|
vol.Optional("device_automation"): {
|
||||||
vol.Optional("action_type"): {str: cv.string_with_no_html},
|
vol.Optional("action_type"): {str: translation_value_validator},
|
||||||
vol.Optional("condition_type"): {str: cv.string_with_no_html},
|
vol.Optional("condition_type"): {str: translation_value_validator},
|
||||||
vol.Optional("trigger_type"): {str: cv.string_with_no_html},
|
vol.Optional("trigger_type"): {str: translation_value_validator},
|
||||||
vol.Optional("trigger_subtype"): {str: cv.string_with_no_html},
|
vol.Optional("trigger_subtype"): {str: translation_value_validator},
|
||||||
},
|
},
|
||||||
vol.Optional("system_health"): {
|
vol.Optional("system_health"): {
|
||||||
vol.Optional("info"): cv.schema_with_slug_keys(
|
vol.Optional("info"): cv.schema_with_slug_keys(
|
||||||
cv.string_with_no_html, slug_validator=translation_key_validator
|
translation_value_validator,
|
||||||
|
slug_validator=translation_key_validator,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
vol.Optional("config_panel"): cv.schema_with_slug_keys(
|
vol.Optional("config_panel"): cv.schema_with_slug_keys(
|
||||||
cv.schema_with_slug_keys(
|
cv.schema_with_slug_keys(
|
||||||
cv.string_with_no_html, slug_validator=translation_key_validator
|
translation_value_validator,
|
||||||
|
slug_validator=translation_key_validator,
|
||||||
),
|
),
|
||||||
slug_validator=vol.Any("_", cv.slug),
|
slug_validator=vol.Any("_", cv.slug),
|
||||||
),
|
),
|
||||||
vol.Optional("application_credentials"): {
|
vol.Optional("application_credentials"): {
|
||||||
vol.Optional("description"): cv.string_with_no_html,
|
vol.Optional("description"): translation_value_validator,
|
||||||
},
|
},
|
||||||
vol.Optional("issues"): {
|
vol.Optional("issues"): {
|
||||||
str: vol.All(
|
str: vol.All(
|
||||||
cv.has_at_least_one_key("description", "fix_flow"),
|
cv.has_at_least_one_key("description", "fix_flow"),
|
||||||
vol.Schema(
|
vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required("title"): cv.string_with_no_html,
|
vol.Required("title"): translation_value_validator,
|
||||||
vol.Exclusive(
|
vol.Exclusive(
|
||||||
"description", "fixable"
|
"description", "fixable"
|
||||||
): cv.string_with_no_html,
|
): translation_value_validator,
|
||||||
vol.Exclusive("fix_flow", "fixable"): gen_data_entry_schema(
|
vol.Exclusive("fix_flow", "fixable"): gen_data_entry_schema(
|
||||||
config=config,
|
config=config,
|
||||||
integration=integration,
|
integration=integration,
|
||||||
|
@ -268,14 +284,14 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema:
|
||||||
{
|
{
|
||||||
vol.Optional("name"): str,
|
vol.Optional("name"): str,
|
||||||
vol.Optional("state"): cv.schema_with_slug_keys(
|
vol.Optional("state"): cv.schema_with_slug_keys(
|
||||||
cv.string_with_no_html,
|
translation_value_validator,
|
||||||
slug_validator=translation_key_validator,
|
slug_validator=translation_key_validator,
|
||||||
),
|
),
|
||||||
vol.Optional("state_attributes"): cv.schema_with_slug_keys(
|
vol.Optional("state_attributes"): cv.schema_with_slug_keys(
|
||||||
{
|
{
|
||||||
vol.Optional("name"): str,
|
vol.Optional("name"): str,
|
||||||
vol.Optional("state"): cv.schema_with_slug_keys(
|
vol.Optional("state"): cv.schema_with_slug_keys(
|
||||||
cv.string_with_no_html,
|
translation_value_validator,
|
||||||
slug_validator=translation_key_validator,
|
slug_validator=translation_key_validator,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
@ -287,16 +303,16 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema:
|
||||||
vol.Optional("entity"): cv.schema_with_slug_keys(
|
vol.Optional("entity"): cv.schema_with_slug_keys(
|
||||||
cv.schema_with_slug_keys(
|
cv.schema_with_slug_keys(
|
||||||
{
|
{
|
||||||
vol.Optional("name"): cv.string_with_no_html,
|
vol.Optional("name"): translation_value_validator,
|
||||||
vol.Optional("state"): cv.schema_with_slug_keys(
|
vol.Optional("state"): cv.schema_with_slug_keys(
|
||||||
cv.string_with_no_html,
|
translation_value_validator,
|
||||||
slug_validator=translation_key_validator,
|
slug_validator=translation_key_validator,
|
||||||
),
|
),
|
||||||
vol.Optional("state_attributes"): cv.schema_with_slug_keys(
|
vol.Optional("state_attributes"): cv.schema_with_slug_keys(
|
||||||
{
|
{
|
||||||
vol.Optional("name"): cv.string_with_no_html,
|
vol.Optional("name"): translation_value_validator,
|
||||||
vol.Optional("state"): cv.schema_with_slug_keys(
|
vol.Optional("state"): cv.schema_with_slug_keys(
|
||||||
cv.string_with_no_html,
|
translation_value_validator,
|
||||||
slug_validator=translation_key_validator,
|
slug_validator=translation_key_validator,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
@ -386,7 +402,9 @@ def gen_platform_strings_schema(config: Config, integration: Integration) -> vol
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
ONBOARDING_SCHEMA = vol.Schema({vol.Required("area"): {str: cv.string_with_no_html}})
|
ONBOARDING_SCHEMA = vol.Schema(
|
||||||
|
{vol.Required("area"): {str: translation_value_validator}}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def validate_translation_file( # noqa: C901
|
def validate_translation_file( # noqa: C901
|
||||||
|
@ -415,7 +433,7 @@ def validate_translation_file( # noqa: C901
|
||||||
strings_schema = gen_strings_schema(config, integration).extend(
|
strings_schema = gen_strings_schema(config, integration).extend(
|
||||||
{
|
{
|
||||||
vol.Optional("device_class"): cv.schema_with_slug_keys(
|
vol.Optional("device_class"): cv.schema_with_slug_keys(
|
||||||
cv.string_with_no_html, slug_validator=vol.Any("_", cv.slug)
|
translation_value_validator, slug_validator=vol.Any("_", cv.slug)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Reference in a new issue