blob: 94d9546eab081ce70c84a9e13b6807e9306d57b6 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/language/language_model_manager_factory.h"
#include <memory>
#include "base/feature_list.h"
#include "base/task/thread_pool.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/language/url_language_histogram_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/language/content/browser/geo_language_model.h"
#include "components/language/content/browser/geo_language_provider.h"
#include "components/language/core/browser/language_model_manager.h"
#include "components/language/core/browser/language_prefs.h"
#include "components/language/core/browser/locale_util.h"
#include "components/language/core/browser/pref_names.h"
#include "components/language/core/browser/ulp_metrics_logger.h"
#include "components/language/core/browser/url_language_histogram.h"
#include "components/language/core/common/language_util.h"
#include "components/language/core/language_model/fluent_language_model.h"
#include "components/language/core/language_model/ulp_language_model.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/translate/core/browser/translate_prefs.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "chrome/browser/language/android/language_bridge.h"
using language::ULPMetricsLogger;
#endif
namespace {
#if BUILDFLAG(IS_ANDROID)
// Records per-initialization ULP-related metrics.
void RecordULPInitMetrics(
PrefService* pref_service,
const language::UrlLanguageHistogram& page_language_histogram,
const std::vector<std::string>& ulp_languages) {
language::ULPMetricsLogger logger;
logger.RecordInitiationLanguageCount(ulp_languages.size());
const std::string app_locale = g_browser_process->GetApplicationLocale();
logger.RecordInitiationUILanguageInULP(
ULPMetricsLogger::DetermineLanguageStatus(app_locale, ulp_languages));
const std::string target_language =
translate::TranslatePrefs(pref_service).GetRecentTargetLanguage();
logger.RecordInitiationTranslateTargetInULP(
ULPMetricsLogger::DetermineLanguageStatus(target_language,
ulp_languages));
std::vector<std::string> accept_languages;
language::LanguagePrefs(pref_service)
.GetAcceptLanguagesList(&accept_languages);
language::ULPLanguageStatus accept_language_status =
language::ULPLanguageStatus::kLanguageEmpty;
if (accept_languages.size() > 0) {
accept_language_status = ULPMetricsLogger::DetermineLanguageStatus(
accept_languages[0], ulp_languages);
}
logger.RecordInitiationTopAcceptLanguageInULP(accept_language_status);
logger.RecordInitiationAcceptLanguagesULPOverlap(
ULPMetricsLogger::LanguagesOverlapRatio(accept_languages, ulp_languages));
std::vector<std::string> never_languages_not_in_ulp =
ULPMetricsLogger::RemoveULPLanguages(
translate::TranslatePrefs(pref_service).GetNeverTranslateLanguages(),
ulp_languages);
logger.RecordInitiationNeverLanguagesMissingFromULP(
never_languages_not_in_ulp);
logger.RecordInitiationNeverLanguagesMissingFromULPCount(
never_languages_not_in_ulp.size());
std::vector<std::string> page_languages;
for (const language::UrlLanguageHistogram::LanguageInfo& language_info :
page_language_histogram.GetTopLanguages()) {
page_languages.emplace_back(language_info.language_code);
}
logger.RecordInitiationAcceptLanguagesPageLanguageOverlap(
ULPMetricsLogger::LanguagesOverlapRatio(page_languages, ulp_languages));
std::vector<std::string> page_languages_not_in_ulp =
ULPMetricsLogger::RemoveULPLanguages(page_languages, ulp_languages);
logger.RecordInitiationPageLanguagesMissingFromULP(page_languages_not_in_ulp);
logger.RecordInitiationPageLanguagesMissingFromULPCount(
page_languages_not_in_ulp.size());
}
void CreateAndAddULPLanguageModel(Profile* profile,
std::vector<std::string> languages) {
PrefService* pref_service = profile->GetPrefs();
language::UrlLanguageHistogram* page_languages =
UrlLanguageHistogramFactory::GetForBrowserContext(profile);
RecordULPInitMetrics(pref_service, *page_languages, languages);
language::LanguagePrefs(pref_service).SetULPLanguages(languages);
std::unique_ptr<language::ULPLanguageModel> ulp_model =
std::make_unique<language::ULPLanguageModel>();
int score_divisor = 1;
for (std::string lang : languages) {
// List of languages is already ordered by preference, generate scores
// accordingly.
ulp_model->AddULPLanguage(lang, 1.0f / score_divisor);
score_divisor++;
}
language::LanguageModelManager* manager =
LanguageModelManagerFactory::GetForBrowserContext(profile);
manager->AddModel(language::LanguageModelManager::ModelType::ULP,
std::move(ulp_model));
}
#endif
void PrepareLanguageModels(Profile* const profile,
language::LanguageModelManager* const manager) {
// Use the GeoLanguageModel as the primary Language Model if its experiment is
// enabled, and the FluentLanguageModel otherwise.
if (language::GetOverrideLanguageModel() ==
language::OverrideLanguageModel::GEO) {
manager->AddModel(language::LanguageModelManager::ModelType::GEO,
std::make_unique<language::GeoLanguageModel>(
language::GeoLanguageProvider::GetInstance()));
manager->SetPrimaryModel(language::LanguageModelManager::ModelType::GEO);
} else {
manager->AddModel(
language::LanguageModelManager::ModelType::FLUENT,
std::make_unique<language::FluentLanguageModel>(profile->GetPrefs()));
manager->SetPrimaryModel(language::LanguageModelManager::ModelType::FLUENT);
}
// On Android, additionally create a ULPLanguageModel and populate it with
// ULP data.
#if BUILDFLAG(IS_ANDROID)
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&language::LanguageBridge::GetULPLanguagesFromDevice,
profile->GetProfileUserName()),
base::BindOnce(&CreateAndAddULPLanguageModel, profile));
#endif
}
} // namespace
// static
LanguageModelManagerFactory* LanguageModelManagerFactory::GetInstance() {
static base::NoDestructor<LanguageModelManagerFactory> instance;
return instance.get();
}
// static
language::LanguageModelManager*
LanguageModelManagerFactory::GetForBrowserContext(
content::BrowserContext* const browser_context) {
return static_cast<language::LanguageModelManager*>(
GetInstance()->GetServiceForBrowserContext(browser_context, true));
}
LanguageModelManagerFactory::LanguageModelManagerFactory()
: ProfileKeyedServiceFactory(
"LanguageModelManager",
// Use the original profile's language model even in Incognito mode.
ProfileSelections::Builder()
.WithRegular(ProfileSelection::kRedirectedToOriginal)
// TODO(crbug.com/40257657): Check if this service is needed in
// Guest mode.
.WithGuest(ProfileSelection::kRedirectedToOriginal)
// TODO(crbug.com/41488885): Check if this service is needed for
// Ash Internals.
.WithAshInternals(ProfileSelection::kRedirectedToOriginal)
.Build()) {}
LanguageModelManagerFactory::~LanguageModelManagerFactory() = default;
std::unique_ptr<KeyedService>
LanguageModelManagerFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* const browser_context) const {
Profile* const profile = Profile::FromBrowserContext(browser_context);
std::unique_ptr<language::LanguageModelManager> manager =
std::make_unique<language::LanguageModelManager>(
profile->GetPrefs(), g_browser_process->GetApplicationLocale());
PrepareLanguageModels(profile, manager.get());
return manager;
}