diff --git a/Telegram/Resources/langs/cloud_lang.strings b/Telegram/Resources/langs/cloud_lang.strings index 1abb893e7..c9d4af958 100644 --- a/Telegram/Resources/langs/cloud_lang.strings +++ b/Telegram/Resources/langs/cloud_lang.strings @@ -35,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "cloud_lng_passport_in_it" = "Italian"; "cloud_lng_passport_in_ja" = "Japanese"; "cloud_lng_passport_in_ka" = "Georgian"; -"cloud_lng_passport_in_km" = "Khmer"; +// "cloud_lng_passport_in_km" = "Khmer"; "cloud_lng_passport_in_ko" = "Korean"; "cloud_lng_passport_in_lo" = "Lao"; "cloud_lng_passport_in_lt" = "Lithuanian"; @@ -58,3 +58,99 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "cloud_lng_passport_in_uk" = "Ukrainian"; "cloud_lng_passport_in_uz" = "Uzbek"; "cloud_lng_passport_in_vi" = "Vietnamese"; + +"cloud_lng_translate_to_ar" = "Arabic"; +"cloud_lng_translate_to_az" = "Azerbaijani"; +"cloud_lng_translate_to_bg" = "Bulgarian"; +// "cloud_lng_translate_to_bn" = "Bangla"; +"cloud_lng_translate_to_cs" = "Czech"; +"cloud_lng_translate_to_da" = "Danish"; +"cloud_lng_translate_to_de" = "German"; +// "cloud_lng_translate_to_dv" = "Divehi"; +// "cloud_lng_translate_to_dz" = "Dzongkha"; +"cloud_lng_translate_to_el" = "Greek"; +"cloud_lng_translate_to_en" = "English"; +"cloud_lng_translate_to_es" = "Spanish"; +"cloud_lng_translate_to_et" = "Estonian"; +"cloud_lng_translate_to_fa" = "Persian"; +"cloud_lng_translate_to_fr" = "French"; +"cloud_lng_translate_to_he" = "Hebrew"; +"cloud_lng_translate_to_hr" = "Croatian"; +"cloud_lng_translate_to_hu" = "Hungarian"; +"cloud_lng_translate_to_hy" = "Armenian"; +"cloud_lng_translate_to_id" = "Indonesian"; +"cloud_lng_translate_to_is" = "Icelandic"; +"cloud_lng_translate_to_it" = "Italian"; +"cloud_lng_translate_to_ja" = "Japanese"; +"cloud_lng_translate_to_ka" = "Georgian"; +// "cloud_lng_translate_to_km" = "Khmer"; +"cloud_lng_translate_to_ko" = "Korean"; +"cloud_lng_translate_to_lo" = "Lao"; +"cloud_lng_translate_to_lt" = "Lithuanian"; +"cloud_lng_translate_to_lv" = "Latvian"; +"cloud_lng_translate_to_mk" = "Macedonian"; +"cloud_lng_translate_to_mn" = "Mongolian"; +"cloud_lng_translate_to_ms" = "Malay"; +"cloud_lng_translate_to_my" = "Burmese"; +"cloud_lng_translate_to_ne" = "Nepali"; +"cloud_lng_translate_to_nl" = "Dutch"; +"cloud_lng_translate_to_pl" = "Polish"; +"cloud_lng_translate_to_pt" = "Portuguese"; +"cloud_lng_translate_to_ro" = "Romanian"; +"cloud_lng_translate_to_ru" = "Russian"; +"cloud_lng_translate_to_sk" = "Slovak"; +"cloud_lng_translate_to_sl" = "Slovenian"; +"cloud_lng_translate_to_th" = "Thai"; +"cloud_lng_translate_to_tk" = "Turkmen"; +"cloud_lng_translate_to_tr" = "Turkish"; +"cloud_lng_translate_to_uk" = "Ukrainian"; +"cloud_lng_translate_to_uz" = "Uzbek"; +"cloud_lng_translate_to_vi" = "Vietnamese"; + +"cloud_lng_language_ar" = "Arabic"; +"cloud_lng_language_az" = "Azerbaijani"; +"cloud_lng_language_bg" = "Bulgarian"; +// "cloud_lng_language_bn" = "Bangla"; +"cloud_lng_language_cs" = "Czech"; +"cloud_lng_language_da" = "Danish"; +"cloud_lng_language_de" = "German"; +// "cloud_lng_language_dv" = "Divehi"; +// "cloud_lng_language_dz" = "Dzongkha"; +"cloud_lng_language_el" = "Greek"; +"cloud_lng_language_en" = "English"; +"cloud_lng_language_es" = "Spanish"; +"cloud_lng_language_et" = "Estonian"; +"cloud_lng_language_fa" = "Persian"; +"cloud_lng_language_fr" = "French"; +"cloud_lng_language_he" = "Hebrew"; +"cloud_lng_language_hr" = "Croatian"; +"cloud_lng_language_hu" = "Hungarian"; +"cloud_lng_language_hy" = "Armenian"; +"cloud_lng_language_id" = "Indonesian"; +"cloud_lng_language_is" = "Icelandic"; +"cloud_lng_language_it" = "Italian"; +"cloud_lng_language_ja" = "Japanese"; +"cloud_lng_language_ka" = "Georgian"; +// "cloud_lng_language_km" = "Khmer"; +"cloud_lng_language_ko" = "Korean"; +"cloud_lng_language_lo" = "Lao"; +"cloud_lng_language_lt" = "Lithuanian"; +"cloud_lng_language_lv" = "Latvian"; +"cloud_lng_language_mk" = "Macedonian"; +"cloud_lng_language_mn" = "Mongolian"; +"cloud_lng_language_ms" = "Malay"; +"cloud_lng_language_my" = "Burmese"; +"cloud_lng_language_ne" = "Nepali"; +"cloud_lng_language_nl" = "Dutch"; +"cloud_lng_language_pl" = "Polish"; +"cloud_lng_language_pt" = "Portuguese"; +"cloud_lng_language_ro" = "Romanian"; +"cloud_lng_language_ru" = "Russian"; +"cloud_lng_language_sk" = "Slovak"; +"cloud_lng_language_sl" = "Slovenian"; +"cloud_lng_language_th" = "Thai"; +"cloud_lng_language_tk" = "Turkmen"; +"cloud_lng_language_tr" = "Turkish"; +"cloud_lng_language_uk" = "Ukrainian"; +"cloud_lng_language_uz" = "Uzbek"; +"cloud_lng_language_vi" = "Vietnamese"; diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0f08fdf67..2d940c84f 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2233,8 +2233,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_translate_show_original" = "Show Original"; "lng_translate_bar_to" = "Translate to {name}"; +"lng_translate_bar_to_other" = "Translate to {name}"; "lng_translate_menu_to" = "Translate To"; "lng_translate_menu_dont" = "Don't translate {name}"; +"lng_translate_menu_dont_other" = "Don't translate {name}"; "lng_translate_menu_hide" = "Hide"; "lng_translate_hidden_user" = "Translation bar is now hidden for this chat."; "lng_translate_hidden_group" = "Translation bar is now hidden for this group."; @@ -3389,6 +3391,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_translate_settings_chat" = "Translate Entire Chat"; "lng_translate_settings_choose" = "Do Not Translate"; "lng_translate_settings_about" = "The 'Translate' button will appear when you open a context menu on a text message."; +"lng_translate_settings_one" = "Please choose at least one language so that it can be used as the \"Translate to\" language."; "lng_launch_exe_warning" = "This file has a {extension} extension.\nAre you sure you want to run it?"; "lng_launch_svg_warning" = "Opening this file can potentially expose your IP address to its sender. Continue?"; diff --git a/Telegram/SourceFiles/boxes/translate_box.cpp b/Telegram/SourceFiles/boxes/translate_box.cpp index 20557206e..07af0a754 100644 --- a/Telegram/SourceFiles/boxes/translate_box.cpp +++ b/Telegram/SourceFiles/boxes/translate_box.cpp @@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/core_settings.h" #include "core/ui_integration.h" #include "data/data_peer.h" +#include "data/data_session.h" +#include "history/history.h" #include "lang/lang_instance.h" #include "lang/lang_keys.h" #include "main/main_session.h" @@ -96,9 +98,15 @@ void TranslateBox( box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); }); const auto container = box->verticalLayout(); - auto id = Core::App().settings().translateToValue(); - const auto api = box->lifetime().make_state( - &peer->session().mtp()); + struct State { + State(not_null session) : api(&session->mtp()) { + } + + MTP::Sender api; + rpl::variable to; + }; + const auto state = box->lifetime().make_state(&peer->session()); + state->to = ChooseTranslateTo(peer->owner().history(peer)); text.entities = ranges::views::all( text.entities @@ -174,10 +182,10 @@ void TranslateBox( const auto padding = st::settingsSubsectionTitlePadding; const auto subtitle = Settings::AddSubsectionTitle( container, - rpl::duplicate(id) | rpl::map(LanguageName)); + state->to.value() | rpl::map(LanguageName)); // Workaround. - rpl::duplicate(id) | rpl::start_with_next([=] { + state->to.value() | rpl::start_with_next([=] { subtitle->resizeToWidth(container->width() - padding.left() - padding.right()); @@ -197,7 +205,7 @@ void TranslateBox( box, st::aboutLabel, std::min(original->entity()->height() / lineHeight, kMaxLines), - rpl::duplicate(id) | rpl::map([=](LanguageId id) { + state->to.value() | rpl::map([=](LanguageId id) { return id.locale().textDirection() == Qt::RightToLeft; })))); @@ -210,7 +218,7 @@ void TranslateBox( const auto send = [=](LanguageId to) { loading->show(anim::type::instant); translated->hide(anim::type::instant); - api->request(MTPmessages_TranslateText( + state->api.request(MTPmessages_TranslateText( MTP_flags(flags), msgId ? peer->input : MTP_inputPeerEmpty(), (msgId @@ -221,7 +229,7 @@ void TranslateBox( : MTP_vector(1, MTP_textWithEntities( MTP_string(text.text), MTP_vector()))), - MTP_string(to.locale().name().mid(0, 2)) + MTP_string(to.twoLetterCode()) )).done([=](const MTPmessages_TranslatedText &result) { const auto &data = result.data(); const auto &list = data.vresult().v; @@ -232,13 +240,15 @@ void TranslateBox( showText(tr::lng_translate_box_error(tr::now)); }).send(); }; - std::move(id) | rpl::start_with_next(send, box->lifetime()); + state->to.value() | rpl::start_with_next(send, box->lifetime()); box->addLeftButton(tr::lng_settings_language(), [=] { if (loading->toggled()) { return; } - Ui::BoxShow(box).showBox(ChooseTranslateToBox()); + Ui::BoxShow(box).showBox(ChooseTranslateToBox( + state->to.current(), + crl::guard(box, [=](LanguageId id) { state->to = id; }))); }); } @@ -283,17 +293,50 @@ object_ptr EditSkipTranslationLanguages() { }, Core::App().settings().skipTranslationLanguages(), true); } -object_ptr ChooseTranslateToBox() { - const auto selected = std::vector{ +object_ptr ChooseTranslateToBox( + LanguageId bringUp, + Fn callback) { + auto selected = std::vector{ Core::App().settings().translateTo(), }; + if (bringUp && bringUp != selected.front()) { + selected.push_back(bringUp); + } return Box(ChooseLanguageBox, tr::lng_languages(), [=]( const std::vector &ids) { Expects(!ids.empty()); - Core::App().settings().setTranslateTo(ids.front()); + const auto id = ids.front(); + Core::App().settings().setTranslateTo(id); Core::App().saveSettingsDelayed(); + callback(id); }, selected, false); } +LanguageId ChooseTranslateTo(not_null history) { + return ChooseTranslateTo(history->translateOfferedFrom()); +} + +LanguageId ChooseTranslateTo(LanguageId offeredFrom) { + auto &settings = Core::App().settings(); + return ChooseTranslateTo( + offeredFrom, + settings.translateTo(), + settings.skipTranslationLanguages()); +} + +LanguageId ChooseTranslateTo( + not_null history, + LanguageId savedTo, + const std::vector &skip) { + return ChooseTranslateTo(history->translateOfferedFrom(), savedTo, skip); +} + +LanguageId ChooseTranslateTo( + LanguageId offeredFrom, + LanguageId savedTo, + const std::vector &skip) { + return (offeredFrom != savedTo) ? savedTo : skip.front(); +} + } // namespace Ui diff --git a/Telegram/SourceFiles/boxes/translate_box.h b/Telegram/SourceFiles/boxes/translate_box.h index 04df4b1f1..1e3bade46 100644 --- a/Telegram/SourceFiles/boxes/translate_box.h +++ b/Telegram/SourceFiles/boxes/translate_box.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/object_ptr.h" +class History; class PeerData; struct LanguageId; @@ -27,6 +28,19 @@ void TranslateBox( [[nodiscard]] bool SkipTranslate(TextWithEntities textWithEntities); [[nodiscard]] object_ptr EditSkipTranslationLanguages(); -[[nodiscard]] object_ptr ChooseTranslateToBox(); +[[nodiscard]] object_ptr ChooseTranslateToBox( + LanguageId bringUp, + Fn callback); + +[[nodiscard]] LanguageId ChooseTranslateTo(not_null history); +[[nodiscard]] LanguageId ChooseTranslateTo(LanguageId offeredFrom); +[[nodiscard]] LanguageId ChooseTranslateTo( + not_null history, + LanguageId savedTo, + const std::vector &skip); +[[nodiscard]] LanguageId ChooseTranslateTo( + LanguageId offeredFrom, + LanguageId savedTo, + const std::vector &skip); } // namespace Ui diff --git a/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp b/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp index 3fb8d8067..a9e01fd57 100644 --- a/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp +++ b/Telegram/SourceFiles/history/view/history_view_translate_bar.cpp @@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "lang/lang_keys.h" #include "main/main_session.h" -#include "spellcheck/spellcheck_types.h" #include "ui/effects/ripple_animation.h" #include "ui/boxes/choose_language_box.h" // EditSkipTranslationLanguages. #include "ui/layers/box_content.h" @@ -294,14 +293,6 @@ void TranslateBar::setup(not_null history) { : Core::App().settings().translateTo()); }); - Core::App().settings().translateToValue( - ) | rpl::filter([=](LanguageId should) { - const auto now = history->translatedTo(); - return now && (now != should); - }) | rpl::start_with_next([=](LanguageId should) { - translateTo(should); - }, _wrap.lifetime()); - const auto label = Ui::CreateChild( button, st::historyTranslateLabel); @@ -343,8 +334,34 @@ void TranslateBar::setup(not_null history) { updateLabelGeometry(); }, lifetime()); - rpl::combine( + _overridenTo = history->translatedTo(); + _to = rpl::combine( Core::App().settings().translateToValue(), + Core::App().settings().skipTranslationLanguagesValue(), + history->session().changes().historyFlagsValue( + history, + Data::HistoryUpdate::Flag::TranslateFrom), + _overridenTo.value() + ) | rpl::map([=]( + LanguageId to, + const std::vector &skip, + const auto &, + LanguageId overridenTo) { + return overridenTo + ? overridenTo + : Ui::ChooseTranslateTo(history, to, skip); + }) | rpl::distinct_until_changed(); + + _to.value( + ) | rpl::filter([=](LanguageId should) { + const auto now = history->translatedTo(); + return now && (now != should); + }) | rpl::start_with_next([=](LanguageId should) { + translateTo(should); + }, _wrap.lifetime()); + + rpl::combine( + _to.value(), history->session().changes().historyFlagsValue( history, (Data::HistoryUpdate::Flag::TranslatedTo @@ -352,16 +369,17 @@ void TranslateBar::setup(not_null history) { history->session().changes().peerFlagsValue( history->peer, Data::PeerUpdate::Flag::TranslationDisabled) - ) | rpl::map([=](LanguageId to, const auto&, const auto&) { + ) | rpl::map([=]( + LanguageId to, + const auto&, + const auto&) { using Flag = PeerData::TranslationFlag; return (history->peer->translationFlag() != Flag::Enabled) ? rpl::single(QString()) : history->translatedTo() ? tr::lng_translate_show_original() : history->translateOfferedFrom() - ? tr::lng_translate_bar_to( - lt_name, - rpl::single(Ui::LanguageName(to))) + ? Ui::TranslateBarTo(to) : rpl::single(QString()); }) | rpl::flatten_latest( ) | rpl::distinct_until_changed( @@ -408,20 +426,25 @@ void TranslateBar::showMenu(base::unique_qptr menu) { _menu = std::move(menu); _menu->setForcedOrigin(Ui::PanelAnimation::Origin::TopRight); + const auto guard = Ui::MakeWeak(&_wrap); + const auto now = _history->translatedTo(); + const auto to = now ? now : Ui::ChooseTranslateTo(_history); const auto weak = base::make_weak(_controller); const auto chooseCallback = [=] { if (const auto strong = weak.get()) { - strong->show(Ui::ChooseTranslateToBox()); + strong->show(Ui::ChooseTranslateToBox( + to, + crl::guard(guard, [=](LanguageId id) { _overridenTo = id; }) + )); } }; _menu->addAction(MakeTranslateToItem( _menu->menu(), - Ui::LanguageName(Core::App().settings().translateTo()), + Ui::LanguageName(to ? to : Ui::ChooseTranslateTo(_history)), chooseCallback)); _menu->addSeparator(); const auto history = _history; if (const auto translateOfferedFrom = _history->translateOfferedFrom()) { - const auto name = Ui::LanguageName(translateOfferedFrom); const auto addToIgnoreList = [=] { showSettingsToast(history->peer, translateOfferedFrom); @@ -436,7 +459,7 @@ void TranslateBar::showMenu(base::unique_qptr menu) { Core::App().saveSettingsDelayed(); }; _menu->addAction( - tr::lng_translate_menu_dont(tr::now, lt_name, name), + Ui::TranslateMenuDont(tr::now, translateOfferedFrom), addToIgnoreList, &st::menuIconBlock); } diff --git a/Telegram/SourceFiles/history/view/history_view_translate_bar.h b/Telegram/SourceFiles/history/view/history_view_translate_bar.h index 62846100a..a50c0fc71 100644 --- a/Telegram/SourceFiles/history/view/history_view_translate_bar.h +++ b/Telegram/SourceFiles/history/view/history_view_translate_bar.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "ui/wrap/slide_wrap.h" +#include "spellcheck/spellcheck_types.h" class History; struct LanguageId; @@ -68,6 +69,8 @@ private: std::unique_ptr _shadow; Fn _shadowGeometryPostprocess; base::unique_qptr _menu; + rpl::variable _overridenTo; + rpl::variable _to; bool _shouldBeShown = false; bool _forceHidden = false; diff --git a/Telegram/SourceFiles/history/view/history_view_translate_tracker.cpp b/Telegram/SourceFiles/history/view/history_view_translate_tracker.cpp index a75a95e09..77fcbfa10 100644 --- a/Telegram/SourceFiles/history/view/history_view_translate_tracker.cpp +++ b/Telegram/SourceFiles/history/view/history_view_translate_tracker.cpp @@ -234,7 +234,7 @@ void TranslateTracker::requestSome() { peer->input, MTP_vector(list), MTPVector(), - MTP_string(to.locale().name().mid(0, 2)) + MTP_string(to.twoLetterCode()) )).done([=](const MTPmessages_TranslatedText &result) { requestDone(to, result.data().vresult().v); }).fail([=] { diff --git a/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp b/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp index 403b24231..6f90d8f73 100644 --- a/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/choose_language_box.cpp @@ -22,7 +22,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { namespace { -const auto kLanguageNamePrefix = "cloud_lng_passport_in_"; +const auto kLanguageNamePrefix = "cloud_lng_language_"; +const auto kTranslateToPrefix = "cloud_lng_translate_to_"; [[nodiscard]] std::vector TranslationLanguagesList() { // If adding some languages here you need to check that it is @@ -214,12 +215,13 @@ QString LanguageNameTranslated(const QString &twoLetterCode) { kLanguageNamePrefix + twoLetterCode.toUtf8()); } +QString LanguageNameLocal(LanguageId id) { + return QLocale::languageToString(id.language()); +} + QString LanguageName(LanguageId id) { - const auto code = id.locale().name().toLower().mid(0, 2); - const auto translated = LanguageNameTranslated(code); - return translated.isEmpty() - ? QLocale::languageToString(id.locale().language()) - : translated; + const auto translated = LanguageNameTranslated(id.twoLetterCode()); + return translated.isEmpty() ? LanguageNameLocal(id) : translated; } QString LanguageNameNative(LanguageId id) { @@ -236,6 +238,29 @@ QString LanguageNameNative(LanguageId id) { } } +rpl::producer TranslateBarTo(LanguageId id) { + const auto translated = Lang::GetNonDefaultValue( + kTranslateToPrefix + id.twoLetterCode().toUtf8()); + return (translated.isEmpty() + ? tr::lng_translate_bar_to_other + : tr::lng_translate_bar_to)( + lt_name, + rpl::single(translated.isEmpty() + ? LanguageNameLocal(id) + : translated)); +} + +QString TranslateMenuDont(tr::now_t, LanguageId id) { + const auto translated = Lang::GetNonDefaultValue( + kTranslateToPrefix + id.twoLetterCode().toUtf8()); + return (translated.isEmpty() + ? tr::lng_translate_menu_dont_other + : tr::lng_translate_menu_dont)( + tr::now, + lt_name, + translated.isEmpty() ? LanguageNameLocal(id) : translated); +} + void ChooseLanguageBox( not_null box, rpl::producer title, @@ -256,6 +281,9 @@ void ChooseLanguageBox( const auto container = box->verticalLayout(); const auto langs = [&] { auto list = TranslationLanguagesList(); + for (const auto id : list) { + LOG(("cloud_lng_language_%1").arg(id.twoLetterCode())); + } const auto current = LanguageId{ QLocale( Lang::LanguageIdOrDefault(Lang::Id())).language() }; if (const auto i = ranges::find(list, current); i != end(list)) { diff --git a/Telegram/SourceFiles/ui/boxes/choose_language_box.h b/Telegram/SourceFiles/ui/boxes/choose_language_box.h index afbe8d5f9..f33af2b2b 100644 --- a/Telegram/SourceFiles/ui/boxes/choose_language_box.h +++ b/Telegram/SourceFiles/ui/boxes/choose_language_box.h @@ -9,14 +9,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL struct LanguageId; +namespace tr { +struct now_t; +} // namespace tr + namespace Ui { class GenericBox; [[nodiscard]] QString LanguageNameTranslated(const QString &twoLetterCode); +[[nodiscard]] QString LanguageNameLocal(LanguageId id); [[nodiscard]] QString LanguageName(LanguageId id); [[nodiscard]] QString LanguageNameNative(LanguageId id); +[[nodiscard]] rpl::producer TranslateBarTo(LanguageId id); +[[nodiscard]] QString TranslateMenuDont(tr::now_t, LanguageId id); + void ChooseLanguageBox( not_null box, rpl::producer title, diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck index 55ed1489f..ae89fefd2 160000 --- a/Telegram/lib_spellcheck +++ b/Telegram/lib_spellcheck @@ -1 +1 @@ -Subproject commit 55ed1489ffac0b60adbd427c2b76be1ce55194e7 +Subproject commit ae89fefd239ecc47d4dab7ba29f9e230376a57d3