diff --git a/__init__.py b/__init__.py index b937a09..fec995d 100644 --- a/__init__.py +++ b/__init__.py @@ -591,11 +591,11 @@ def handle_change_media_properties(self, message: Message): if old_media: self.speak_dialog("media_type_changed", - {"old": translate(old_media), - "new": translate(new_media)}) + {"old": translate(old_media, lang=self.lang), + "new": translate(new_media, lang=self.lang)}) else: self.speak_dialog("media_type_set", - {"new": translate(new_media)}) + {"new": translate(new_media, lang=self.lang)}) # Query Alerts @intent_handler(IntentBuilder("ListAlerts").require("query") @@ -828,7 +828,7 @@ def handle_create_todo(self, message: Message, alert: Optional[Alert] = None): return if not alert: alert = Alert.create( - alert_name=name, alert_type=AlertType.TODO, dav_type=DAVType.VTODO + alert_name=name, alert_type=AlertType.TODO, dav_type=DAVType.VTODO, lang=self.lang ) if self.alert_manager.dav_active: self.specify_dav_attributes(alert, @@ -873,6 +873,7 @@ def handle_add_subitem_to_todo(self, message: Message): dav_calendar=todo.calendar, dav_service=todo.service, context=dict(related_to=todo.ident), + lang=self.lang ) self.alert_manager.add_alert(alert) self.speak_dialog( @@ -1157,9 +1158,9 @@ def confirm_alert(self, alert: Alert, message: Message, alert.expiration, self.lang) elif len(alert.repeat_days) == 7: - repeat_interval = translate("day", self.lang) + repeat_interval = translate("day", lang=self.lang) elif alert.repeat_days == WEEKDAYS: - repeat_interval = translate("weekday", self.lang) + repeat_interval = translate("weekday", lang=self.lang) else: repeat_interval = join_word_list([spoken_weekday(day, self.lang) for day in alert.repeat_days], diff --git a/test/test_skill.py b/test/test_skill.py index 7db9a95..0056b2f 100644 --- a/test/test_skill.py +++ b/test/test_skill.py @@ -43,7 +43,7 @@ from ovos_utils.messagebus import FakeBus from ovos_workshop.skills import OVOSSkill -from ovos_config.locale import load_language, get_default_tz +from ovos_config.locale import get_default_tz from ovos_skill_alerts import AlertSkill from ovos_skill_alerts.util import AlertPriority, AlertState, AlertType, DAVType, Weekdays, EVERYDAY @@ -130,9 +130,6 @@ def setUpClass(cls) -> None: cls.skill.alert_manager.get_dav_calendar = Mock(return_value=FakeCalendar()) cls.skill.alert_manager.get_dav_calendars = Mock(return_value=[FakeCalendar()]) - # Setup alerts - load_language("en-us") - uuid_subitem1 = str(uuid4()) uuid_subitem2 = str(uuid4()) uuid_parent = str(uuid4()) @@ -3672,7 +3669,6 @@ def _validate_alert_default_params(reminder: Alert): @unittest.skip('Work in progress') class TestUIModels(unittest.TestCase): - load_language("en") def test_build_timer_data(self): from ovos_skill_alerts.util.ui_models import build_timer_data diff --git a/util/alert.py b/util/alert.py index 6416586..198f2ef 100644 --- a/util/alert.py +++ b/util/alert.py @@ -683,6 +683,7 @@ def create( dav_calendar: str = None, dav_service: str = None, context: dict = None, + lang: str = None ): """ Object representing an arbitrary alert @@ -768,6 +769,7 @@ def create( "dav_service": dav_service, "dav_synchron": False, }) + data["lang"] = lang or get_default_lang() return Alert(data) diff --git a/util/locale.py b/util/locale.py index 5c9fffd..cdd3418 100644 --- a/util/locale.py +++ b/util/locale.py @@ -5,7 +5,8 @@ from typing import Optional, List, Union, Tuple from ovos_bus_client.message import Message -from ovos_config.locale import get_default_lang, load_language +from ovos_bus_client.util import get_message_lang +from ovos_config.locale import get_default_lang from ovos_date_parser import ( nice_duration, nice_time, @@ -70,8 +71,7 @@ def translate(word, lang=None): raise FileNotFoundError -def spoken_alert_type(alert_type: AlertType, - lang: str = None) -> str: +def spoken_alert_type(alert_type: AlertType, lang: str = None) -> str: """ Get a translated string for the specified alert_type :param alert_type: AlertType to be spoken @@ -140,6 +140,7 @@ def spoken_duration(alert_time: Union[dt.timedelta, dt.datetime], :param lang: Language to format response in :return: speakable duration string """ + lang = lang or get_default_lang() if isinstance(alert_time, dt.datetime): anchor_time = anchor_time or \ dt.datetime.now(alert_time.tzinfo).replace(microsecond=0) @@ -161,24 +162,24 @@ def spoken_duration(alert_time: Union[dt.timedelta, dt.datetime], else: _seconds = remaining_time.total_seconds() - return nice_duration(int(_seconds), lang=lang or get_default_lang()) + return nice_duration(int(_seconds), lang=lang) -def get_abbreviation(wd: Weekdays) -> str: +def get_abbreviation(wd: Weekdays, lang = None) -> str: if wd == Weekdays.MON: - return translate("abbreviation_monday") + return translate("abbreviation_monday", lang=lang) elif wd == Weekdays.TUE: - return translate("abbreviation_tuesday") + return translate("abbreviation_tuesday", lang=lang) elif wd == Weekdays.WED: - return translate("abbreviation_wednesday") + return translate("abbreviation_wednesday", lang=lang) elif wd == Weekdays.THU: - return translate("abbreviation_thursday") + return translate("abbreviation_thursday", lang=lang) elif wd == Weekdays.FRI: - return translate("abbreviation_friday") + return translate("abbreviation_friday", lang=lang) elif wd == Weekdays.SAT: - return translate("abbreviation_saturday") + return translate("abbreviation_saturday", lang=lang) elif wd == Weekdays.SUN: - return translate("abbreviation_sunday") + return translate("abbreviation_sunday", lang=lang) def get_alert_type_from_intent(message: Message) \ @@ -188,7 +189,7 @@ def get_alert_type_from_intent(message: Message) \ :param message: Message associated with intent match :returns: tuple of AlertType requested and spoken_type """ - lang = message.data.get("lang") + lang = get_message_lang(message) if message.data.get("alarm") or message.data.get("wake"): return AlertType.ALARM, translate("alarm", lang) elif message.data.get('timer'): diff --git a/util/parse_utils.py b/util/parse_utils.py index 0b6edc3..c332176 100644 --- a/util/parse_utils.py +++ b/util/parse_utils.py @@ -32,8 +32,8 @@ from uuid import uuid4 from dateutil.relativedelta import relativedelta -from ovos_bus_client import Message -from ovos_config.locale import get_default_lang, load_language, get_default_tz +from ovos_bus_client.message import Message, dig_for_message +from ovos_config.locale import get_default_lang, get_default_tz from ovos_date_parser import nice_time, nice_day, extract_datetime, extract_duration from ovos_number_parser import extract_number from ovos_skill_alerts.util import AlertPriority, Weekdays, AlertType, DAVType, LOCAL_USER @@ -49,6 +49,7 @@ from ovos_utils.log import LOG from ovos_utterance_normalizer import UtteranceNormalizerPlugin from rapidfuzz import fuzz +from ovos_bus_client.util import get_message_lang class Tokens(list): @@ -62,7 +63,7 @@ def __init__(self, chunks: list, message: Message = None): @property def lang(self): - return self.message.data.get("lang") or get_default_lang() + return get_message_lang(self.message) def unmatched(self, original=False): if original: @@ -82,7 +83,7 @@ def strip_time(self): return time_data = [] - lang = self.message.data.get("lang") or get_default_lang() + lang = get_message_lang(self.message) for i, token in enumerate(self): if self.is_matched(token): continue @@ -119,7 +120,7 @@ def tokenize_utterance(message: Message) -> Tokens: :returns: list of utterance tokens where a tag defines a token """ utterance: str = message.data.get("utterance", "").lower() - lang = message.data.get("lang") or get_default_lang() + lang = get_message_lang(message) if utterance is None: return Tokens([], message) @@ -180,7 +181,6 @@ def get_default_alert_name(alert_time: Union[dt.date, dt.datetime, dt.timedelta] isinstance(alert_time, dt.datetime) else get_default_tz() now_time = now_time or dt.datetime.now(tz=timezone) lang = lang or get_default_lang() - load_language(lang) if alert_type == AlertType.TIMER: time_str = spoken_duration(alert_time, now_time, lang) # TODO ordinalize (LF) @@ -201,7 +201,7 @@ def has_default_name(alert: Alert, lang: str = None) -> bool: :param lang: language to format response in :return: True if alert_name is the default name for the alert """ - lang = lang or get_default_lang() + lang = lang or alert.lang expiration = alert.expiration.date() if alert.is_all_day else alert.expiration default_name = get_default_alert_name(expiration, alert.alert_type, @@ -218,7 +218,7 @@ def build_alert_from_intent(message: Message) -> Optional[Alert]: :param timezone: Timezone for user associated with request :returns: Alert extracted from utterance or None if missing required params """ - lang = message.data.get("lang") or get_default_lang() + lang = get_message_lang(message) LOG.debug(f"{lang}, {type(lang)}") data = dict() @@ -275,7 +275,7 @@ def build_alert_from_intent(message: Message) -> Optional[Alert]: data["alert_type"] = alert_type if alert_type == AlertType.TODO: data["dav_type"] = DAVType.VTODO - + data["lang"] = lang return Alert.create(**data) @@ -290,8 +290,7 @@ def parse_repeat_from_message(message: Message, :returns: list of parsed repeat Weekdays or timedelta between occurrences """ repeat_days = list() - lang = message.data.get("lang") or get_default_lang() - load_language(lang) + lang = get_message_lang(message) if message.data.get("everyday"): repeat_days = [Weekdays(i) for i in range(0, 7)] elif message.data.get("weekends"): @@ -363,8 +362,7 @@ def parse_end_condition_from_message(message: Message, :param timezone: timezone of request, defaults to mycroft location :returns: extracted datetime of end condition, else None """ - lang = message.data.get("lang") or get_default_lang() - load_language(lang) + lang = get_message_lang(message) tokens = tokens or tokenize_utterance(message) timezone = timezone or get_default_tz() @@ -412,8 +410,7 @@ def parse_alert_time_from_message(message: Message, :param anchor_time: The base datetime the utterance is relative to :returns: Parsed datetime for the alert or None if no time is extracted """ - lang = message.data.get("lang") or get_default_lang() - load_language(lang) + lang = get_message_lang(message) timezone = timezone or get_default_tz() tokens = tokens or tokenize_utterance(message) @@ -496,7 +493,7 @@ def parse_alert_priority_from_message(message: Message, :param tokens: optional tokens parsed from message by `tokenize_utterances` """ tokens = tokens or tokenize_utterance(message) - lang = message.data.get("lang") or get_default_lang() + lang = get_message_lang(message) priority = AlertPriority.AVERAGE.value if message.data.get("priority"): @@ -512,12 +509,9 @@ def parse_alert_name_from_message(message: Message, Try to parse an alert name from unparsed tokens :param message: Message associated with the request :param tokens: optional tokens parsed from message by `tokenize_utterances` - :param strip_datetimes: if True, ignore any passed tokens containing times - :param articles: list of words to strip from a candidate alert name :returns: Best guess at a name extracted from tokens """ - lang = message.data.get("lang") or get_default_lang() - load_language(lang) + lang = get_message_lang(message) tokens = tokens or tokenize_utterance(message) tokens.strip_time() @@ -571,7 +565,7 @@ def parse_alert_context_from_message(message: Message) -> dict: "destination": message.context.get("destination") or "audio", # "session": message.context.get("session") or dict(), "user": message.context.get("user") or LOCAL_USER, - "lang": message.data.get("lang") or get_default_lang(), + "lang": get_message_lang(message), "ident": message.context.get("ident") or str(uuid4()), "origin_ident": message.context.get('ident'), "created": message.context.get("timing", @@ -582,11 +576,10 @@ def parse_alert_context_from_message(message: Message) -> dict: # TODO: no need for timezone in the signature def extract_dt_or_duration( - text: str, + text: str, lang: str, anchor_time: dt.datetime = None, timezone: dt.tzinfo = None, - add: bool = True, - lang=None + add: bool = True ) -> Union[dt.datetime, dt.timedelta, None]: """ Helper to extract either a duration or datetime @@ -620,18 +613,17 @@ def parse_relative_time_from_message(message: Message, tokens: Optional[Tokens] = None, timezone: Optional[dt.tzinfo] = None, anchor_time: Optional[dt.datetime] = None): - lang = message.data.get("lang") or get_default_lang() + lang = get_message_lang(message) earlier = "earlier" in message.data - load_language(lang) timezone = timezone or get_default_tz() tokens = tokens or tokenize_utterance(message) for token in tokens.unmatched(): - time_, remainder = extract_dt_or_duration(token, + time_, remainder = extract_dt_or_duration(token, lang, anchor_time, timezone, - not earlier, lang=lang) + not earlier) if time_: tokens[tokens.index(token)] = remainder return time_ @@ -664,22 +656,24 @@ def parse_timeframe_from_message(message: Message, return begin, end -def validate_dt_or_delta(response: str, lang: str = None) -> Union[dt.datetime, dt.timedelta, str]: +def validate_dt_or_delta(response: str) -> Union[dt.datetime, dt.timedelta, str]: """ Validates if the sentence contains a datetime or duration """ - lang = lang or get_default_lang() + message = dig_for_message() # used inside get_response + lang = get_message_lang(message) if message else get_default_lang() if voc_match(response, "no", lang): return "no" else: return extract_dt_or_duration(response, lang=lang)[0] -def validate_dt(response: str, lang: str = None) -> Union[dt.datetime, str]: +def validate_dt(response: str) -> Union[dt.datetime, str]: """ Validates if the sentence contains a datetime """ - lang = lang or get_default_lang() + message = dig_for_message() # used inside get_response + lang = get_message_lang(message) if message else get_default_lang() if voc_match(response, "no", lang): return "no" else: @@ -687,8 +681,9 @@ def validate_dt(response: str, lang: str = None) -> Union[dt.datetime, str]: return dt_ if dt_ is None else dt_[0] -def validate_number(response: str, lang: str = None) -> int: - lang = lang or get_default_lang() +def validate_number(response: str) -> int: + message = dig_for_message() # used inside get_response + lang = get_message_lang(message) if message else get_default_lang() num = extract_number(response, lang=lang) if num: return int(num) diff --git a/util/ui_models.py b/util/ui_models.py index 21dc489..2a7e17b 100644 --- a/util/ui_models.py +++ b/util/ui_models.py @@ -102,7 +102,7 @@ def build_alarm_data(alert: Alert) -> dict: if alert.has_repeat: alarm_repeat_str = create_repeat_str(alert) else: - alarm_repeat_str = translate("once").title() + alarm_repeat_str = translate("once", lang=alert.lang).title() return { "alarmTime": alarm_time, @@ -130,8 +130,8 @@ def get_sequences(d): if alert.repeat_days: sequences = get_sequences(list(alert.repeat_days)) for i, sequence in enumerate(sequences): - first = get_abbreviation(min(sequence)) - last = get_abbreviation(max(sequence)) + first = get_abbreviation(min(sequence), lang=alert.lang) + last = get_abbreviation(max(sequence), lang=alert.lang) if len(sequence) > 2: sequences[i] = f"{first}-{last}" elif len(sequence) == 2: @@ -146,6 +146,6 @@ def get_sequences(d): if alert.until: if repeat_str: repeat_str += "|" - repeat_str += f"{translate('until')} {datetime_display(alert.until.date(), lang=alert.lang)}" + repeat_str += f"{translate('until', lang=alert.lang)} {datetime_display(alert.until.date(), lang=alert.lang)}" return repeat_str