Skip to content

Commit

Permalink
patch/locale_switching + packaging/LN+LF_support (#6)(#7)(#27)(#62)
Browse files Browse the repository at this point in the history
- integration of timezone support with LN (was a dummy method until now)
- lingua_franca and lingua_nostra need to be able to coexist
	- skills might be importing one of those libs directly
- mycroft-lib will call property setters (lang/timezone) for both libs if possible
- all other wrapped methods (`mycroft.util.parse` + `mycroft.util.format` + `mycroft.util.time`) will give preference to LN if it is available, use LF otherwise
	- this should be irrelevant for end users since LN is a drop in replacement
  • Loading branch information
JarbasAl committed Jul 22, 2021
1 parent 5b20afa commit 196fbb6
Show file tree
Hide file tree
Showing 14 changed files with 318 additions and 104 deletions.
3 changes: 2 additions & 1 deletion mycroft/audio/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
)
from mycroft.util.log import LOG
from mycroft.util.process_utils import ProcessStatus, StatusCallbackMap

from mycroft.configuration import setup_locale
import mycroft.audio.speech as speech
from mycroft.audio.audioservice import AudioService

Expand Down Expand Up @@ -53,6 +53,7 @@ def main(ready_hook=on_ready, error_hook=on_error, stopping_hook=on_stopping):
on_stopping=stopping_hook)
status = ProcessStatus('audio', bus, callbacks)

setup_locale()
speech.init(bus)

# Connect audio service instance to message bus
Expand Down
2 changes: 2 additions & 0 deletions mycroft/client/enclosure/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
control over the Mark-1 Faceplate.
"""
from mycroft.configuration import LocalConf, SYSTEM_CONFIG
from mycroft.configuration import setup_locale
from mycroft.util.log import LOG
from mycroft.util import wait_for_exit_signal, reset_sigint_handler

Expand Down Expand Up @@ -78,6 +79,7 @@ def main(ready_hook=on_ready, error_hook=on_error, stopping_hook=on_stopping):
LOG.debug("Enclosure created")
try:
reset_sigint_handler()
setup_locale()
enclosure.run()
ready_hook()
wait_for_exit_signal()
Expand Down
4 changes: 2 additions & 2 deletions mycroft/client/speech/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from mycroft import dialog
from mycroft.enclosure.api import EnclosureAPI
from mycroft.client.speech.listener import RecognizerLoop
from mycroft.configuration import Configuration
from mycroft.configuration import Configuration, setup_locale
from mycroft.identity import IdentityManager
from mycroft.lock import Lock as PIDLock # Create/Support PID locking file
from mycroft.messagebus.message import Message
Expand Down Expand Up @@ -228,7 +228,7 @@ def main(ready_hook=on_ready, error_hook=on_error, stopping_hook=on_stopping,
callbacks = StatusCallbackMap(on_ready=ready_hook, on_error=error_hook,
on_stopping=stopping_hook)
status = ProcessStatus('speech', bus, callbacks)

setup_locale()
# Register handlers on internal RecognizerLoop bus
loop = RecognizerLoop(watchdog)
connect_loop_events(loop)
Expand Down
3 changes: 2 additions & 1 deletion mycroft/client/text/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
start_log_monitor, start_mic_monitor, connect_to_mycroft,
ctrl_c_handler
)
from mycroft.configuration import Configuration
from mycroft.configuration import Configuration, setup_locale

sys.stdout = io.StringIO()
sys.stderr = io.StringIO()
Expand Down Expand Up @@ -54,6 +54,7 @@ def main():
start_mic_monitor(os.path.join(get_ipc_directory(), "mic_level"))

connect_to_mycroft()
setup_locale()
if '--simple' in sys.argv:
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
Expand Down
4 changes: 3 additions & 1 deletion mycroft/configuration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@
# limitations under the License.
#
from mycroft.configuration.config import Configuration, LocalConf, RemoteConf
from mycroft.configuration.locale import set_default_lf_lang
from mycroft.configuration.locale import set_default_lf_lang, setup_locale, \
set_default_tz, set_default_lang, get_default_tz, get_default_lang, \
get_config_tz, get_primary_lang_code, load_languages, load_language
from mycroft.configuration.locations import SYSTEM_CONFIG, USER_CONFIG
121 changes: 119 additions & 2 deletions mycroft/configuration/locale.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,127 @@
The mycroft.util.lang module provides the main interface for setting up the
lingua-franca (https://github.com/mycroftai/lingua-franca) selected language
"""
from dateutil.tz import gettz, tzlocal

from lingua_franca import set_default_lang as _set_default_lf_lang
# we want to support both LF and LN
# when using the mycroft wrappers this is not an issue, but skills might use
# either, so mycroft-lib needs to account for this and set the defaults for
# both libs.

# lingua_franca/lingua_nostra are optional and might not be installed
# exceptions should only be raised in the parse and format utils


try:
import lingua_franca as LF
except ImportError:
LF = None

try:
import lingua_nostra as LN
except ImportError:
LN = None

_lang = "en-us"
_tz = tzlocal()


def get_primary_lang_code():
if LN:
return LN.get_primary_lang_code()
if LF:
return LF.get_primary_lang_code()
return _lang.split("-")[0]


def get_default_lang():
if LN:
return LN.get_default_lang()
if LF:
return LF.get_default_lang()
return _lang


def set_default_lang(lang):
global _lang
_lang = lang
if LN:
LN.set_default_lang(lang)
if LF:
LF.set_default_lang(lang)


def get_config_tz():
# TODO cyclic import error because top module is called "locale"
from mycroft.configuration.config import Configuration
config = Configuration.get()
code = config["location"]["timezone"]["code"]
return gettz(code)


def get_default_tz():
tz = None
# go with LF/LN default timezone, in case skills changed it temporarily
# for some arcane reason...
if LN:
tz = tz or LN.time.default_timezone()
if LF:
try:
tz = tz or LF.time.default_timezone()
except Exception:
# old versions of LF
# AttributeError: module 'lingua_franca' has no attribute 'time'
pass

# use the timezone from .conf
tz = tz or get_config_tz()

# Just go with global default
return tz or _tz


def set_default_tz(tz=None):
""" configure both LF and LN """
global _tz
tz = tz or get_config_tz() or tzlocal()
_tz = tz
if LN:
LN.time.set_default_tz(tz)
if LF:
# tz added in recently, depends on version
try:
LF.time.set_default_tz(tz)
except:
pass


def load_languages(langs):
if LN:
LN.load_languages(langs)
if LF:
LF.load_languages(langs)


def load_language(lang):
if LN:
LN.load_language(lang)
if LF:
LF.load_language(lang)


def setup_locale(lang=None, tz=None):
# TODO cyclic import error because top module is called "locale"
from mycroft.configuration.config import Configuration
lang_code = lang or Configuration.get().get("lang", "en-us")
# Load language resources, currently en-us must also be loaded at all times
load_languages([lang_code, "en-us"])
# Set the active lang to match the configured one
set_default_lang(lang_code)
# Set the default timezone to match the configured one
set_default_tz(tz)


# mycroft-core backwards compat LF only interface
def set_default_lf_lang(lang_code="en-us"):
"""Set the default language of Lingua Franca for parsing and formatting.
Expand All @@ -32,4 +149,4 @@ def set_default_lf_lang(lang_code="en-us"):
Args:
lang (str): BCP-47 language code, e.g. "en-us" or "es-mx"
"""
return _set_default_lf_lang(lang_code=lang_code)
return set_default_lang(lang_code)
6 changes: 2 additions & 4 deletions mycroft/skills/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,13 @@
from threading import Event

from msm.exceptions import MsmException
from lingua_franca import load_languages

import mycroft.lock
from mycroft import dialog
from mycroft.api import is_paired, BackendDown, DeviceApi
from mycroft.audio import wait_while_speaking
from mycroft.enclosure.api import EnclosureAPI
from mycroft.configuration import Configuration
from mycroft.configuration import Configuration, setup_locale
from mycroft.messagebus.message import Message
from mycroft.util import (
connected,
Expand Down Expand Up @@ -199,8 +198,7 @@ def main(alive_hook=on_alive, started_hook=on_started, ready_hook=on_ready,
# Create PID file, prevent multiple instances of this service
mycroft.lock.Lock('skills')
config = Configuration.get()
lang_code = config.get("lang", "en-us")
load_languages([lang_code, "en-us"])
setup_locale()

# Connect this process to the Mycroft message bus
bus = start_message_bus_client("SKILLS")
Expand Down
8 changes: 4 additions & 4 deletions mycroft/skills/intent_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from copy import copy
import time

from mycroft.configuration import Configuration, set_default_lf_lang
from mycroft.configuration import Configuration, setup_locale
from mycroft.util.log import LOG
from mycroft.util.parse import normalize
from mycroft.metrics import report_timing, Stopwatch
Expand All @@ -33,7 +33,7 @@ def _get_message_lang(message):
message: message to check for language code.
Returns:
The languge code from the message or the default language.
The language code from the message or the default language.
"""
default_lang = Configuration.get().get('lang', 'en-us')
return message.data.get('lang', default_lang).lower()
Expand Down Expand Up @@ -152,7 +152,7 @@ def get_skill_name(self, skill_id):
def reset_converse(self, message):
"""Let skills know there was a problem with speech recognition"""
lang = _get_message_lang(message)
set_default_lf_lang(lang)
setup_locale(lang) # restore default lang
for skill in copy(self.active_skills):
self.do_converse(None, skill[0], lang, message)

Expand Down Expand Up @@ -273,7 +273,7 @@ def handle_utterance(self, message):
"""
try:
lang = _get_message_lang(message)
set_default_lf_lang(lang)
setup_locale(lang) # set default lang

utterances = message.data.get('utterances', [])
combined = _normalize_all_utterances(utterances)
Expand Down
55 changes: 38 additions & 17 deletions mycroft/util/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,44 @@
from calendar import leapdays
from enum import Enum

# These are the main functions we are using lingua franca to provide
# TODO 21.08 - move nice_duration methods to Lingua Franca.
from lingua_franca.format import (
join_list,
nice_date,
nice_date_time,
nice_number,
nice_time,
nice_year,
pronounce_number
)
# TODO 21.08 - remove import of private method _translate_word
# Consider whether the remaining items here are necessary.
from lingua_franca.format import (NUMBER_TUPLE, DateTimeFormat,
date_time_format, expand_options,
_translate_word)
from padatious.util import expand_parentheses
from mycroft.util.bracket_expansion import expand_parentheses, expand_options
from mycroft.configuration.locale import get_default_lang


# lingua_franca is optional, both lingua_franca and lingua_nostra are supported
# if both are installed preference is given to LN
# "setters" will be set in both lbs
# LN should be functionality equivalent to LF

try:
try:
from lingua_nostra.format import (NUMBER_TUPLE, DateTimeFormat,
join_list,
date_time_format, expand_options,
_translate_word,
nice_number, nice_time,
pronounce_number,
nice_date, nice_date_time, nice_year)
except ImportError:
# These are the main functions we are using lingua franca to provide
from lingua_franca.format import (NUMBER_TUPLE, DateTimeFormat,
join_list,
date_time_format, expand_options,
_translate_word,
nice_number, nice_time,
pronounce_number,
nice_date, nice_date_time, nice_year)
except ImportError:
def lingua_franca_error(*args, **kwargs):
raise ImportError("lingua_franca is not installed")

from mycroft.util.bracket_expansion import expand_options

NUMBER_TUPLE, DateTimeFormat = None, None

join_list = date_time_format = _translate_word = nice_number = \
nice_time = pronounce_number = nice_date = nice_date_time = \
nice_year = lingua_franca_error


class TimeResolution(Enum):
Expand Down
Loading

0 comments on commit 196fbb6

Please sign in to comment.