[go: nahoru, domu]

blob: f9c4bbb5ecdcf351606b95b721c3fc13248910b6 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/ui/settings/language/language_settings_mediator.h"
#import <memory>
#import "base/apple/foundation_util.h"
#import "base/check.h"
#import "base/containers/contains.h"
#import "base/metrics/histogram_macros.h"
#import "base/notreached.h"
#import "base/strings/sys_string_conversions.h"
#import "components/language/core/browser/language_model_manager.h"
#import "components/language/core/browser/pref_names.h"
#import "components/language/core/common/language_util.h"
#import "components/prefs/ios/pref_observer_bridge.h"
#import "components/prefs/pref_change_registrar.h"
#import "components/prefs/pref_service.h"
#import "components/translate/core/browser/translate_pref_names.h"
#import "components/translate/core/browser/translate_prefs.h"
#import "ios/chrome/browser/shared/model/application_context/application_context.h"
#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
#import "ios/chrome/browser/translate/chrome_ios_translate_client.h"
#import "ios/chrome/browser/translate/translate_service_ios.h"
#import "ios/chrome/browser/ui/settings/language/cells/language_item.h"
#import "ios/chrome/browser/ui/settings/language/language_settings_consumer.h"
#import "ios/chrome/browser/ui/settings/language/language_settings_histograms.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ui/base/l10n/l10n_util_mac.h"
@interface LanguageSettingsMediator () <PrefObserverDelegate> {
// Registrar for pref change notifications.
std::unique_ptr<PrefChangeRegistrar> _prefChangeRegistrar;
// Pref observer to track changes to translate::prefs::kOfferTranslateEnabled.
std::unique_ptr<PrefObserverBridge> _offerTranslatePrefObserverBridge;
// Pref observer to track changes to language::prefs::kAcceptLanguages.
std::unique_ptr<PrefObserverBridge> _acceptLanguagesPrefObserverBridge;
// Pref observer to track changes to prefs::kBlockedLanguages.
std::unique_ptr<PrefObserverBridge> _blockedLanguagesPrefObserverBridge;
// Translate wrapper for the PrefService.
std::unique_ptr<translate::TranslatePrefs> _translatePrefs;
}
// The LanguageModelManager passed to this instance.
@property(nonatomic, assign)
language::LanguageModelManager* languageModelManager;
// The PrefService passed to this instance.
@property(nonatomic, assign) PrefService* prefService;
@end
@implementation LanguageSettingsMediator
@synthesize consumer = _consumer;
- (instancetype)initWithLanguageModelManager:
(language::LanguageModelManager*)languageModelManager
prefService:(PrefService*)prefService {
self = [super init];
if (self) {
_languageModelManager = languageModelManager;
_prefService = prefService;
_prefChangeRegistrar = std::make_unique<PrefChangeRegistrar>();
_prefChangeRegistrar->Init(self.prefService);
_offerTranslatePrefObserverBridge =
std::make_unique<PrefObserverBridge>(self);
_offerTranslatePrefObserverBridge->ObserveChangesForPreference(
translate::prefs::kOfferTranslateEnabled, _prefChangeRegistrar.get());
_acceptLanguagesPrefObserverBridge =
std::make_unique<PrefObserverBridge>(self);
_acceptLanguagesPrefObserverBridge->ObserveChangesForPreference(
language::prefs::kAcceptLanguages, _prefChangeRegistrar.get());
_blockedLanguagesPrefObserverBridge =
std::make_unique<PrefObserverBridge>(self);
_blockedLanguagesPrefObserverBridge->ObserveChangesForPreference(
translate::prefs::kBlockedLanguages, _prefChangeRegistrar.get());
_translatePrefs =
ChromeIOSTranslateClient::CreateTranslatePrefs(self.prefService);
}
return self;
}
- (void)dealloc {
// In case this has not been explicitly called.
[self stopObservingModel];
_languageModelManager = nullptr;
_prefService = nullptr;
}
#pragma mark - PrefObserverDelegate
// Called when the value of translate::prefs::kOfferTranslateEnabled,
// language::prefs::kAcceptLanguages or
// translate::prefs::kBlockedLanguages change.
- (void)onPreferenceChanged:(const std::string&)preferenceName {
DCHECK(preferenceName == translate::prefs::kOfferTranslateEnabled ||
preferenceName == language::prefs::kAcceptLanguages ||
preferenceName == translate::prefs::kBlockedLanguages);
// Inform the consumer.
if (preferenceName == translate::prefs::kOfferTranslateEnabled) {
[self.consumer translateEnabled:[self translateEnabled]];
} else {
[self.consumer languagePrefsChanged];
}
}
#pragma mark - LanguageSettingsDataSource
- (NSArray<LanguageItem*>*)acceptLanguagesItems {
// Create a map of supported language codes to supported languages.
std::vector<translate::TranslateLanguageInfo> supportedLanguages;
translate::TranslatePrefs::GetLanguageInfoList(
GetApplicationContext()->GetApplicationLocale(),
_translatePrefs->IsTranslateAllowedByPolicy(), &supportedLanguages);
std::map<std::string, translate::TranslateLanguageInfo> supportedLanguagesMap;
for (const auto& supportedLanguage : supportedLanguages) {
supportedLanguagesMap[supportedLanguage.code] = supportedLanguage;
}
// Get the accept languages.
std::vector<std::string> languageCodes;
_translatePrefs->GetLanguageList(&languageCodes);
NSMutableArray<LanguageItem*>* acceptLanguages =
[NSMutableArray arrayWithCapacity:languageCodes.size()];
for (const auto& languageCode : languageCodes) {
// Ignore unsupported languages.
auto it = supportedLanguagesMap.find(languageCode);
if (it == supportedLanguagesMap.end()) {
// languageCodes comes from a synced pref and may contain language codes
// that are not supported on the platform, or on this device locale as
// defined by the GetLanguageInfoList above.
// Ignore them.
// TODO(crbug.com/1430745): Investigate why this happens and how to
// reconcile data.
continue;
}
const translate::TranslateLanguageInfo& language = it->second;
LanguageItem* languageItem = [self languageItemFromLanguage:language];
// Language codes used in the language settings have the Chrome internal
// format while the Translate target language has the Translate server
// format. To convert the former to the latter the utilily function
// ToTranslateLanguageSynonym() must be used.
std::string canonicalLanguageCode = languageItem.languageCode;
language::ToTranslateLanguageSynonym(&canonicalLanguageCode);
std::string targetLanguageCode = TranslateServiceIOS::GetTargetLanguage(
self.prefService, self.languageModelManager->GetPrimaryModel());
languageItem.targetLanguage = targetLanguageCode == canonicalLanguageCode;
// A language is Translate-blocked if the language is not supported by the
// Translate server, or user is fluent in the language, or it is the
// Translate target language.
languageItem.blocked =
!languageItem.supportsTranslate ||
_translatePrefs->IsBlockedLanguage(languageItem.languageCode) ||
[languageItem isTargetLanguage];
if ([self translateEnabled]) {
// Show a disclosure indicator to suggest language details are available
// as well as a label indicating if the language is Translate-blocked.
languageItem.accessibilityTraits |= UIAccessibilityTraitButton;
languageItem.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
languageItem.trailingDetailText =
[languageItem isBlocked]
? l10n_util::GetNSString(
IDS_IOS_LANGUAGE_SETTINGS_NEVER_TRANSLATE_TITLE)
: l10n_util::GetNSString(
IDS_IOS_LANGUAGE_SETTINGS_OFFER_TO_TRANSLATE_TITLE);
}
[acceptLanguages addObject:languageItem];
}
return acceptLanguages;
}
- (NSArray<LanguageItem*>*)supportedLanguagesItems {
// Get the accept languages.
std::vector<std::string> acceptLanguageCodes;
_translatePrefs->GetLanguageList(&acceptLanguageCodes);
// Get the supported languages.
std::vector<translate::TranslateLanguageInfo> languages;
translate::TranslatePrefs::GetLanguageInfoList(
GetApplicationContext()->GetApplicationLocale(),
_translatePrefs->IsTranslateAllowedByPolicy(), &languages);
NSMutableArray<LanguageItem*>* supportedLanguages =
[NSMutableArray arrayWithCapacity:languages.size()];
for (const auto& language : languages) {
// Ignore languages already in the accept languages list.
if (base::Contains(acceptLanguageCodes, language.code)) {
continue;
}
LanguageItem* languageItem = [self languageItemFromLanguage:language];
languageItem.accessibilityTraits |= UIAccessibilityTraitButton;
[supportedLanguages addObject:languageItem];
}
return supportedLanguages;
}
- (BOOL)translateEnabled {
return self.prefService->GetBoolean(translate::prefs::kOfferTranslateEnabled);
}
- (BOOL)translateManaged {
return self.prefService->IsManagedPreference(
translate::prefs::kOfferTranslateEnabled);
}
- (void)stopObservingModel {
_offerTranslatePrefObserverBridge.reset();
_acceptLanguagesPrefObserverBridge.reset();
_blockedLanguagesPrefObserverBridge.reset();
_prefChangeRegistrar.reset();
_translatePrefs.reset();
}
#pragma mark - LanguageSettingsCommands
- (void)setTranslateEnabled:(BOOL)enabled {
self.prefService->SetBoolean(translate::prefs::kOfferTranslateEnabled,
enabled);
UMA_HISTOGRAM_ENUMERATION(
kLanguageSettingsActionsHistogram,
enabled ? LanguageSettingsActions::ENABLE_TRANSLATE_GLOBALLY
: LanguageSettingsActions::DISABLE_TRANSLATE_GLOBALLY);
}
- (void)moveLanguage:(const std::string&)languageCode
downward:(BOOL)downward
withOffset:(NSUInteger)offset {
translate::TranslatePrefs::RearrangeSpecifier where =
downward ? translate::TranslatePrefs::kDown
: translate::TranslatePrefs::kUp;
std::vector<std::string> languageCodes;
_translatePrefs->GetLanguageList(&languageCodes);
_translatePrefs->RearrangeLanguage(languageCode, where, offset,
languageCodes);
UMA_HISTOGRAM_ENUMERATION(kLanguageSettingsActionsHistogram,
LanguageSettingsActions::LANGUAGE_LIST_REORDERED);
}
- (void)addLanguage:(const std::string&)languageCode {
_translatePrefs->AddToLanguageList(languageCode, /*force_blocked=*/false);
UMA_HISTOGRAM_ENUMERATION(kLanguageSettingsActionsHistogram,
LanguageSettingsActions::LANGUAGE_ADDED);
}
- (void)removeLanguage:(const std::string&)languageCode {
_translatePrefs->RemoveFromLanguageList(languageCode);
UMA_HISTOGRAM_ENUMERATION(kLanguageSettingsActionsHistogram,
LanguageSettingsActions::LANGUAGE_REMOVED);
}
- (void)blockLanguage:(const std::string&)languageCode {
_translatePrefs->BlockLanguage(languageCode);
UMA_HISTOGRAM_ENUMERATION(
kLanguageSettingsActionsHistogram,
LanguageSettingsActions::DISABLE_TRANSLATE_FOR_SINGLE_LANGUAGE);
}
- (void)unblockLanguage:(const std::string&)languageCode {
_translatePrefs->UnblockLanguage(languageCode);
UMA_HISTOGRAM_ENUMERATION(
kLanguageSettingsActionsHistogram,
LanguageSettingsActions::ENABLE_TRANSLATE_FOR_SINGLE_LANGUAGE);
}
#pragma mark - Private methods
- (LanguageItem*)languageItemFromLanguage:
(const translate::TranslateLanguageInfo&)language {
LanguageItem* languageItem = [[LanguageItem alloc] init];
languageItem.languageCode = language.code;
languageItem.text = base::SysUTF8ToNSString(language.display_name);
languageItem.leadingDetailText =
base::SysUTF8ToNSString(language.native_display_name);
languageItem.supportsTranslate = language.supports_translate;
return languageItem;
}
@end