From aa92420af8585d8a6c7a71881b1884ec14cf95a9 Mon Sep 17 00:00:00 2001 From: Felix Schwenzel Date: Mon, 22 Oct 2018 14:21:13 +0200 Subject: [PATCH 1/6] added translations for german (de_DE) --- README.md | 1 + action-owm.py | 2 +- snipsowm/sentence_generator.py | 153 ++++++++++++++++++--------------- snipsowm/snips.py | 53 ++++++++++++ snipsowm/weather_condition.py | 11 +++ 5 files changed, 150 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 1ea9cd3..0e2830f 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ To have the skills properly working, you **need** to generate locales for your l - 🇺🇸 `en_US` - 🇫🇷 `fr_FR` - 🇪🇸 `es_ES` +- `de_DE` You can generate them with `sudo raspi-config`. Going in the `Localisation Options` submenu, then in the `Change Locale` submenu, and selecting the locales you want to support. For instance, select `en_US UTF-8` if you want support for English. diff --git a/action-owm.py b/action-owm.py index da7101a..c5fdda5 100755 --- a/action-owm.py +++ b/action-owm.py @@ -188,7 +188,7 @@ def searchWeatherForecastItem(hermes, intent_message): if skill_locale == u"": print "No locale information is found!" - print "Please edit 'config.ini' file, give either en_US, fr_FR or es_ES refering to the language of your assistant" + print "Please edit 'config.ini' file, give either en_US, fr_FR, de_DE or es_ES refering to the language of your assistant" sys.exit(1) skill = SnipsOWM(config["secret"]["api_key"], diff --git a/snipsowm/sentence_generator.py b/snipsowm/sentence_generator.py index c5e30ad..b6b69f3 100644 --- a/snipsowm/sentence_generator.py +++ b/snipsowm/sentence_generator.py @@ -1,57 +1,62 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- -import datetime +import abc +import collections from enum import Enum import locale +import sentence_generation_utils as utils import random -class SentenceTone(Enum): - NEUTRAL = 0 - POSITIVE = 1 - NEGATIVE = 2 +class SentenceGenerator(object): + __metaclass__ = abc.ABCMeta + def __init__(self, locale="en_US"): + self.locale = locale -def date_to_string(date, granularity=0): - """ Convert a date to a string, with an appropriate level of - granularity. + def generate_error_sentence(self): + error_sentences = { + "en_US": "An error occured when trying to retrieve the weather, please try again", + "fr_FR": "Désolé, il y a eu une erreur lors de la récupération des données météo. Veuillez réessayer", + "es_ES": "Ha ocurrido un error obteniendo la información meteorológica, por favor inténtalo de nuevo", + "de_DE": "Da ist was schiefgelaufen beim Wetterdaten einlesen, versuche es bitte nochmal" + } - :param date: A datetime object. - :param granularity: Granularity for the desired textual representation. - 0: precise (date and time are returned) - 1: day (only the week day is returned) - 2: month (only year and month are returned) - 3: year (only year is returned) - :return: A textual representation of the date. - """ - if not date: - return "" + return error_sentences[self.locale] - if granularity == 0: - return date.strftime("%A") - elif granularity == 1: - return date.strftime("%A, %d") - elif granularity == 2: - return date.strftime("%A, %d %B") + def generate_error_locale_sentence(self): + """ + TODO : This method is too specific to be implemented by the SentenceGenerator class. + :return: + :rtype:basestring + """ + error_sentences = { + "en_US": "An error occured. Your system doesn't have the correct locale installed. Please refer to the documentation. ", + "fr_FR": "Désolé, il y a eu une erreur. Votre système n'a pas la locale correcte installée. Veuillez consulter la documentation pour plus de détails.", + "es_ES": "Ha ocurrido un error. Parece que el sistema no tiene instalado el paquete de idioma correcto ('locale'). Consula la documentación para más detalles.", + "de_DE": "Da ist was schiefgelaufen. Dieser Rechner hat nicht die korrekte Lokalisierung installiert. Bitte lese die Installationsanleitung." + } - return date.strftime("%A, %d %B, %H:%M%p") + return error_sentences[self.locale] + def generate_api_key_error_sentence(self): + error_sentences = { + "en_US": "The API key you provided is invalid, check your config.ini", + "fr_FR": "La clé API fournie est incorrecte, vérifiez le fichier config.ini", + "es_ES": "La clave de la API proporcionada no es válida, por favor comprueba tu fichero config.ini", + "de_DE": "Der eingetragene API-Schlüssel ist ungültig, bitte prüfe die Konfigurationsdatei" + } + return error_sentences[self.locale] -def french_is_masculine_word(word): - return word[len(word) - 1] not in ['é', 'e'] +class SimpleSentenceGenerator(SentenceGenerator): + pass -def starts_with_vowel(word): - return word[0] in ['a', 'e', 'i', 'o', 'u', 'y'] - -class SentenceGenerator(object): - def __init__(self, locale="en_US"): - """ - :param language: - :type language: Language - """ - self.locale = locale +class AnswerSentenceGenerator(SentenceGenerator): + class SentenceTone(Enum): + NEUTRAL = 0 + POSITIVE = 1 + NEGATIVE = 2 def generate_sentence_introduction(self, tone): """ @@ -64,19 +69,24 @@ def generate_sentence_introduction(self, tone): sentence_beginnings = { "en_US": { - SentenceTone.POSITIVE: "Yes,", - SentenceTone.NEGATIVE: "No,", - SentenceTone.NEUTRAL: "" + AnswerSentenceGenerator.SentenceTone.POSITIVE: "Yes,", + AnswerSentenceGenerator.SentenceTone.NEGATIVE: "No,", + AnswerSentenceGenerator.SentenceTone.NEUTRAL: "" }, "fr_FR": { - SentenceTone.POSITIVE: "Oui,", - SentenceTone.NEGATIVE: "Non,", - SentenceTone.NEUTRAL: "" + AnswerSentenceGenerator.SentenceTone.POSITIVE: "Oui,", + AnswerSentenceGenerator.SentenceTone.NEGATIVE: "Non,", + AnswerSentenceGenerator.SentenceTone.NEUTRAL: "" }, "es_ES": { - SentenceTone.POSITIVE: "Sí,", - SentenceTone.NEGATIVE: "No,", - SentenceTone.NEUTRAL: "" + AnswerSentenceGenerator.SentenceTone.POSITIVE: "Sí,", + AnswerSentenceGenerator.SentenceTone.NEGATIVE: "No,", + AnswerSentenceGenerator.SentenceTone.NEUTRAL: "" + }, + "de_DE": { + AnswerSentenceGenerator.SentenceTone.POSITIVE: "Ja,", + AnswerSentenceGenerator.SentenceTone.NEGATIVE: "Nein,", + AnswerSentenceGenerator.SentenceTone.NEUTRAL: "" } } @@ -97,13 +107,13 @@ def generate_sentence_locality(self, POI=None, Locality=None, Region=None, Count :rtype: basestring """ if self.locale == "en_US": - if POI or Locality or Region or Country: + if (POI or Locality or Region or Country): locality = filter(lambda x: x is not None, [POI, Locality, Region, Country])[0] return "in {}".format(locality) else: return "" - if self.locale == "fr_FR": + elif self.locale == "fr_FR": """ Country granularity: - We use "au" for masculine nouns that begins with a consonant @@ -128,12 +138,12 @@ def generate_sentence_locality(self, POI=None, Locality=None, Region=None, Count return "à {}".format(Locality) if Region: - if french_is_masculine_word(Region) and (not starts_with_vowel(Region)): + if utils.french_is_masculine_word(Region) and (not utils.starts_with_vowel(Region)): return "au {}".format(Region) else: return "en {}".format(Region) if Country: - if french_is_masculine_word(Country) and (not starts_with_vowel(Country)): + if utils.french_is_masculine_word(Country) and (not utils.starts_with_vowel(Country)): return "au {}".format(Country) else: return "en {}".format(Country) @@ -147,6 +157,13 @@ def generate_sentence_locality(self, POI=None, Locality=None, Region=None, Count else: return "" + elif self.locale == "de_DE": + if POI or Locality or Region or Country: + locality = filter(lambda x: x is not None, [POI, Locality, Region, Country])[0] + return "in {}".format(locality) + else: + return "" + else: return "" @@ -171,13 +188,16 @@ def generate_sentence_date(self, date, granularity=0): print "Careful! There was an error while trying to set the locale {}. This means your locale is not properly installed. Please refer to the README for more information.".format(full_locale) print "Some information displayed might not be formated to your locale" - return date_to_string(date, granularity) + return utils.date_to_string(date, granularity) + + +class ConditionQuerySentenceGenerator(AnswerSentenceGenerator): def generate_condition_description(self, condition_description): return condition_description if len(condition_description) > 0 else "" def generate_condition_sentence(self, - tone=SentenceTone.POSITIVE, + tone=AnswerSentenceGenerator.SentenceTone.POSITIVE, date=None, granularity=0, condition_description=None, POI=None, Locality=None, Region=None, Country=None): @@ -214,6 +234,8 @@ def generate_condition_sentence(self, parameters = filter(lambda x: not x is None and len(x) > 0, parameters) return ("{} " * len(parameters)).format(*parameters) + +class TemperatureQuerySentenceGenerator(AnswerSentenceGenerator): def generate_temperature_sentence(self, temperature="-273.15", date=None, granularity=0, @@ -236,7 +258,8 @@ def generate_temperature_sentence(self, error_sentences = { "en_US": "I couldn't fetch the right data for the specified place and date", "fr_FR": "Je n'ai pas pu récupérer les prévisions de température pour cet endroit et ces dates", - "es_ES": "No he podido encontrar información meteorológica para el lugar y la fecha especificados" + "es_ES": "No he podido encontrar información meteorológica para el lugar y la fecha especificados", + "de_DE": "Ich konnte nicht die richtigen Daten für den angegebenen Ort und das datum finden" } if (temperature is None): @@ -245,7 +268,8 @@ def generate_temperature_sentence(self, sentence_introductions = { "en_US": ["The temperature will be {} degrees"], "fr_FR": ["La température sera de {} degrés", "Il fera {} degrés"], - "es_ES": ["La temperatura será de {} grados", "Habrá {} grados"] + "es_ES": ["La temperatura será de {} grados", "Habrá {} grados"], + "de_DE": ["Die Temperatur wird {} Grad sein", "Es wird {} Grad"] } introduction = random.choice(sentence_introductions[self.locale]).format(temperature) @@ -257,19 +281,10 @@ def generate_temperature_sentence(self, parameters = (introduction,) + tuple(permutable_parameters) return "{} {} {}".format(*parameters) - def generate_error_sentence(self): - error_sentences = { - "en_US": "An error occured when trying to retrieve the weather, please try again", - "fr_FR": "Désolé, il y a eu une erreur lors de la récupération des données météo. Veuillez réessayer", - "es_ES": "Ha ocurrido un error obteniendo la información climática, por favor inténtalo de nuevo" - } - return error_sentences[self.locale] +class SentenceGenerationException(Exception): + pass - def generate_api_key_error_sentence(self): - error_sentences = { - "en_US": "The API key you provided is invalid, check your config.ini", - "fr_FR": "La clé API fournie est incorrecte, vérifiez le fichier config.ini", - "es_ES": "La clave de la API es incorrecta, por favor verifica tu fichero config.ini" - } - return error_sentences[self.locale] \ No newline at end of file + +class SentenceGenerationLocaleException(SentenceGenerationException): + pass \ No newline at end of file diff --git a/snipsowm/snips.py b/snipsowm/snips.py index a5f2c2a..e3d49b8 100644 --- a/snipsowm/snips.py +++ b/snipsowm/snips.py @@ -60,262 +60,315 @@ class SnipsWeatherConditions(Enum): mappings = { SnipsWeatherConditions.HUMID: { + 'de_DE': [u'feucht'], 'fr_FR': [u'humide'], 'en_US': [u'humid'], 'es_ES': [u'húmedo'] }, SnipsWeatherConditions.BLIZZARD: { + 'de_DE': [u'blizzard'], 'fr_FR': [u'blizzard'], 'en_US': [u'blizzard'], 'es_ES': [u'ventisca'] }, SnipsWeatherConditions.SNOWFALL: { + 'de_DE': [u'schneefall'], 'fr_FR': [u'chutes de neige'], 'en_US': [u'snowfall'], 'es_ES': [u'nieve'] }, SnipsWeatherConditions.WINDY: { + 'de_DE': [u'windig'], 'fr_FR': [u'venteux'], 'en_US': [u'windy'], 'es_ES': [u'viento'] }, SnipsWeatherConditions.CLOUD: { + 'de_DE': [u'wolken'], 'fr_FR': [u'nuage', u'nuages'], 'en_US': [u'cloud', u'clouds'], 'es_ES': [u'nuboso'] }, SnipsWeatherConditions.RAINY: { + 'de_DE': [u'regnerisch'], 'fr_FR': [u'pluvieux'], 'en_US': [u'rainy'], 'es_ES': [u'lluvioso'] }, SnipsWeatherConditions.STORMY: { + 'de_DE': [u'stürmisch'], 'fr_FR': [u'orageux'], 'en_US': [u'stormy'], 'es_ES': [u'tormentoso'] }, SnipsWeatherConditions.SUN: { + 'de_DE': [u'sonne'], 'fr_FR': [u'soleil'], 'en_US': [u'sun'], 'es_ES': [u'soleado'] }, SnipsWeatherConditions.SNOW: { + 'de_DE': [u'schnee'], 'fr_FR': [u'neige', u'neiger', u'neigera'], 'en_US': [u'snow'], 'es_ES': [u'nieve'] }, SnipsWeatherConditions.FOG: { + 'de_DE': [u'nebel'], 'fr_FR': [u'brouillard'], 'en_US': [u'fog'], 'es_ES': [u'niebla'] }, SnipsWeatherConditions.DEPRESSION: { + 'de_DE': [u'tiefdruck'], 'fr_FR': [u'dépression'], 'en_US': [u'depression'], 'es_ES': [u'depresión'] }, SnipsWeatherConditions.STORM: { + 'de_DE': [u'sturm'], 'fr_FR': [u'tempête'], 'en_US': [u'storm'], 'es_ES': [u'tormenta'] }, SnipsWeatherConditions.RAINFALL: { + 'de_DE': [u'regen'], 'fr_FR': [u'précipitations'], 'en_US': [u'rainfall'], 'es_ES': [u'precipitaciones'] }, SnipsWeatherConditions.SNOWY: { + 'de_DE': [u'verschneit'], 'fr_FR': [u'neigeux'], 'en_US': [u'snowy'], 'es_ES': [u'nevada'] }, SnipsWeatherConditions.SUNNY: { + 'de_DE': [u'sonnig'], 'fr_FR': [u'ensoleillé'], 'en_US': [u'sunny'], 'es_ES': [u'soleado'] }, SnipsWeatherConditions.RAIN: { + 'de_DE': [u'regen'], 'fr_FR': [u'pluie'], 'en_US': [u'rain'], 'es_ES': [u'lluvia'] }, SnipsWeatherConditions.HAIL: { + 'de_DE': [u'hagel'], 'fr_FR': [u'grêle'], 'en_US': [u'hail'], 'es_ES': [u'granizo'] }, SnipsWeatherConditions.FOGGY: { + 'de_DE': [u'nebelich'], 'fr_FR': [u'brumeux'], 'en_US': [u'foggy'], 'es_ES': [u'nebuloso'] }, SnipsWeatherConditions.OVERCAST: { + 'de_DE': [u'bewölkt'], 'fr_FR': [u'couvert'], 'en_US': [u'overcast'], 'es_ES': [u'cubierto'] }, SnipsWeatherConditions.CLOUDY: { + 'de_DE': [u'wolkig'], 'fr_FR': [u'nuageux'], 'en_US': [u'cloudy'], 'es_ES': [u'nublado'] }, SnipsWeatherConditions.HUMIDITY: { + 'de_DE': [u'feuchte'], 'fr_FR': [u'humidité'], 'en_US': [u'humidity'], 'es_ES': [u'humedad'] }, SnipsWeatherConditions.SNOWSTORM: { + 'de_DE': [u'schneesturm'], 'fr_FR': [u'tempête de neige'], 'en_US': [u'snowstorm'], 'es_ES': [u'tormenta de nieve'] }, SnipsWeatherConditions.WIND: { + 'de_DE': [u'wind'], 'fr_FR': [u'vent'], 'en_US': [u'wind'], 'es_ES': [u'viento'] }, SnipsWeatherConditions.TRENCH_COAT: { + 'de_DE': [u'mantel'], 'fr_FR': [u'trench'], 'en_US': [u'trench coat'], 'es_ES': [u'gabardina'] }, SnipsWeatherConditions.PARKA: { + 'de_DE': [u'anorak'], 'fr_FR': [u'parka'], 'en_US': [u'parka'], 'es_ES': [u'anorak'] }, SnipsWeatherConditions.CARDIGAN: { + 'de_DE': [u'strickjacke'], 'fr_FR': [u'cardigan'], 'en_US': [u'cardigan'], 'es_ES': [u'chaqueta'] }, SnipsWeatherConditions.SUMMER_CLOTHING: { + 'de_DE': [u'sommerkleidung'], 'fr_FR': [u'légé'], 'en_US': [u'summer clothing'], 'es_ES': [u'ropa de verano'] }, SnipsWeatherConditions.GAMP: { + 'de_DE': [u'regenschirm'], 'fr_FR': [u'parapluie'], 'en_US': [u'gamp'], 'es_ES': [u'paraguas'] }, SnipsWeatherConditions.BROLLY: { + 'de_DE': [u'regenschirm'], 'fr_FR': [u'parapluie'], 'en_US': [u'brolly'], 'es_ES': [u'paraguas'] }, SnipsWeatherConditions.SUNSHADE: { + 'de_DE': [u'sonnenschirm'], 'fr_FR': [u'parasol'], 'en_US': [u'sunshade'], 'es_ES': [u'sombrilla'] }, SnipsWeatherConditions.PARASOL: { + 'de_DE': [u'sonnenschirm'], 'fr_FR': [u'parasol'], 'en_US': [u'parasol'], 'es_ES': [u'sombrilla'] }, SnipsWeatherConditions.UMBRELLA: { + 'de_DE': [u'regenschirm'], 'fr_FR': [u'parapluie'], 'en_US': [u'umbrella'], 'es_ES': [u'paraguas'] }, SnipsWeatherConditions.OPEN_TOED_SHOES: { + 'de_DE': [u'sandalen'], 'fr_FR': [u'sandales'], 'en_US': [u'open toed shoes'], 'es_ES': [u'sandalias'] }, SnipsWeatherConditions.SHORTS: { + 'de_DE': [u'kurze hosen'], 'fr_FR': [u'short'], 'en_US': [u'shorts'], 'es_ES': [u'pantalones cortos'] }, SnipsWeatherConditions.SKIRT: { + 'de_DE': [u'rock'], 'fr_FR': [u'jupe'], 'en_US': [u'skirt'], 'es_ES': [u'falda'] }, SnipsWeatherConditions.WARM_JUMPER: { + 'de_DE': [u'pulli'], 'fr_FR': [u'jupe courte'], 'en_US': [u'warm jumper'], 'es_ES': [u'jersey cálido'] }, SnipsWeatherConditions.WARM_SOCKS: { + 'de_DE': [u'warme socken'], 'fr_FR': [u'chausettes chaudes'], 'en_US': [u'warm socks'], 'es_ES': [u'calcetines cálidos'] }, SnipsWeatherConditions.WARM_SWEATER: { + 'de_DE': [u'pullover'], 'fr_FR': [u'pull'], 'en_US': [u'warm sweater'], 'es_ES': [u'jersey cálido'] }, SnipsWeatherConditions.SCARF: { + 'de_DE': [u'schal'], 'fr_FR': [u'écharpe'], 'en_US': [u'scarf'], 'es_ES': [u'bufanda'] }, SnipsWeatherConditions.STRAW_HAT: { + 'de_DE': [u'strohhut'], 'fr_FR': [u'chapeau de paille'], 'en_US': [u'straw hat'], 'es_ES': [u'sombero de paja'] }, SnipsWeatherConditions.HAT: { + 'de_DE': [u'hut'], 'fr_FR': [u'chapeau'], 'en_US': [u'hat'], 'es_ES': [u'sombrero'] }, SnipsWeatherConditions.SUNBLOCK: { + 'de_DE': [u'sonnencreme'], 'fr_FR': [u'crème solaire'], 'en_US': [u'sunblock'], 'es_ES': [u'crema solar'] }, SnipsWeatherConditions.SUNSCREEN: { + 'de_DE': [u'sonnenschutz'], 'fr_FR': [u'écran solaire'], 'en_US': [u'sunscreen'], 'es_ES': [u'protector solar'] }, SnipsWeatherConditions.SUN_CREAM: { + 'de_DE': [u'sonnencreme'], 'fr_FR': [u'crème solaire'], 'en_US': [u'sun cream'], 'es_ES': [u'crema solar'] }, SnipsWeatherConditions.WOOLEN_SWEATER: { + 'de_DE': [u'wollpullover'], 'fr_FR': [u'pull en laine'], 'en_US': [u'woolen sweater'], 'es_ES': [u'jersey de lana'] }, SnipsWeatherConditions.WOOLEN_JUMPER: { + 'de_DE': [u'pullover'], 'fr_FR': [u'pull en laine'], 'en_US': [u'woolen jumper'], 'es_ES': [u'jersey de lana'] }, SnipsWeatherConditions.WOOLEN_TIGHTS: { + 'de_DE': [u'strumpfhosen'], 'fr_FR': [u'collants en laine'], 'en_US': [u'woolen tights'], 'es_ES': [u'medias de lana'] }, SnipsWeatherConditions.SLEEVELESS_SUNDRESS: { + 'de_DE': [u'sommerkleidung'], 'fr_FR': [u'robe d\'été'], 'en_US': [u'sleeveless sundress'], 'es_ES': [u'vestido sin mangas'] }, SnipsWeatherConditions.SUNDRESS: { + 'de_DE': [u'sommerkleidung'], 'fr_FR': [u'robe d\'été'], 'en_US': [u'sundress'], 'es_ES': [u'vestido de verano'] }, SnipsWeatherConditions.CHUNKY_SWEATER: { + 'de_DE': [u'pullover'], 'fr_FR': [u'gros pull'], 'en_US': [u'chunky sweater'], 'es_ES': [u'jersey grueso'] }, SnipsWeatherConditions.SUNGLASSES: { + 'de_DE': [u'sonnenbrille'], 'fr_FR': [u'lunettes de soleil'], 'en_US': [u'sunglasses'], 'es_ES': [u'gafas de sol'] }, SnipsWeatherConditions.RAINCOAT: { + 'de_DE': [u'regenjacke'], 'fr_FR': [u'manteau pour la pluie'], 'en_US': [u'raincoat'], 'es_ES': [u'impermeable'] }, SnipsWeatherConditions.WOOLEN_SOCKS: { + 'de_DE': [u'wollsocken'], 'fr_FR': [u'chaussettes en laine'], 'en_US': [u'woolen socks'], 'es_ES': [u'calcetines de lana'] diff --git a/snipsowm/weather_condition.py b/snipsowm/weather_condition.py index ebc3743..13426c5 100644 --- a/snipsowm/weather_condition.py +++ b/snipsowm/weather_condition.py @@ -17,56 +17,67 @@ def __init__(self, key): descriptions = { WeatherConditions.THUNDERSTORM: { + "en_US": ["voraussichtlich Gewitterstürme"], "en_US": ["Thunderstorms are expected", "expect thunderstorms"], "fr_FR": ["de l'orage et des éclair sont prévus"], "es_ES": ["Se esperan tormentas"], }, WeatherConditions.DRIZZLE: { + "en_US": ["voraussichtlich Nieselregen"], "en_US": ["drizzle are expected", "expect drizzle"], "fr_FR": ["prévoir de la bruine"], "es_ES": ["Se espera granizo"], }, WeatherConditions.RAIN: { + "en_US": ["voraussichtlich regen"], "en_US": ["rain is expected", "it's going to be rainy", "expect rain"], "fr_FR": ["il va pleuvoir", "il pleuvra", "le temps sera pluvieux"], "es_ES": ["Se esperan lluvias"], }, WeatherConditions.SNOW: { + "en_US": ["voraussichtlich Schnee"], "en_US": ["snow is expected", "it's going to snow", "expect snow"], "fr_FR": ["il neigera", "il va neiger", "le temps sera neigeux"], "es_ES": ["Se esperan nevadas"], }, WeatherConditions.FOG: { + "en_US": ["voraussichtlich Nebel"], "en_US": ["fog is expected", "it's going to be foggy", "expect fog"], "fr_FR": ["Il y aura du brouillard"], "es_ES": ["Se espera niebla"], }, WeatherConditions.SUN: { + "en_US": ["Sonnenschein", "voraussichtlich sonnig"], "en_US": ["sun is expected", "it's going to be sunny", "the sun will shine"], "fr_FR": ["le temps sera ensoleillé"], "es_ES": ["Se espera sol", "El sol brillará"], }, WeatherConditions.CLOUDS: { + "en_US": ["voraussichtlich wolkig"], "en_US": ["it's going to be cloudy", "expect clouds"], "fr_FR": ["le temps sera nuageux"], "es_ES": ["Se esperan nubes", "El cielo estará nublado"], }, WeatherConditions.STORM: { + "en_US": ["voraussichtlich stürmisch"], "en_US": ["storms are expected", "it's going to be stormy", "expect storms"], "fr_FR": ["il y aura de l'orage"], "es_ES": ["Se esperan tormentas"], }, WeatherConditions.HUMID: { + "en_US": ["voraussichtlich hohe Luftfeuchtigkeit"], "en_US": ["humidity is expected", "it's going to be humid"], "fr_FR": ["le temps sera humide"], "es_ES": ["La humedad será elevada"], }, WeatherConditions.WIND: { + "en_US": ["voraussichtlich windig"], "en_US": ["wind is expected", "it's going to be windy", "expect wind"], "fr_FR": ["s'attendre à du vent"], "es_ES": ["Se espera un tiempo ventoso"], }, WeatherConditions.UNKNOWN: { + "en_US": ["Unbekannte Wetterbedingungen"], "en_US": ["I don't know how to describe the weather"], "fr_FR": ["Je ne peux pas décrire la météo"], "es_ES": ["No puedo decirte el tiempo que hará"], From 57a19a6564f2527c4e77dfb153d9d1a3ec9ac466 Mon Sep 17 00:00:00 2001 From: Felix Schwenzel Date: Mon, 22 Oct 2018 14:30:46 +0200 Subject: [PATCH 2/6] correction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (i think i edited the wrong file, now it’s right) --- snipsowm/sentence_generator.py | 147 +++++++++++++++++---------------- 1 file changed, 74 insertions(+), 73 deletions(-) diff --git a/snipsowm/sentence_generator.py b/snipsowm/sentence_generator.py index b6b69f3..31939cb 100644 --- a/snipsowm/sentence_generator.py +++ b/snipsowm/sentence_generator.py @@ -1,62 +1,57 @@ +#!/usr/bin/python # -*- coding: utf-8 -*- -import abc -import collections +import datetime from enum import Enum import locale -import sentence_generation_utils as utils import random -class SentenceGenerator(object): - __metaclass__ = abc.ABCMeta +class SentenceTone(Enum): + NEUTRAL = 0 + POSITIVE = 1 + NEGATIVE = 2 - def __init__(self, locale="en_US"): - self.locale = locale - def generate_error_sentence(self): - error_sentences = { - "en_US": "An error occured when trying to retrieve the weather, please try again", - "fr_FR": "Désolé, il y a eu une erreur lors de la récupération des données météo. Veuillez réessayer", - "es_ES": "Ha ocurrido un error obteniendo la información meteorológica, por favor inténtalo de nuevo", - "de_DE": "Da ist was schiefgelaufen beim Wetterdaten einlesen, versuche es bitte nochmal" - } +def date_to_string(date, granularity=0): + """ Convert a date to a string, with an appropriate level of + granularity. - return error_sentences[self.locale] + :param date: A datetime object. + :param granularity: Granularity for the desired textual representation. + 0: precise (date and time are returned) + 1: day (only the week day is returned) + 2: month (only year and month are returned) + 3: year (only year is returned) + :return: A textual representation of the date. + """ + if not date: + return "" - def generate_error_locale_sentence(self): - """ - TODO : This method is too specific to be implemented by the SentenceGenerator class. - :return: - :rtype:basestring - """ - error_sentences = { - "en_US": "An error occured. Your system doesn't have the correct locale installed. Please refer to the documentation. ", - "fr_FR": "Désolé, il y a eu une erreur. Votre système n'a pas la locale correcte installée. Veuillez consulter la documentation pour plus de détails.", - "es_ES": "Ha ocurrido un error. Parece que el sistema no tiene instalado el paquete de idioma correcto ('locale'). Consula la documentación para más detalles.", - "de_DE": "Da ist was schiefgelaufen. Dieser Rechner hat nicht die korrekte Lokalisierung installiert. Bitte lese die Installationsanleitung." - } + if granularity == 0: + return date.strftime("%A") + elif granularity == 1: + return date.strftime("%A, %d") + elif granularity == 2: + return date.strftime("%A, %d %B") - return error_sentences[self.locale] + return date.strftime("%A, %d %B, %H:%M%p") - def generate_api_key_error_sentence(self): - error_sentences = { - "en_US": "The API key you provided is invalid, check your config.ini", - "fr_FR": "La clé API fournie est incorrecte, vérifiez le fichier config.ini", - "es_ES": "La clave de la API proporcionada no es válida, por favor comprueba tu fichero config.ini", - "de_DE": "Der eingetragene API-Schlüssel ist ungültig, bitte prüfe die Konfigurationsdatei" - } - return error_sentences[self.locale] +def french_is_masculine_word(word): + return word[len(word) - 1] not in ['é', 'e'] -class SimpleSentenceGenerator(SentenceGenerator): - pass +def starts_with_vowel(word): + return word[0] in ['a', 'e', 'i', 'o', 'u', 'y'] -class AnswerSentenceGenerator(SentenceGenerator): - class SentenceTone(Enum): - NEUTRAL = 0 - POSITIVE = 1 - NEGATIVE = 2 + +class SentenceGenerator(object): + def __init__(self, locale="en_US"): + """ + :param language: + :type language: Language + """ + self.locale = locale def generate_sentence_introduction(self, tone): """ @@ -69,24 +64,24 @@ def generate_sentence_introduction(self, tone): sentence_beginnings = { "en_US": { - AnswerSentenceGenerator.SentenceTone.POSITIVE: "Yes,", - AnswerSentenceGenerator.SentenceTone.NEGATIVE: "No,", - AnswerSentenceGenerator.SentenceTone.NEUTRAL: "" + SentenceTone.POSITIVE: "Yes,", + SentenceTone.NEGATIVE: "No,", + SentenceTone.NEUTRAL: "" }, "fr_FR": { - AnswerSentenceGenerator.SentenceTone.POSITIVE: "Oui,", - AnswerSentenceGenerator.SentenceTone.NEGATIVE: "Non,", - AnswerSentenceGenerator.SentenceTone.NEUTRAL: "" + SentenceTone.POSITIVE: "Oui,", + SentenceTone.NEGATIVE: "Non,", + SentenceTone.NEUTRAL: "" }, "es_ES": { - AnswerSentenceGenerator.SentenceTone.POSITIVE: "Sí,", - AnswerSentenceGenerator.SentenceTone.NEGATIVE: "No,", - AnswerSentenceGenerator.SentenceTone.NEUTRAL: "" + SentenceTone.POSITIVE: "Sí,", + SentenceTone.NEGATIVE: "No,", + SentenceTone.NEUTRAL: "" }, - "de_DE": { - AnswerSentenceGenerator.SentenceTone.POSITIVE: "Ja,", - AnswerSentenceGenerator.SentenceTone.NEGATIVE: "Nein,", - AnswerSentenceGenerator.SentenceTone.NEUTRAL: "" + "es_ES": { + SentenceTone.POSITIVE: "Ja,", + SentenceTone.NEGATIVE: "Nein,", + SentenceTone.NEUTRAL: "" } } @@ -107,13 +102,13 @@ def generate_sentence_locality(self, POI=None, Locality=None, Region=None, Count :rtype: basestring """ if self.locale == "en_US": - if (POI or Locality or Region or Country): + if POI or Locality or Region or Country: locality = filter(lambda x: x is not None, [POI, Locality, Region, Country])[0] return "in {}".format(locality) else: return "" - elif self.locale == "fr_FR": + if self.locale == "fr_FR": """ Country granularity: - We use "au" for masculine nouns that begins with a consonant @@ -138,12 +133,12 @@ def generate_sentence_locality(self, POI=None, Locality=None, Region=None, Count return "à {}".format(Locality) if Region: - if utils.french_is_masculine_word(Region) and (not utils.starts_with_vowel(Region)): + if french_is_masculine_word(Region) and (not starts_with_vowel(Region)): return "au {}".format(Region) else: return "en {}".format(Region) if Country: - if utils.french_is_masculine_word(Country) and (not utils.starts_with_vowel(Country)): + if french_is_masculine_word(Country) and (not starts_with_vowel(Country)): return "au {}".format(Country) else: return "en {}".format(Country) @@ -157,7 +152,7 @@ def generate_sentence_locality(self, POI=None, Locality=None, Region=None, Count else: return "" - elif self.locale == "de_DE": + if self.locale == "de_DE": if POI or Locality or Region or Country: locality = filter(lambda x: x is not None, [POI, Locality, Region, Country])[0] return "in {}".format(locality) @@ -188,16 +183,13 @@ def generate_sentence_date(self, date, granularity=0): print "Careful! There was an error while trying to set the locale {}. This means your locale is not properly installed. Please refer to the README for more information.".format(full_locale) print "Some information displayed might not be formated to your locale" - return utils.date_to_string(date, granularity) - - -class ConditionQuerySentenceGenerator(AnswerSentenceGenerator): + return date_to_string(date, granularity) def generate_condition_description(self, condition_description): return condition_description if len(condition_description) > 0 else "" def generate_condition_sentence(self, - tone=AnswerSentenceGenerator.SentenceTone.POSITIVE, + tone=SentenceTone.POSITIVE, date=None, granularity=0, condition_description=None, POI=None, Locality=None, Region=None, Country=None): @@ -234,8 +226,6 @@ def generate_condition_sentence(self, parameters = filter(lambda x: not x is None and len(x) > 0, parameters) return ("{} " * len(parameters)).format(*parameters) - -class TemperatureQuerySentenceGenerator(AnswerSentenceGenerator): def generate_temperature_sentence(self, temperature="-273.15", date=None, granularity=0, @@ -281,10 +271,21 @@ def generate_temperature_sentence(self, parameters = (introduction,) + tuple(permutable_parameters) return "{} {} {}".format(*parameters) + def generate_error_sentence(self): + error_sentences = { + "en_US": "An error occured when trying to retrieve the weather, please try again", + "fr_FR": "Désolé, il y a eu une erreur lors de la récupération des données météo. Veuillez réessayer", + "es_ES": "Ha ocurrido un error obteniendo la información meteorológica, por favor inténtalo de nuevo", + "de_DE": "Da ist was schiefgelaufen beim Wetterdaten einlesen, versuche es bitte nochmal" + } -class SentenceGenerationException(Exception): - pass - + return error_sentences[self.locale] -class SentenceGenerationLocaleException(SentenceGenerationException): - pass \ No newline at end of file + def generate_api_key_error_sentence(self): + error_sentences = { + "en_US": "The API key you provided is invalid, check your config.ini", + "fr_FR": "La clé API fournie est incorrecte, vérifiez le fichier config.ini", + "es_ES": "La clave de la API proporcionada no es válida, por favor comprueba tu fichero config.ini", + "de_DE": "Der eingetragene API-Schlüssel ist ungültig, bitte prüfe die Konfigurationsdatei" + } + return error_sentences[self.locale] \ No newline at end of file From e333e9176bc3f7c2d89981f3c78a2332f335807c Mon Sep 17 00:00:00 2001 From: felix schwenzel Date: Mon, 22 Oct 2018 15:29:56 +0200 Subject: [PATCH 3/6] added german flag emoji --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e2830f..2448f6c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ To have the skills properly working, you **need** to generate locales for your l - 🇺🇸 `en_US` - 🇫🇷 `fr_FR` - 🇪🇸 `es_ES` -- `de_DE` +- 🇩🇪 `de_DE` You can generate them with `sudo raspi-config`. Going in the `Localisation Options` submenu, then in the `Change Locale` submenu, and selecting the locales you want to support. For instance, select `en_US UTF-8` if you want support for English. From 5f0632d0d547b5a1fcb86ef7b66dd59a58ba8afa Mon Sep 17 00:00:00 2001 From: Felix Schwenzel Date: Mon, 22 Oct 2018 19:13:56 +0200 Subject: [PATCH 4/6] forgot some files --- snipsowm/feedback/sentence_generator.py | 27 ++++++++++++++++++++----- snipsowm/weather_condition.py | 22 ++++++++++---------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/snipsowm/feedback/sentence_generator.py b/snipsowm/feedback/sentence_generator.py index 68096d1..bf735fd 100644 --- a/snipsowm/feedback/sentence_generator.py +++ b/snipsowm/feedback/sentence_generator.py @@ -17,7 +17,8 @@ def generate_error_sentence(self): error_sentences = { "en_US": "An error occured when trying to retrieve the weather, please try again", "fr_FR": "Désolé, il y a eu une erreur lors de la récupération des données météo. Veuillez réessayer", - "es_ES": "Ha ocurrido un error obteniendo la información meteorológica, por favor inténtalo de nuevo" + "es_ES": "Ha ocurrido un error obteniendo la información meteorológica, por favor inténtalo de nuevo", + "de_DE": "Da ist was schiefgelaufen beim Wetterdaten einlesen, versuche es bitte nochmal" } return error_sentences[self.locale] @@ -31,7 +32,8 @@ def generate_error_locale_sentence(self): error_sentences = { "en_US": "An error occured. Your system doesn't have the correct locale installed. Please refer to the documentation. ", "fr_FR": "Désolé, il y a eu une erreur. Votre système n'a pas la locale correcte installée. Veuillez consulter la documentation pour plus de détails.", - "es_ES": "Ha ocurrido un error. Parece que el sistema no tiene instalado el paquete de idioma correcto ('locale'). Consula la documentación para más detalles." + "es_ES": "Ha ocurrido un error. Parece que el sistema no tiene instalado el paquete de idioma correcto ('locale'). Consula la documentación para más detalles.", + "de_DE": "Da ist was schiefgelaufen: Auf dem System ist nicht die richtige Lokalisierung installiert ('locale'). Bitte prüfen Sie die Dokumentation." } return error_sentences[self.locale] @@ -40,7 +42,8 @@ def generate_api_key_error_sentence(self): error_sentences = { "en_US": "The API key you provided is invalid, check your config.ini", "fr_FR": "La clé API fournie est incorrecte, vérifiez le fichier config.ini", - "es_ES": "La clave de la API proporcionada no es válida, por favor comprueba tu fichero config.ini" + "es_ES": "La clave de la API proporcionada no es válida, por favor comprueba tu fichero config.ini", + "de_DE": "Der eingetragene API-Schlüssel ist ungültig, bitte prüfe die Konfigurationsdatei" } return error_sentences[self.locale] @@ -79,6 +82,11 @@ def generate_sentence_introduction(self, tone): AnswerSentenceGenerator.SentenceTone.POSITIVE: "Sí,", AnswerSentenceGenerator.SentenceTone.NEGATIVE: "No,", AnswerSentenceGenerator.SentenceTone.NEUTRAL: "" + }, + "de_DE": { + AnswerSentenceGenerator.SentenceTone.POSITIVE: "Ja,", + AnswerSentenceGenerator.SentenceTone.NEGATIVE: "Nein,", + AnswerSentenceGenerator.SentenceTone.NEUTRAL: "" } } @@ -149,6 +157,13 @@ def generate_sentence_locality(self, POI=None, Locality=None, Region=None, Count else: return "" + elif self.locale == "de_DE": + if POI or Locality or Region or Country: + locality = filter(lambda x: x is not None, [POI, Locality, Region, Country])[0] + return "in {}".format(locality) + else: + return "" + else: return "" @@ -243,7 +258,8 @@ def generate_temperature_sentence(self, error_sentences = { "en_US": "I couldn't fetch the right data for the specified place and date", "fr_FR": "Je n'ai pas pu récupérer les prévisions de température pour cet endroit et ces dates", - "es_ES": "No he podido encontrar información meteorológica para el lugar y la fecha especificados" + "es_ES": "No he podido encontrar información meteorológica para el lugar y la fecha especificados", + "de_DE": "Ich konnte nicht die richtigen Daten für den angegebenen Ort und das datum finden" } if (temperature is None): @@ -252,7 +268,8 @@ def generate_temperature_sentence(self, sentence_introductions = { "en_US": ["The temperature will be {} degrees"], "fr_FR": ["La température sera de {} degrés", "Il fera {} degrés"], - "es_ES": ["La temperatura será de {} grados", "Habrá {} grados"] + "es_ES": ["La temperatura será de {} grados", "Habrá {} grados"], + "de_DE": ["Die Temperatur ist {} Grad", "Es ist {} Grad"] } introduction = random.choice(sentence_introductions[self.locale]).format(temperature) diff --git a/snipsowm/weather_condition.py b/snipsowm/weather_condition.py index 13426c5..32a6a64 100644 --- a/snipsowm/weather_condition.py +++ b/snipsowm/weather_condition.py @@ -17,67 +17,67 @@ def __init__(self, key): descriptions = { WeatherConditions.THUNDERSTORM: { - "en_US": ["voraussichtlich Gewitterstürme"], + "de_DE": ["voraussichtlich Gewitterstürme"], "en_US": ["Thunderstorms are expected", "expect thunderstorms"], "fr_FR": ["de l'orage et des éclair sont prévus"], "es_ES": ["Se esperan tormentas"], }, WeatherConditions.DRIZZLE: { - "en_US": ["voraussichtlich Nieselregen"], + "de_DE": ["voraussichtlich Nieselregen"], "en_US": ["drizzle are expected", "expect drizzle"], "fr_FR": ["prévoir de la bruine"], "es_ES": ["Se espera granizo"], }, WeatherConditions.RAIN: { - "en_US": ["voraussichtlich regen"], + "de_DE": ["voraussichtlich regen"], "en_US": ["rain is expected", "it's going to be rainy", "expect rain"], "fr_FR": ["il va pleuvoir", "il pleuvra", "le temps sera pluvieux"], "es_ES": ["Se esperan lluvias"], }, WeatherConditions.SNOW: { - "en_US": ["voraussichtlich Schnee"], + "de_DE": ["voraussichtlich Schnee"], "en_US": ["snow is expected", "it's going to snow", "expect snow"], "fr_FR": ["il neigera", "il va neiger", "le temps sera neigeux"], "es_ES": ["Se esperan nevadas"], }, WeatherConditions.FOG: { - "en_US": ["voraussichtlich Nebel"], + "de_DE": ["voraussichtlich Nebel"], "en_US": ["fog is expected", "it's going to be foggy", "expect fog"], "fr_FR": ["Il y aura du brouillard"], "es_ES": ["Se espera niebla"], }, WeatherConditions.SUN: { - "en_US": ["Sonnenschein", "voraussichtlich sonnig"], + "de_DE": ["Sonnenschein", "voraussichtlich sonnig"], "en_US": ["sun is expected", "it's going to be sunny", "the sun will shine"], "fr_FR": ["le temps sera ensoleillé"], "es_ES": ["Se espera sol", "El sol brillará"], }, WeatherConditions.CLOUDS: { - "en_US": ["voraussichtlich wolkig"], + "de_DE": ["voraussichtlich wolkig"], "en_US": ["it's going to be cloudy", "expect clouds"], "fr_FR": ["le temps sera nuageux"], "es_ES": ["Se esperan nubes", "El cielo estará nublado"], }, WeatherConditions.STORM: { - "en_US": ["voraussichtlich stürmisch"], + "de_DE": ["voraussichtlich stürmisch"], "en_US": ["storms are expected", "it's going to be stormy", "expect storms"], "fr_FR": ["il y aura de l'orage"], "es_ES": ["Se esperan tormentas"], }, WeatherConditions.HUMID: { - "en_US": ["voraussichtlich hohe Luftfeuchtigkeit"], + "de_DE": ["voraussichtlich hohe Luftfeuchtigkeit"], "en_US": ["humidity is expected", "it's going to be humid"], "fr_FR": ["le temps sera humide"], "es_ES": ["La humedad será elevada"], }, WeatherConditions.WIND: { - "en_US": ["voraussichtlich windig"], + "de_DE": ["voraussichtlich windig"], "en_US": ["wind is expected", "it's going to be windy", "expect wind"], "fr_FR": ["s'attendre à du vent"], "es_ES": ["Se espera un tiempo ventoso"], }, WeatherConditions.UNKNOWN: { - "en_US": ["Unbekannte Wetterbedingungen"], + "de_DE": ["Unbekannte Wetterbedingungen"], "en_US": ["I don't know how to describe the weather"], "fr_FR": ["Je ne peux pas décrire la météo"], "es_ES": ["No puedo decirte el tiempo que hará"], From 1b9a4adf65e259e75c87bd6e413b2dc5ba251d6f Mon Sep 17 00:00:00 2001 From: Felix Schwenzel Date: Mon, 22 Oct 2018 19:56:58 +0200 Subject: [PATCH 5/6] small crrections --- snipsowm/feedback/sentence_generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snipsowm/feedback/sentence_generator.py b/snipsowm/feedback/sentence_generator.py index bf735fd..e6de62f 100644 --- a/snipsowm/feedback/sentence_generator.py +++ b/snipsowm/feedback/sentence_generator.py @@ -259,7 +259,7 @@ def generate_temperature_sentence(self, "en_US": "I couldn't fetch the right data for the specified place and date", "fr_FR": "Je n'ai pas pu récupérer les prévisions de température pour cet endroit et ces dates", "es_ES": "No he podido encontrar información meteorológica para el lugar y la fecha especificados", - "de_DE": "Ich konnte nicht die richtigen Daten für den angegebenen Ort und das datum finden" + "de_DE": "Ich konnte nicht die richtigen Daten für den angegebenen Ort und Zeit finden" } if (temperature is None): @@ -269,7 +269,7 @@ def generate_temperature_sentence(self, "en_US": ["The temperature will be {} degrees"], "fr_FR": ["La température sera de {} degrés", "Il fera {} degrés"], "es_ES": ["La temperatura será de {} grados", "Habrá {} grados"], - "de_DE": ["Die Temperatur ist {} Grad", "Es ist {} Grad"] + "de_DE": ["Die Temperatur wird {} Grad", "Es wird {} Grad"] } introduction = random.choice(sentence_introductions[self.locale]).format(temperature) From b8e56f675c6a3ce9ecac4889d5fe70358b7ebba0 Mon Sep 17 00:00:00 2001 From: Felix Schwenzel Date: Mon, 22 Oct 2018 20:05:29 +0200 Subject: [PATCH 6/6] minor correction --- snipsowm/feedback/sentence_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snipsowm/feedback/sentence_generator.py b/snipsowm/feedback/sentence_generator.py index e6de62f..5c398fb 100644 --- a/snipsowm/feedback/sentence_generator.py +++ b/snipsowm/feedback/sentence_generator.py @@ -269,7 +269,7 @@ def generate_temperature_sentence(self, "en_US": ["The temperature will be {} degrees"], "fr_FR": ["La température sera de {} degrés", "Il fera {} degrés"], "es_ES": ["La temperatura será de {} grados", "Habrá {} grados"], - "de_DE": ["Die Temperatur wird {} Grad", "Es wird {} Grad"] + "de_DE": ["Es wird {} Grad"] } introduction = random.choice(sentence_introductions[self.locale]).format(temperature)