| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/live_caption/live_translate_controller.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/metrics_hashes.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/values.h" |
| #include "components/live_caption/features.h" |
| #include "components/live_caption/pref_names.h" |
| #include "components/live_caption/translation_dispatcher.h" |
| #include "components/live_caption/translation_util.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_change_registrar.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/soda/constants.h" |
| #include "components/sync_preferences/pref_service_syncable.h" |
| #include "content/public/browser/browser_context.h" |
| #include "google_apis/google_api_keys.h" |
| #include "media/mojo/mojom/speech_recognition_result.h" |
| |
| namespace captions { |
| namespace { |
| |
| constexpr char kLiveOnDeviceTranslateDispatcherResult[] = |
| "Accessibility.LiveTranslate.OnDeviceTranslation.Result"; |
| constexpr char kLiveGoogleApiTranslateDispatcherResult[] = |
| "Accessibility.LiveTranslate.GoogleApiTranslation.Result"; |
| constexpr char kLiveOnDeviceTranslateDispatcherLatency[] = |
| "Accessibility.LiveTranslate.OnDeviceTranslation.Latency"; |
| constexpr char kLiveGoogleApiTranslateDispatcherLatency[] = |
| "Accessibility.LiveTranslate.GoogleApiTranslation.Latency"; |
| constexpr char kLiveTotalTranslationLatency[] = |
| "Accessibility.LiveTranslate.GetTranslation.Latency"; |
| |
| } // namespace |
| |
| LiveTranslateController::LiveTranslateController( |
| PrefService* profile_prefs, |
| std::unique_ptr<TranslationDispatcher> on_device_dispatcher, |
| std::unique_ptr<TranslationDispatcher> google_api_dispatcher) |
| : profile_prefs_(profile_prefs), |
| pref_change_registrar_(std::make_unique<PrefChangeRegistrar>()), |
| on_device_dispatcher_(std::move(on_device_dispatcher)), |
| google_api_dispatcher_(std::move(google_api_dispatcher)) { |
| pref_change_registrar_->Init(profile_prefs_); |
| pref_change_registrar_->Add( |
| prefs::kLiveTranslateEnabled, |
| base::BindRepeating( |
| &LiveTranslateController::OnLiveTranslateEnabledChanged, |
| // Unretained is safe because |this| owns |pref_change_registrar_|. |
| base::Unretained(this))); |
| } |
| |
| LiveTranslateController::~LiveTranslateController() = default; |
| // static |
| void LiveTranslateController::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| registry->RegisterBooleanPref(prefs::kLiveTranslateEnabled, false); |
| |
| registry->RegisterStringPref(prefs::kLiveTranslateTargetLanguageCode, |
| speech::kEnglishLocaleNoCountry); |
| } |
| |
| void LiveTranslateController::GetTranslation(const std::string& result, |
| std::string source_language, |
| std::string target_language, |
| TranslateEventCallback callback) { |
| base::UmaHistogramSparse( |
| "Accessibility.LiveTranslate.GetTranslation.SourceLanguage", |
| base::HashMetricName( |
| speech::GetBCP47LanguageCodeFromSodaLanguage(source_language) |
| .value_or(source_language))); |
| base::UmaHistogramSparse( |
| "Accessibility.LiveTranslate.GetTranslation.TargetLanguage", |
| base::HashMetricName(target_language)); |
| |
| base::TimeTicks total_start_time = base::TimeTicks::Now(); |
| bool use_on_device = base::FeatureList::IsEnabled( |
| live_caption::kLiveCaptionOnDeviceTranslation); |
| if (use_on_device && |
| base::FeatureList::IsEnabled( |
| live_caption::kLiveCaptionOnDeviceTranslationEnglishOnly)) { |
| auto is_english = [](const std::string& lang) { |
| return base::StartsWith(lang, "en", base::CompareCase::INSENSITIVE_ASCII); |
| }; |
| auto is_zh_tw = [](const std::string& lang) { |
| return base::ToLowerASCII(lang) == "zh-tw" || |
| base::ToLowerASCII(lang) == "cmn-hant-tw"; |
| }; |
| |
| if (!(is_english(source_language) || is_english(target_language)) || |
| is_zh_tw(source_language) || is_zh_tw(target_language)) { |
| use_on_device = false; |
| } |
| } |
| |
| if (use_on_device) { |
| on_device_dispatcher_->GetTranslation( |
| result, source_language, target_language, |
| base::BindOnce(&LiveTranslateController::OnOnDeviceTranslated, |
| weak_factory_.GetWeakPtr(), result, source_language, |
| target_language, std::move(callback), total_start_time)); |
| } else { |
| google_api_dispatcher_->GetTranslation( |
| result, source_language, target_language, |
| base::BindOnce(&LiveTranslateController::OnGoogleApiTranslated, |
| weak_factory_.GetWeakPtr(), std::move(callback), |
| total_start_time, total_start_time)); |
| } |
| } |
| |
| void LiveTranslateController::OnOnDeviceTranslated( |
| std::string_view result, |
| std::string_view source_language, |
| std::string_view target_language, |
| TranslateEventCallback callback, |
| base::TimeTicks total_start_time, |
| const TranslateEvent& translate_event) { |
| base::UmaHistogramBoolean(kLiveOnDeviceTranslateDispatcherResult, |
| translate_event.has_value()); |
| if (!translate_event.has_value() && google_api_dispatcher_) { |
| google_api_dispatcher_->GetTranslation( |
| result, source_language, target_language, |
| base::BindOnce(&LiveTranslateController::OnGoogleApiTranslated, |
| weak_factory_.GetWeakPtr(), std::move(callback), |
| total_start_time, base::TimeTicks::Now())); |
| return; |
| } |
| |
| if (translate_event.has_value()) { |
| base::TimeDelta latency = base::TimeTicks::Now() - total_start_time; |
| base::UmaHistogramTimes(kLiveOnDeviceTranslateDispatcherLatency, latency); |
| base::UmaHistogramTimes(kLiveTotalTranslationLatency, latency); |
| } |
| std::move(callback).Run(translate_event); |
| } |
| |
| void LiveTranslateController::OnGoogleApiTranslated( |
| TranslateEventCallback callback, |
| base::TimeTicks total_start_time, |
| base::TimeTicks google_api_start_time, |
| const TranslateEvent& translate_event) { |
| base::UmaHistogramBoolean(kLiveGoogleApiTranslateDispatcherResult, |
| translate_event.has_value()); |
| if (translate_event.has_value()) { |
| base::TimeTicks now = base::TimeTicks::Now(); |
| base::UmaHistogramTimes(kLiveGoogleApiTranslateDispatcherLatency, |
| now - google_api_start_time); |
| base::UmaHistogramTimes(kLiveTotalTranslationLatency, |
| now - total_start_time); |
| } |
| std::move(callback).Run(translate_event); |
| } |
| |
| void LiveTranslateController::OnLiveTranslateEnabledChanged() { |
| if (profile_prefs_->GetBoolean(prefs::kLiveTranslateEnabled)) |
| profile_prefs_->SetBoolean(prefs::kLiveCaptionEnabled, true); |
| } |
| |
| } // namespace captions |