1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/translate/translate_ui_delegate.h"
6
7#include "base/i18n/string_compare.h"
8#include "base/metrics/histogram.h"
9#include "chrome/browser/browser_process.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/translate/translate_manager.h"
12#include "chrome/browser/translate/translate_prefs.h"
13#include "chrome/browser/translate/translate_tab_helper.h"
14#include "components/translate/common/translate_constants.h"
15#include "content/public/browser/browser_context.h"
16#include "content/public/browser/navigation_entry.h"
17#include "content/public/browser/web_contents.h"
18#include "third_party/icu/source/i18n/unicode/coll.h"
19#include "ui/base/l10n/l10n_util.h"
20
21namespace {
22
23const char kDeclineTranslate[] = "Translate.DeclineTranslate";
24const char kRevertTranslation[] = "Translate.RevertTranslation";
25const char kPerformTranslate[] = "Translate.Translate";
26const char kNeverTranslateLang[] = "Translate.NeverTranslateLang";
27const char kNeverTranslateSite[] = "Translate.NeverTranslateSite";
28const char kAlwaysTranslateLang[] = "Translate.AlwaysTranslateLang";
29const char kModifyOriginalLang[] = "Translate.ModifyOriginalLang";
30const char kModifyTargetLang[] = "Translate.ModifyTargetLang";
31const char kDeclineTranslateDismissUI[] = "Translate.DeclineTranslateDismissUI";
32const char kShowErrorUI[] = "Translate.ShowErrorUI";
33
34}  // namespace
35
36TranslateUIDelegate::TranslateUIDelegate(content::WebContents* web_contents,
37                                         const std::string& original_language,
38                                         const std::string& target_language)
39    : web_contents_(web_contents),
40      original_language_index_(NO_INDEX),
41      initial_original_language_index_(NO_INDEX),
42      target_language_index_(NO_INDEX) {
43  DCHECK(web_contents_);
44
45  std::vector<std::string> language_codes;
46  TranslateManager::GetSupportedLanguages(&language_codes);
47
48  // Preparing for the alphabetical order in the locale.
49  UErrorCode error = U_ZERO_ERROR;
50  std::string locale = g_browser_process->GetApplicationLocale();
51  icu::Locale loc(locale.c_str());
52  scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(loc, error));
53  collator->setStrength(icu::Collator::PRIMARY);
54
55  languages_.reserve(language_codes.size());
56  for (std::vector<std::string>::const_iterator iter = language_codes.begin();
57       iter != language_codes.end(); ++iter) {
58    std::string language_code = *iter;
59
60    base::string16 language_name = l10n_util::GetDisplayNameForLocale(
61        language_code, g_browser_process->GetApplicationLocale(), true);
62    // Insert the language in languages_ in alphabetical order.
63    std::vector<LanguageNamePair>::iterator iter2;
64    for (iter2 = languages_.begin(); iter2 != languages_.end(); ++iter2) {
65      if (base::i18n::CompareString16WithCollator(collator.get(),
66          language_name, iter2->second) == UCOL_LESS) {
67        break;
68      }
69    }
70    languages_.insert(iter2, LanguageNamePair(language_code, language_name));
71  }
72  for (std::vector<LanguageNamePair>::const_iterator iter = languages_.begin();
73       iter != languages_.end(); ++iter) {
74    std::string language_code = iter->first;
75    if (language_code == original_language) {
76      original_language_index_ = iter - languages_.begin();
77      initial_original_language_index_ = original_language_index_;
78    }
79    if (language_code == target_language)
80      target_language_index_ = iter - languages_.begin();
81  }
82
83  Profile* profile =
84      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
85  prefs_.reset(new TranslatePrefs(profile->GetPrefs()));
86}
87
88TranslateUIDelegate::~TranslateUIDelegate() {
89}
90
91void TranslateUIDelegate::OnErrorShown(TranslateErrors::Type error_type) {
92  DCHECK_LE(TranslateErrors::NONE, error_type);
93  DCHECK_LT(error_type, TranslateErrors::TRANSLATE_ERROR_MAX);
94
95  if (error_type == TranslateErrors::NONE)
96    return;
97
98  UMA_HISTOGRAM_ENUMERATION(kShowErrorUI, error_type,
99                            TranslateErrors::TRANSLATE_ERROR_MAX);
100}
101
102size_t TranslateUIDelegate::GetNumberOfLanguages() const {
103  return languages_.size();
104}
105
106size_t TranslateUIDelegate::GetOriginalLanguageIndex() const {
107  return original_language_index_;
108}
109
110void TranslateUIDelegate::UpdateOriginalLanguageIndex(size_t language_index) {
111  if (original_language_index_ == language_index)
112    return;
113
114  UMA_HISTOGRAM_BOOLEAN(kModifyOriginalLang, true);
115  original_language_index_ = language_index;
116}
117
118size_t TranslateUIDelegate::GetTargetLanguageIndex() const {
119  return target_language_index_;
120}
121
122void TranslateUIDelegate::UpdateTargetLanguageIndex(size_t language_index) {
123  if (target_language_index_ == language_index)
124    return;
125
126  DCHECK_LT(language_index, GetNumberOfLanguages());
127  UMA_HISTOGRAM_BOOLEAN(kModifyTargetLang, true);
128  target_language_index_ = language_index;
129}
130
131
132std::string TranslateUIDelegate::GetLanguageCodeAt(size_t index) const {
133  DCHECK_LT(index, GetNumberOfLanguages());
134  return languages_[index].first;
135}
136
137base::string16 TranslateUIDelegate::GetLanguageNameAt(size_t index) const {
138  if (index == static_cast<size_t>(NO_INDEX))
139    return base::string16();
140  DCHECK_LT(index, GetNumberOfLanguages());
141  return languages_[index].second;
142}
143
144std::string TranslateUIDelegate::GetOriginalLanguageCode() const {
145  return (GetOriginalLanguageIndex() == static_cast<size_t>(NO_INDEX)) ?
146      translate::kUnknownLanguageCode :
147      GetLanguageCodeAt(GetOriginalLanguageIndex());
148}
149
150std::string TranslateUIDelegate::GetTargetLanguageCode() const {
151  return GetLanguageCodeAt(GetTargetLanguageIndex());
152}
153
154void TranslateUIDelegate::Translate() {
155  if (!web_contents()->GetBrowserContext()->IsOffTheRecord()) {
156    prefs_->ResetTranslationDeniedCount(GetOriginalLanguageCode());
157    prefs_->IncrementTranslationAcceptedCount(GetOriginalLanguageCode());
158  }
159  TranslateManager::GetInstance()->TranslatePage(web_contents(),
160                                                 GetOriginalLanguageCode(),
161                                                 GetTargetLanguageCode());
162
163  UMA_HISTOGRAM_BOOLEAN(kPerformTranslate, true);
164}
165
166void TranslateUIDelegate::RevertTranslation() {
167  TranslateManager::GetInstance()->RevertTranslation(web_contents());
168
169  UMA_HISTOGRAM_BOOLEAN(kRevertTranslation, true);
170}
171
172void TranslateUIDelegate::TranslationDeclined(bool explicitly_closed) {
173  if (!web_contents()->GetBrowserContext()->IsOffTheRecord()) {
174    prefs_->ResetTranslationAcceptedCount(GetOriginalLanguageCode());
175    prefs_->IncrementTranslationDeniedCount(GetOriginalLanguageCode());
176  }
177
178  // Remember that the user declined the translation so as to prevent showing a
179  // translate infobar for that page again.  (TranslateManager initiates
180  // translations when getting a LANGUAGE_DETERMINED from the page, which
181  // happens when a load stops. That could happen multiple times, including
182  // after the user already declined the translation.)
183  TranslateTabHelper::FromWebContents(web_contents())->
184      language_state().set_translation_declined(true);
185
186  UMA_HISTOGRAM_BOOLEAN(kDeclineTranslate, true);
187
188  if (!explicitly_closed)
189    UMA_HISTOGRAM_BOOLEAN(kDeclineTranslateDismissUI, true);
190}
191
192bool TranslateUIDelegate::IsLanguageBlocked() {
193  return prefs_->IsBlockedLanguage(GetOriginalLanguageCode());
194}
195
196void TranslateUIDelegate::SetLanguageBlocked(bool value) {
197  if (value) {
198    prefs_->BlockLanguage(GetOriginalLanguageCode());
199    TranslateTabHelper* translate_tab_helper =
200        TranslateTabHelper::FromWebContents(web_contents());
201    DCHECK(translate_tab_helper);
202    translate_tab_helper->language_state().SetTranslateEnabled(false);
203  } else {
204    prefs_->UnblockLanguage(GetOriginalLanguageCode());
205  }
206
207  UMA_HISTOGRAM_BOOLEAN(kNeverTranslateLang, true);
208}
209
210bool TranslateUIDelegate::IsSiteBlacklisted() {
211  std::string host = GetPageHost();
212  return !host.empty() && prefs_->IsSiteBlacklisted(host);
213}
214
215void TranslateUIDelegate::SetSiteBlacklist(bool value) {
216  std::string host = GetPageHost();
217  if (host.empty())
218    return;
219
220  if (value) {
221    prefs_->BlacklistSite(host);
222    TranslateTabHelper* translate_tab_helper =
223        TranslateTabHelper::FromWebContents(web_contents());
224    DCHECK(translate_tab_helper);
225    translate_tab_helper->language_state().SetTranslateEnabled(false);
226  } else {
227    prefs_->RemoveSiteFromBlacklist(host);
228  }
229
230  UMA_HISTOGRAM_BOOLEAN(kNeverTranslateSite, true);
231}
232
233bool TranslateUIDelegate::ShouldAlwaysTranslate() {
234  return prefs_->IsLanguagePairWhitelisted(GetOriginalLanguageCode(),
235                                           GetTargetLanguageCode());
236}
237
238void TranslateUIDelegate::SetAlwaysTranslate(bool value) {
239  const std::string& original_lang = GetOriginalLanguageCode();
240  const std::string& target_lang = GetTargetLanguageCode();
241  if (value)
242    prefs_->WhitelistLanguagePair(original_lang, target_lang);
243  else
244    prefs_->RemoveLanguagePairFromWhitelist(original_lang, target_lang);
245
246  UMA_HISTOGRAM_BOOLEAN(kAlwaysTranslateLang, true);
247}
248
249std::string TranslateUIDelegate::GetPageHost() {
250  content::NavigationEntry* entry =
251      web_contents()->GetController().GetActiveEntry();
252  return entry ? entry->GetURL().HostNoBrackets() : std::string();
253}
254