Commit 4d93574e authored by David Tseng's avatar David Tseng Committed by Commit Bot

Change chrome.accessibilityPrivate.getDisplayLanguage to getDisplayNameForLocale

A locale as used by ICU and by ISO standards refers to the [language]-[region/country]
specifier. This change makes it so the chrome.accessibilityPrivate binding formerly called getDisplayLanguage returns instead the full display name for locale.

This means that we can disambiguate between languages from specific regions e.g. English from the United Kingdom (en-GB) vs English from The United States (en-US).

Possible clients of the api are ChromeVox and tts. This is mostly a stopgap measure until a more standards based solution comes.

Change-Id: I1d0eda2edb28dbb5ee23e035d58ee5e4e46b617a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1955100
Commit-Queue: David Tseng <dtseng@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Reviewed-by: default avatarJungshik Shin <jshin@chromium.org>
Reviewed-by: default avatarAkihiro Ota <akihiroota@chromium.org>
Cr-Commit-Position: refs/heads/master@{#727181}
parent 331c1b5a
......@@ -23,9 +23,10 @@ IN_PROC_BROWSER_TEST_F(AccessibilityPrivateApiTest, SendSyntheticKeyEvent) {
<< message_;
}
IN_PROC_BROWSER_TEST_F(AccessibilityPrivateApiTest, GetDisplayLanguageTest) {
IN_PROC_BROWSER_TEST_F(AccessibilityPrivateApiTest,
GetDisplayNameForLocaleTest) {
ASSERT_TRUE(
RunExtensionSubtest("accessibility_private/", "display_language.html"))
RunExtensionSubtest("accessibility_private/", "display_locale.html"))
<< message_;
}
......
......@@ -142,7 +142,7 @@ LanguageSwitching.assignLanguagesForStringAttribute = function(
if (LanguageSwitching.didLanguageSwitch(newLanguage)) {
LanguageSwitching.currentLanguage_ = newLanguage;
// Get human-readable language in |newLanguage|.
displayLanguage = chrome.accessibilityPrivate.getDisplayLanguage(
displayLanguage = chrome.accessibilityPrivate.getDisplayNameForLocale(
newLanguage /* Language code to translate */,
newLanguage /* Target language code */);
// Prepend the human-readable language to outputString.
......@@ -154,7 +154,7 @@ LanguageSwitching.assignLanguagesForStringAttribute = function(
appendStringWithLanguage(newLanguage, outputString);
} else {
// Translate |newLanguage| into human-readable string in the UI language.
displayLanguage = chrome.accessibilityPrivate.getDisplayLanguage(
displayLanguage = chrome.accessibilityPrivate.getDisplayNameForLocale(
newLanguage /* Language code to translate */,
LanguageSwitching.browserUILanguage_ /* Target language code */);
outputString =
......@@ -270,10 +270,10 @@ LanguageSwitching.isValidLanguageCode = function(languageCode) {
return false;
}
// Use the accessibilityPrivate.getDisplayLanguage() API to validate language
// code. If the language code is invalid, then this API returns an empty
// string.
if (chrome.accessibilityPrivate.getDisplayLanguage(
// Use the accessibilityPrivate.getDisplayNameForLocale() API to validate
// language code. If the language code is invalid, then this API returns an
// empty string.
if (chrome.accessibilityPrivate.getDisplayNameForLocale(
languageCode, languageCode) === '') {
return false;
}
......
......@@ -388,7 +388,8 @@ TEST_F(
// with high enough probability. We fall back on node-level
// detected language, which is 'en-us'.
.expectSpeechWithLanguage(
'en-us', 'English: It\'s a pleasure to meet you. ')
'en-us',
'English (United States): It\'s a pleasure to meet you. ')
.expectSpeechWithLanguage(
'ja', '日本語: どうぞよろしくお願いします.');
mockFeedback.replay();
......@@ -705,7 +706,7 @@ TEST_F('ChromeVoxLanguageSwitchingTest', 'DefaultToUILanguageTest', function() {
.expectSpeechWithLanguage(
'ja', '日本語: どうぞよろしくお願いします')
.call(doCmd('nextObject'))
.expectSpeechWithLanguage('en-us', 'English: Test')
.expectSpeechWithLanguage('en-us', 'English (United States): Test')
.call(doCmd('nextObject'))
.expectSpeechWithLanguage('en-us', 'Yikes');
mockFeedback.replay();
......
......@@ -148,23 +148,23 @@
],
"functions": [
{
"name": "getDisplayLanguage",
"name": "getDisplayNameForLocale",
"type": "function",
"nocompile": true,
"description": "Called to translate languageCodeToTranslate into human-readable string in the language specified by targetLanguageCode",
"description": "Called to translate localeCodeToTranslate into human-readable string in the locale specified by displayLocaleCode",
"parameters": [
{
"name": "languageCodeToTranslate",
"name": "localeCodeToTranslate",
"type": "string"
},
{
"name": "targetLanguageCode",
"name": "displayLocaleCode",
"type": "string"
}
],
"returns": {
"type": "string",
"description": "The human-readable language string in the provided language."
"description": "The human-readable locale string in the provided locale."
},
"platforms": ["chromeos"]
},
......
file://extensions/OWNERS
per-file automation_ax_tree_wrapper*=file://ui/accessibility/OWNERS
per-file automation_internal_custom_bindings*=file://ui/accessibility/OWNERS
# COMPONENT: Platform>Extensions
# TEAM: extensions-reviews@chromium.org
......@@ -11,14 +11,14 @@
#include "extensions/renderer/get_script_context.h"
#include "extensions/renderer/script_context.h"
#include "gin/converter.h"
#include "third_party/icu/source/common/unicode/locid.h"
#include "ui/base/l10n/l10n_util.h"
namespace extensions {
namespace {
constexpr char kGetDisplayLanguage[] =
"accessibilityPrivate.getDisplayLanguage";
constexpr char kUndeterminedLanguage[] = "und";
constexpr char kGetDisplayNameForLocale[] =
"accessibilityPrivate.getDisplayNameForLocale";
constexpr char kUndeterminedLocale[] = "und";
} // namespace
using RequestResult = APIBindingHooks::RequestResult;
......@@ -35,8 +35,8 @@ RequestResult AccessibilityPrivateHooksDelegate::HandleRequest(
std::vector<v8::Local<v8::Value>>* arguments,
const APITypeReferenceMap& refs) {
// Error checks.
// Ensure we would like to call the GetDisplayLanguage function.
if (method_name != kGetDisplayLanguage)
// Ensure we would like to call the GetDisplayNameForLocale function.
if (method_name != kGetDisplayNameForLocale)
return RequestResult(RequestResult::NOT_HANDLED);
// Ensure arguments are successfully parsed and converted.
APISignature::V8ParseResult parse_result =
......@@ -46,17 +46,11 @@ RequestResult AccessibilityPrivateHooksDelegate::HandleRequest(
result.error = std::move(*parse_result.error);
return result;
}
return HandleGetDisplayLanguage(GetScriptContextFromV8ContextChecked(context),
*parse_result.arguments);
return HandleGetDisplayNameForLocale(
GetScriptContextFromV8ContextChecked(context), *parse_result.arguments);
}
// Called to translate |language_code_to_translate| into human-readable string
// in the language specified by |target_language_code|. For example, if
// language_code_to_translate = 'en' and target_language_code = 'fr', then this
// function returns 'anglais'.
// If language_code_to_translate = 'fr' and target_language_code = 'en', then
// this function returns 'french'.
RequestResult AccessibilityPrivateHooksDelegate::HandleGetDisplayLanguage(
RequestResult AccessibilityPrivateHooksDelegate::HandleGetDisplayNameForLocale(
ScriptContext* script_context,
const std::vector<v8::Local<v8::Value>>& parsed_arguments) {
DCHECK(script_context->extension());
......@@ -64,57 +58,28 @@ RequestResult AccessibilityPrivateHooksDelegate::HandleGetDisplayLanguage(
DCHECK(parsed_arguments[0]->IsString());
DCHECK(parsed_arguments[1]->IsString());
std::string language_code_to_translate =
const std::string locale =
gin::V8ToString(script_context->isolate(), parsed_arguments[0]);
std::string target_language_code =
const std::string display_locale =
gin::V8ToString(script_context->isolate(), parsed_arguments[1]);
// The locale whose language code we want to translate.
icu::Locale locale_to_translate =
icu::Locale(language_code_to_translate.c_str());
// The locale that |display_language| should be in.
icu::Locale target_locale = icu::Locale(target_language_code.c_str());
// Validate locales.
// Get list of available locales. Please see the ICU User Guide for more
// details: http://userguide.icu-project.org/locale.
bool valid_arg1 = false;
bool valid_arg2 = false;
int32_t num_locales = 0;
const icu::Locale* available_locales =
icu::Locale::getAvailableLocales(num_locales);
for (int32_t i = 0; i < num_locales; ++i) {
// Check both the language and country for each locale.
const char* current_language = available_locales[i].getLanguage();
const char* current_country = available_locales[i].getCountry();
if (strcmp(locale_to_translate.getLanguage(), current_language) == 0 &&
strcmp(locale_to_translate.getCountry(), current_country) == 0) {
valid_arg1 = true;
}
if (strcmp(target_locale.getLanguage(), current_language) == 0 &&
strcmp(target_locale.getCountry(), current_country) == 0) {
valid_arg2 = true;
}
}
std::string locale_result =
base::UTF16ToUTF8(l10n_util::GetDisplayNameForLocale(
locale, display_locale, true /* is_ui */,
true /* disallow_default */));
// If either of the language codes is invalid, we should return empty string.
if (!(valid_arg1 && valid_arg2)) {
RequestResult empty_result(RequestResult::HANDLED);
empty_result.return_value = gin::StringToV8(script_context->isolate(), "");
return empty_result;
}
RequestResult result(RequestResult::HANDLED);
icu::UnicodeString display_language;
locale_to_translate.getDisplayLanguage(target_locale, display_language);
std::string language_result =
base::UTF16ToUTF8(base::i18n::UnicodeStringToString16(display_language));
// Instead of returning "und", which is what the ICU Locale class returns for
// undetermined languages, we would simply like to return an empty string
// to communicate that we could not determine the display language.
if (language_result == kUndeterminedLanguage)
language_result = "";
RequestResult result(RequestResult::HANDLED);
// undetermined locales, we would simply like to return an empty string to
// communicate that we could not determine the display locale. In addition,
// ICU returns the |locale| as |result_locale| if it has no translation for a
// valid locale. Also return an empty string for that case.
if (locale_result == kUndeterminedLocale || locale_result == locale)
locale_result = std::string();
result.return_value =
gin::StringToV8(script_context->isolate(), language_result);
gin::StringToV8(script_context->isolate(), locale_result);
return result;
}
......
......@@ -32,7 +32,7 @@ class AccessibilityPrivateHooksDelegate : public APIBindingHooksDelegate {
private:
// Method handlers:
APIBindingHooks::RequestResult HandleGetDisplayLanguage(
APIBindingHooks::RequestResult HandleGetDisplayNameForLocale(
ScriptContext* script_context,
const std::vector<v8::Local<v8::Value>>& parsed_arguments);
DISALLOW_COPY_AND_ASSIGN(AccessibilityPrivateHooksDelegate);
......
......@@ -18,7 +18,7 @@ namespace extensions {
using AccessibilityPrivateHooksDelegateTest =
NativeExtensionBindingsSystemUnittest;
TEST_F(AccessibilityPrivateHooksDelegateTest, TestGetDisplayLanguage) {
TEST_F(AccessibilityPrivateHooksDelegateTest, TestGetDisplayNameForLocale) {
// Initialize bindings system.
bindings_system()
->api_system()
......@@ -50,30 +50,35 @@ TEST_F(AccessibilityPrivateHooksDelegateTest, TestGetDisplayLanguage) {
ASSERT_TRUE(availability.is_available()) << availability.message();
// Setup function that will run extension api.
auto run_get_display_language = [context](const char* args) {
auto run_get_display_locale = [context](const char* args) {
SCOPED_TRACE(args);
constexpr char kRunGetDisplayLanguageFunction[] =
constexpr char kRunGetDisplayLocale[] =
R"((function() {
return chrome.accessibilityPrivate.getDisplayLanguage(%s);
return chrome.accessibilityPrivate.getDisplayNameForLocale(%s);
}))";
v8::Local<v8::Function> function = FunctionFromString(
context, base::StringPrintf(kRunGetDisplayLanguageFunction, args));
context, base::StringPrintf(kRunGetDisplayLocale, args));
v8::Local<v8::Value> result = RunFunction(function, context, 0, nullptr);
return V8ToString(result, context);
};
// Test behavior.
EXPECT_EQ(R"("")", run_get_display_language("'',''"));
EXPECT_EQ(R"("")", run_get_display_language("'not a language code','ja-JP'"));
EXPECT_EQ(R"("")",
run_get_display_language("'zh-TW', 'not a language code'"));
EXPECT_EQ(R"("English")", run_get_display_language("'en','en'"));
EXPECT_EQ(R"("English")", run_get_display_language("'en-US','en'"));
EXPECT_EQ(R"("français")", run_get_display_language("'fr','fr'"));
EXPECT_EQ(R"("español")", run_get_display_language("'es','es'"));
EXPECT_EQ(R"("日本語")", run_get_display_language("'ja','ja'"));
EXPECT_EQ(R"("anglais")", run_get_display_language("'en','fr'"));
EXPECT_EQ(R"("Japanese")", run_get_display_language("'ja','en'"));
EXPECT_EQ(R"("")", run_get_display_locale("'', ''"));
EXPECT_EQ(R"("")", run_get_display_locale("'not a locale code', 'ja-JP'"));
EXPECT_EQ(R"|("Chinese (Traditional)")|",
run_get_display_locale("'zh-TW', 'en'"));
EXPECT_EQ(R"("")", run_get_display_locale("'zh-TW', 'not a locale code'"));
EXPECT_EQ(R"("English")", run_get_display_locale("'en', 'en'"));
EXPECT_EQ(R"|("English (United States)")|",
run_get_display_locale("'en-US', 'en'"));
EXPECT_EQ(R"("français")", run_get_display_locale("'fr', 'fr-FR'"));
EXPECT_EQ(R"|("français (France)")|",
run_get_display_locale("'fr-FR', 'fr-FR'"));
EXPECT_EQ(R"|("francés (Francia)")|",
run_get_display_locale("'fr-FR', 'es'"));
EXPECT_EQ(R"("日本語")", run_get_display_locale("'ja', 'ja'"));
EXPECT_EQ(R"("anglais")", run_get_display_locale("'en', 'fr-CA'"));
EXPECT_EQ(R"("Japanese")", run_get_display_locale("'ja', 'en'"));
}
} // namespace extensions
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var allTests = [function getDisplayLanguage() {
chrome.test.assertEq(
'', chrome.accessibilityPrivate.getDisplayLanguage('', ''));
chrome.test.assertEq(
'',
chrome.accessibilityPrivate.getDisplayLanguage(
'not a language code', 'ja-JP'));
chrome.test.assertEq(
'',
chrome.accessibilityPrivate.getDisplayLanguage(
'zh-TW', 'not a language code'));
chrome.test.assertEq(
'English', chrome.accessibilityPrivate.getDisplayLanguage('en', 'en'));
chrome.test.assertEq(
'English', chrome.accessibilityPrivate.getDisplayLanguage('en-US', 'en'));
chrome.test.assertEq(
'français',
chrome.accessibilityPrivate.getDisplayLanguage('fr', 'fr-FR'));
chrome.test.assertEq(
'日本語', chrome.accessibilityPrivate.getDisplayLanguage('ja', 'ja'));
chrome.test.assertEq(
'anglais', chrome.accessibilityPrivate.getDisplayLanguage('en', 'fr-CA'));
chrome.test.assertEq(
'Japanese', chrome.accessibilityPrivate.getDisplayLanguage('ja', 'en'));
chrome.test.succeed();
}];
chrome.test.runTests(allTests);
......@@ -6,4 +6,4 @@
<head>
<meta charset="utf-8">
</head>
<script src="display_language.js"></script>
<script src="display_locale.js"></script>
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
var allTests = [function testGetDisplayNameForLocale() {
// The implementation of getDisplayNameForLocale() is more heavily
// unittested elsewhere; here, we just need a sanity check to make sure
// everything is correctly wired up.
chrome.test.assertEq(
'English',
chrome.accessibilityPrivate.getDisplayNameForLocale('en', 'en'));
chrome.test.succeed();
}];
chrome.test.runTests(allTests);
......@@ -148,13 +148,13 @@ chrome.accessibilityPrivate.FocusType = {
chrome.accessibilityPrivate.FocusRingInfo;
/**
* Called to translate languageCodeToTranslate into human-readable string in the
* language specified by targetLanguageCode
* @param {string} languageCodeToTranslate
* @param {string} targetLanguageCode
* @return {string} The human-readable language string in the provided language.
* Called to translate localeCodeToTranslate into human-readable string in the
* locale specified by displayLocaleCode
* @param {string} localeCodeToTranslate
* @param {string} displayLocaleCode
* @return {string} The human-readable locale string in the provided locale.
*/
chrome.accessibilityPrivate.getDisplayLanguage = function(languageCodeToTranslate, targetLanguageCode) {};
chrome.accessibilityPrivate.getDisplayNameForLocale = function(localeCodeToTranslate, displayLocaleCode) {};
/**
* Called to request battery status from Chrome OS system.
......
......@@ -547,7 +547,8 @@ bool IsLocaleNameTranslated(const char* locale,
base::string16 GetDisplayNameForLocale(const std::string& locale,
const std::string& display_locale,
bool is_for_ui) {
bool is_for_ui,
bool disallow_default) {
std::string locale_code = locale;
// Internally, we use the language code of zh-CN and zh-TW, but we want the
// display names to be Chinese (Simplified) and Chinese (Traditional) instead
......@@ -594,6 +595,8 @@ base::string16 GetDisplayNameForLocale(const std::string& locale,
locale_code.c_str(), display_locale.c_str(),
base::WriteInto(&display_name, kBufferSize), kBufferSize - 1, &error);
}
if (disallow_default && U_USING_DEFAULT_WARNING == error)
return base::string16();
DCHECK(U_SUCCESS(error));
display_name.resize(actual_size);
}
......
......@@ -71,7 +71,8 @@ bool IsLocaleSupportedByOS(const std::string& locale);
UI_BASE_EXPORT base::string16 GetDisplayNameForLocale(
const std::string& locale,
const std::string& display_locale,
bool is_for_ui);
bool is_for_ui,
bool disallow_default = false);
// Returns the display name of the |country_code| in |display_locale|.
UI_BASE_EXPORT base::string16 GetDisplayNameForCountry(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment