# hifis-surveyval
# Framework to help developing analysis scripts for the HIFIS Software survey.
#
# SPDX-FileCopyrightText: 2021 HIFIS Software <support@hifis.net>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Models survey elements that can be represented in multiple languages."""
from typing import Dict, List
from schema import And, Regex, Schema
from hifis_surveyval.models.mixins.yaml_constructable import (
YamlConstructable,
YamlDict,
)
[docs]class Translated(YamlConstructable):
"""
A wrapper around text instances with multiple translations.
Languages are identified by their ISO 693-1 two-letter codes.
Optionally an uppercase region identifier may be appended as well. Region
identifiers consist of a two or three letter code and must be separated by
a dash from the language identifier.
Examples for valid identifiers are `de`, `en-US` but not `frFR`.
"""
schema = Schema(
{
And(str, Regex("^[a-z]{2}(-[A-Z]{2,3})?$")): And(
str,
lambda s: s,
error="Translation must neither be empty nor None",
)
}
)
"""
The validation schema used for translation dictionaries.
* The dictionary may not be empty
* The key must exist, be a string and consist of two lower-case letters
* The value must be a string and must not be empty
"""
# TODO: Properly format this docstring
[docs] def __init__(self, translations: Dict[str, str]):
"""
Create a new instance of translated text.
Args:
translations:
A mapping from the language code to the translation in the
respective language. The dictionary must not be empty.
Keys are expected to be two-letter codes with an optional
region code, and values must neither be None nor be empty.
"""
self._translations = Translated.schema.validate(translations)
[docs] def available_languages(self) -> List[str]:
"""
Get all languages for which translations exist.
Returns:
A list of language codes
"""
return list(self._translations.keys())
[docs] def get_translation(self, language_code: str) -> str:
"""
Get the translation for a specific language.
If the passed in language code has a region specifier, and no
translation for this region is given, a translation with only the
language code will be looked up as a fallback.
Args:
language_code:
The ISO 693-1 two letter code for the language the text is
requested for. Optionally a dash followed by an uppercase
two or three letter region code may be appended as well.
Returns:
The text in the requested language or None if no translation for
this language exists.
Raises:
KeyError:
If no translation for the requested language (with or
without region code) can be found.
"""
try:
return self._translations[language_code]
except KeyError:
pass
try:
return self._translations[language_code[0:1]]
except KeyError:
raise KeyError(
f"No translation for {language_code} in {self._translations}"
)
@staticmethod
def _from_yaml_dictionary(yaml: YamlDict, **kwargs) -> "Translated":
"""
Construct a new Translated instance from the given data.
The validation of the dictionaries keys and values are done by the
Translated-constructor.
Args:
yaml:
The YamlDict representing the translations.
Keys are expected to be language and optional region codes and
values are supposed to be the appropriate translations.
Returns:
A new Translated-instance as given in the YAML data.
"""
return Translated(yaml)