From dae80d699d379a9a05d24b9625f988fa6a8d9259 Mon Sep 17 00:00:00 2001 From: Vladimir Byko-Ianko Date: Tue, 13 Oct 2015 18:32:03 +0300 Subject: [PATCH] Using english in TTS if sound.txt doesn't contain the current language. Some refactoring. --- map/framework.hpp | 5 ++++ platform/get_text_by_id.cpp | 56 +++++++++++++++++++++++++++++-------- platform/get_text_by_id.hpp | 26 ++++++++++++----- routing/turns_tts_text.cpp | 22 ++++++++++----- routing/turns_tts_text.hpp | 6 ++-- 5 files changed, 86 insertions(+), 29 deletions(-) diff --git a/map/framework.hpp b/map/framework.hpp index 2d30557cd8..c6799e26a7 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -603,7 +603,12 @@ public: // Sound notifications for turn instructions. inline void EnableTurnNotifications(bool enable) { m_routingSession.EnableTurnNotifications(enable); } inline bool AreTurnNotificationsEnabled() const { return m_routingSession.AreTurnNotificationsEnabled(); } + /// \brief Sets a locale for TTS. + /// \param locale is a string with locale code. For example "en", "ru", "zh-Hant" and so on. + /// See sound/tts/languages.txt for the full list. inline void SetTurnNotificationsLocale(string const & locale) { m_routingSession.SetTurnNotificationsLocale(locale); } + /// @return current TTS locale. For example "en", "ru", "zh-Hant" and so on. + /// In case of error returns an empty string. inline string GetTurnNotificationsLocale() const { return m_routingSession.GetTurnNotificationsLocale(); } /// \brief When an end user is going to a turn he gets sound turn instructions. /// If C++ part wants the client to pronounce an instruction GenerateTurnSound (in turnNotifications) returns diff --git a/platform/get_text_by_id.cpp b/platform/get_text_by_id.cpp index 9abda6c21d..a14da57200 100644 --- a/platform/get_text_by_id.cpp +++ b/platform/get_text_by_id.cpp @@ -11,6 +11,8 @@ namespace { +string const kDefaultLanguage = "en"; + string GetTextSourceString(platform::TextSource textSouce) { #if defined(OMIM_OS_ANDROID) || defined(OMIM_OS_IPHONE) @@ -28,30 +30,62 @@ string GetTextSourceString(platform::TextSource textSouce) #endif ASSERT(false, ()); } -} // namespace -namespace platform -{ -GetTextById::GetTextById(TextSource textSouce, string const & localeName) +bool GetJsonBuffer(platform::TextSource textSouce, string const & localeName, string & jsonBuffer) { string const pathToJson = my::JoinFoldersToPath( {GetTextSourceString(textSouce), localeName + ".json"}, "localize.json"); - // @TODO(vbykoianko) Add assert if locale path pathToJson is not valid. - string jsonBuffer; try { + jsonBuffer.clear(); GetPlatform().GetReader(pathToJson)->ReadAsString(jsonBuffer); } catch (RootException const & ex) { - LOG(LWARNING, ("Can't open sound instructions file", pathToJson, ex.what())); - return; + LOG(LWARNING, ("Can't open", localeName,"sound instructions file. pathToJson is", + pathToJson, ex.what())); + return false; // No json file for localeName } - InitFromJson(jsonBuffer); + return true; +} +} // namespace + +namespace platform +{ +unique_ptr MakeGetTextById(string const & jsonBuffer, string const & localeName) +{ + unique_ptr result(new GetTextById(jsonBuffer, localeName)); + if (!result || !result->IsValid()) + { + ASSERT(false, ()); + return nullptr; + } + return result; } -GetTextById::GetTextById(string const & jsonBuffer) { InitFromJson(jsonBuffer); } +unique_ptr GetTextByIdFactory(TextSource textSouce, string const & localeName) +{ + string jsonBuffer; + if (GetJsonBuffer(textSouce, localeName, jsonBuffer)) + return MakeGetTextById(jsonBuffer, localeName); + + if (GetJsonBuffer(textSouce, kDefaultLanguage, jsonBuffer)) + return MakeGetTextById(jsonBuffer, kDefaultLanguage); + + ASSERT(false, ("sound.txt does not contain default language.")); + return nullptr; +} + +unique_ptr ForTestingGetTextByIdFactory(string const & jsonBuffer, string const & localeName) +{ + return MakeGetTextById(jsonBuffer, localeName); +} + +GetTextById::GetTextById(string const & jsonBuffer, string const & localeName) : m_locale(localeName) +{ + InitFromJson(jsonBuffer); +} void GetTextById::InitFromJson(string const & jsonBuffer) { @@ -84,7 +118,7 @@ void GetTextById::InitFromJson(string const & jsonBuffer) string GetTextById::operator()(string const & textId) const { if (!IsValid()) - return ""; + return string(); auto const textIt = m_localeTexts.find(textId); if (textIt == m_localeTexts.end()) diff --git a/platform/get_text_by_id.hpp b/platform/get_text_by_id.hpp index 7d1c5cac50..50c5184a46 100644 --- a/platform/get_text_by_id.hpp +++ b/platform/get_text_by_id.hpp @@ -1,6 +1,7 @@ #pragma once #include "std/string.hpp" +#include "std/unique_ptr.hpp" #include "std/unordered_map.hpp" #include "std/utility.hpp" @@ -18,19 +19,30 @@ enum class TextSource class GetTextById { public: - GetTextById(TextSource textSouce, string const & localeName); - /// The constructor is used for writing unit tests only. - explicit GetTextById(string const & jsonBuffer); - - bool IsValid() const { return !m_localeTexts.empty(); } /// @return a pair of a text string in a specified locale for textId and a boolean flag. /// If textId is found in m_localeTexts then the boolean flag is set to true. /// The boolean flag is set to false otherwise. string operator()(string const & textId) const; - + string GetLocale() const { return m_locale; } private: - void InitFromJson(string const & jsonBuffer); + friend unique_ptr GetTextByIdFactory(TextSource textSouce, string const & localeName); + friend unique_ptr ForTestingGetTextByIdFactory(string const & jsonBuffer, string const & localeName); + friend unique_ptr MakeGetTextById(string const & jsonBuffer, string const & localeName); + GetTextById(string const & jsonBuffer, string const & localeName); + void InitFromJson(string const & jsonBuffer); + /// \note IsValid is used only in factories and shall be private. + bool IsValid() const { return !m_localeTexts.empty(); } + + string m_locale; unordered_map m_localeTexts; }; + +/// Factoris to create intances of GetTextById. +/// If unique_ptr is created by GetTextByIdFactory or ForTestingGetTextByIdFactory +/// threre are only two possibities. +/// * a factory returns a valid instance +/// * a factory returns nullptr +unique_ptr GetTextByIdFactory(TextSource textSouce, string const & localeName); +unique_ptr ForTestingGetTextByIdFactory(string const & jsonBuffer, string const & localeName); } // namespace platform diff --git a/routing/turns_tts_text.cpp b/routing/turns_tts_text.cpp index 25caeabd75..cb2e302d32 100644 --- a/routing/turns_tts_text.cpp +++ b/routing/turns_tts_text.cpp @@ -36,16 +36,14 @@ namespace sound { void GetTtsText::SetLocale(string const & locale) { - m_locale = locale; - m_getCurLang.reset(new platform::GetTextById(platform::TextSource::TtsSound, locale)); - /// @todo Factor out file check from constructor and do not create m_getCurLang object in case of error. + m_getCurLang = platform::GetTextByIdFactory(platform::TextSource::TtsSound, locale); ASSERT(m_getCurLang, ()); } -void GetTtsText::ForTestingSetLocaleWithJson(string const & jsonBuffer) +void GetTtsText::ForTestingSetLocaleWithJson(string const & jsonBuffer, string const & locale) { - m_getCurLang.reset(new platform::GetTextById(jsonBuffer)); - ASSERT(m_getCurLang && m_getCurLang->IsValid(), ()); + m_getCurLang = platform::ForTestingGetTextByIdFactory(jsonBuffer, locale); + ASSERT(m_getCurLang, ()); } string GetTtsText::operator()(Notification const & notification) const @@ -63,11 +61,21 @@ string GetTtsText::operator()(Notification const & notification) const return distStr + " " + dirStr; } +string GetTtsText::GetLocale() const +{ + if (m_getCurLang) + { + ASSERT(false, ()); + return string(); + } + return m_getCurLang->GetLocale(); +} + string GetTtsText::GetTextById(string const & textId) const { ASSERT(!textId.empty(), ()); - if (!m_getCurLang || !m_getCurLang->IsValid()) + if (!m_getCurLang) { ASSERT(false, ()); return ""; diff --git a/routing/turns_tts_text.hpp b/routing/turns_tts_text.hpp index 43390e7463..880da596c7 100644 --- a/routing/turns_tts_text.hpp +++ b/routing/turns_tts_text.hpp @@ -22,16 +22,14 @@ class GetTtsText { public: string operator()(Notification const & notification) const; - /// TODO(vbykoianko) Check if locale is available. If not use default (en) locale. void SetLocale(string const & locale); - inline string GetLocale() const { return m_locale; } - void ForTestingSetLocaleWithJson(string const & jsonBuffer); + string GetLocale() const; + void ForTestingSetLocaleWithJson(string const & jsonBuffer, string const & locale); private: string GetTextById(string const & textId) const; unique_ptr m_getCurLang; - string m_locale; }; /// Generates text message id about the distance of the notification. For example: In 300 meters.