diff --git a/script/hassfest/translations.py b/script/hassfest/translations.py index a9612cf1943..111a8ce235b 100644 --- a/script/hassfest/translations.py +++ b/script/hassfest/translations.py @@ -21,6 +21,7 @@ REQUIRED = 1 REMOVED = 2 RE_REFERENCE = r"\[\%key:(.+)\%\]" +RE_TRANSLATION_KEY = re.compile(r"^(?!.+[_-]{2})(?![_-])[a-z0-9-_]+(? str: - """Validate value is lowercase.""" - if value.lower() != value: - raise vol.Invalid("Needs to be lowercase") +def translation_key_validator(value: str) -> str: + """Validate value is valid translation key.""" + if RE_TRANSLATION_KEY.match(value) is None: + raise vol.Invalid( + f"Invalid translation key '{value}', need to be [a-z0-9-_]+ and" + " cannot start or end with a hyphen or underscore." + ) return value @@ -221,7 +223,9 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema: vol.Optional("trigger_subtype"): {str: cv.string_with_no_html}, }, vol.Optional("state"): cv.schema_with_slug_keys( - cv.schema_with_slug_keys(str, slug_validator=lowercase_validator), + cv.schema_with_slug_keys( + cv.string_with_no_html, slug_validator=translation_key_validator + ), slug_validator=vol.Any("_", cv.slug), ), vol.Optional("state_attributes"): cv.schema_with_slug_keys( @@ -229,19 +233,22 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema: { vol.Optional("name"): str, vol.Optional("state"): cv.schema_with_slug_keys( - str, slug_validator=lowercase_validator + cv.string_with_no_html, + slug_validator=translation_key_validator, ), }, - slug_validator=lowercase_validator, + slug_validator=translation_key_validator, ), slug_validator=vol.Any("_", cv.slug), ), vol.Optional("system_health"): { - vol.Optional("info"): {str: cv.string_with_no_html} + vol.Optional("info"): cv.schema_with_slug_keys( + cv.string_with_no_html, slug_validator=translation_key_validator + ), }, vol.Optional("config_panel"): cv.schema_with_slug_keys( cv.schema_with_slug_keys( - cv.string_with_no_html, slug_validator=lowercase_validator + cv.string_with_no_html, slug_validator=translation_key_validator ), slug_validator=vol.Any("_", cv.slug), ), @@ -273,10 +280,16 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema: vol.Optional("state_attributes"): { str: { vol.Optional("name"): cv.string_with_no_html, - vol.Optional("state"): {str: cv.string_with_no_html}, + vol.Optional("state"): cv.schema_with_slug_keys( + cv.string_with_no_html, + slug_validator=translation_key_validator, + ), } }, - vol.Optional("state"): {str: cv.string_with_no_html}, + vol.Optional("state"): cv.schema_with_slug_keys( + cv.string_with_no_html, + slug_validator=translation_key_validator, + ), } } }, @@ -352,7 +365,7 @@ def gen_platform_strings_schema(config: Config, integration: Integration) -> vol return vol.Schema( { vol.Optional("state"): cv.schema_with_slug_keys( - cv.schema_with_slug_keys(str, slug_validator=lowercase_validator), + cv.schema_with_slug_keys(str, slug_validator=translation_key_validator), slug_validator=device_class_validator, ) }