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) { ...@@ -23,9 +23,10 @@ IN_PROC_BROWSER_TEST_F(AccessibilityPrivateApiTest, SendSyntheticKeyEvent) {
<< message_; << message_;
} }
IN_PROC_BROWSER_TEST_F(AccessibilityPrivateApiTest, GetDisplayLanguageTest) { IN_PROC_BROWSER_TEST_F(AccessibilityPrivateApiTest,
GetDisplayNameForLocaleTest) {
ASSERT_TRUE( ASSERT_TRUE(
RunExtensionSubtest("accessibility_private/", "display_language.html")) RunExtensionSubtest("accessibility_private/", "display_locale.html"))
<< message_; << message_;
} }
......
...@@ -142,7 +142,7 @@ LanguageSwitching.assignLanguagesForStringAttribute = function( ...@@ -142,7 +142,7 @@ LanguageSwitching.assignLanguagesForStringAttribute = function(
if (LanguageSwitching.didLanguageSwitch(newLanguage)) { if (LanguageSwitching.didLanguageSwitch(newLanguage)) {
LanguageSwitching.currentLanguage_ = newLanguage; LanguageSwitching.currentLanguage_ = newLanguage;
// Get human-readable language in |newLanguage|. // Get human-readable language in |newLanguage|.
displayLanguage = chrome.accessibilityPrivate.getDisplayLanguage( displayLanguage = chrome.accessibilityPrivate.getDisplayNameForLocale(
newLanguage /* Language code to translate */, newLanguage /* Language code to translate */,
newLanguage /* Target language code */); newLanguage /* Target language code */);
// Prepend the human-readable language to outputString. // Prepend the human-readable language to outputString.
...@@ -154,7 +154,7 @@ LanguageSwitching.assignLanguagesForStringAttribute = function( ...@@ -154,7 +154,7 @@ LanguageSwitching.assignLanguagesForStringAttribute = function(
appendStringWithLanguage(newLanguage, outputString); appendStringWithLanguage(newLanguage, outputString);
} else { } else {
// Translate |newLanguage| into human-readable string in the UI language. // Translate |newLanguage| into human-readable string in the UI language.
displayLanguage = chrome.accessibilityPrivate.getDisplayLanguage( displayLanguage = chrome.accessibilityPrivate.getDisplayNameForLocale(
newLanguage /* Language code to translate */, newLanguage /* Language code to translate */,
LanguageSwitching.browserUILanguage_ /* Target language code */); LanguageSwitching.browserUILanguage_ /* Target language code */);
outputString = outputString =
...@@ -270,10 +270,10 @@ LanguageSwitching.isValidLanguageCode = function(languageCode) { ...@@ -270,10 +270,10 @@ LanguageSwitching.isValidLanguageCode = function(languageCode) {
return false; return false;
} }
// Use the accessibilityPrivate.getDisplayLanguage() API to validate language // Use the accessibilityPrivate.getDisplayNameForLocale() API to validate
// code. If the language code is invalid, then this API returns an empty // language code. If the language code is invalid, then this API returns an
// string. // empty string.
if (chrome.accessibilityPrivate.getDisplayLanguage( if (chrome.accessibilityPrivate.getDisplayNameForLocale(
languageCode, languageCode) === '') { languageCode, languageCode) === '') {
return false; return false;
} }
......
...@@ -388,7 +388,8 @@ TEST_F( ...@@ -388,7 +388,8 @@ TEST_F(
// with high enough probability. We fall back on node-level // with high enough probability. We fall back on node-level
// detected language, which is 'en-us'. // detected language, which is 'en-us'.
.expectSpeechWithLanguage( .expectSpeechWithLanguage(
'en-us', 'English: It\'s a pleasure to meet you. ') 'en-us',
'English (United States): It\'s a pleasure to meet you. ')
.expectSpeechWithLanguage( .expectSpeechWithLanguage(
'ja', '日本語: どうぞよろしくお願いします.'); 'ja', '日本語: どうぞよろしくお願いします.');
mockFeedback.replay(); mockFeedback.replay();
...@@ -705,7 +706,7 @@ TEST_F('ChromeVoxLanguageSwitchingTest', 'DefaultToUILanguageTest', function() { ...@@ -705,7 +706,7 @@ TEST_F('ChromeVoxLanguageSwitchingTest', 'DefaultToUILanguageTest', function() {
.expectSpeechWithLanguage( .expectSpeechWithLanguage(
'ja', '日本語: どうぞよろしくお願いします') 'ja', '日本語: どうぞよろしくお願いします')
.call(doCmd('nextObject')) .call(doCmd('nextObject'))
.expectSpeechWithLanguage('en-us', 'English: Test') .expectSpeechWithLanguage('en-us', 'English (United States): Test')
.call(doCmd('nextObject')) .call(doCmd('nextObject'))
.expectSpeechWithLanguage('en-us', 'Yikes'); .expectSpeechWithLanguage('en-us', 'Yikes');
mockFeedback.replay(); mockFeedback.replay();
......
...@@ -148,23 +148,23 @@ ...@@ -148,23 +148,23 @@
], ],
"functions": [ "functions": [
{ {
"name": "getDisplayLanguage", "name": "getDisplayNameForLocale",
"type": "function", "type": "function",
"nocompile": true, "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": [ "parameters": [
{ {
"name": "languageCodeToTranslate", "name": "localeCodeToTranslate",
"type": "string" "type": "string"
}, },
{ {
"name": "targetLanguageCode", "name": "displayLocaleCode",
"type": "string" "type": "string"
} }
], ],
"returns": { "returns": {
"type": "string", "type": "string",
"description": "The human-readable language string in the provided language." "description": "The human-readable locale string in the provided locale."
}, },
"platforms": ["chromeos"] "platforms": ["chromeos"]
}, },
......
file://extensions/OWNERS 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 # COMPONENT: Platform>Extensions
# TEAM: extensions-reviews@chromium.org # TEAM: extensions-reviews@chromium.org
...@@ -11,14 +11,14 @@ ...@@ -11,14 +11,14 @@
#include "extensions/renderer/get_script_context.h" #include "extensions/renderer/get_script_context.h"
#include "extensions/renderer/script_context.h" #include "extensions/renderer/script_context.h"
#include "gin/converter.h" #include "gin/converter.h"
#include "third_party/icu/source/common/unicode/locid.h" #include "ui/base/l10n/l10n_util.h"
namespace extensions { namespace extensions {
namespace { namespace {
constexpr char kGetDisplayLanguage[] = constexpr char kGetDisplayNameForLocale[] =
"accessibilityPrivate.getDisplayLanguage"; "accessibilityPrivate.getDisplayNameForLocale";
constexpr char kUndeterminedLanguage[] = "und"; constexpr char kUndeterminedLocale[] = "und";
} // namespace } // namespace
using RequestResult = APIBindingHooks::RequestResult; using RequestResult = APIBindingHooks::RequestResult;
...@@ -35,8 +35,8 @@ RequestResult AccessibilityPrivateHooksDelegate::HandleRequest( ...@@ -35,8 +35,8 @@ RequestResult AccessibilityPrivateHooksDelegate::HandleRequest(
std::vector<v8::Local<v8::Value>>* arguments, std::vector<v8::Local<v8::Value>>* arguments,
const APITypeReferenceMap& refs) { const APITypeReferenceMap& refs) {
// Error checks. // Error checks.
// Ensure we would like to call the GetDisplayLanguage function. // Ensure we would like to call the GetDisplayNameForLocale function.
if (method_name != kGetDisplayLanguage) if (method_name != kGetDisplayNameForLocale)
return RequestResult(RequestResult::NOT_HANDLED); return RequestResult(RequestResult::NOT_HANDLED);
// Ensure arguments are successfully parsed and converted. // Ensure arguments are successfully parsed and converted.
APISignature::V8ParseResult parse_result = APISignature::V8ParseResult parse_result =
...@@ -46,17 +46,11 @@ RequestResult AccessibilityPrivateHooksDelegate::HandleRequest( ...@@ -46,17 +46,11 @@ RequestResult AccessibilityPrivateHooksDelegate::HandleRequest(
result.error = std::move(*parse_result.error); result.error = std::move(*parse_result.error);
return result; return result;
} }
return HandleGetDisplayLanguage(GetScriptContextFromV8ContextChecked(context), return HandleGetDisplayNameForLocale(
*parse_result.arguments); GetScriptContextFromV8ContextChecked(context), *parse_result.arguments);
} }
// Called to translate |language_code_to_translate| into human-readable string RequestResult AccessibilityPrivateHooksDelegate::HandleGetDisplayNameForLocale(
// 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(
ScriptContext* script_context, ScriptContext* script_context,
const std::vector<v8::Local<v8::Value>>& parsed_arguments) { const std::vector<v8::Local<v8::Value>>& parsed_arguments) {
DCHECK(script_context->extension()); DCHECK(script_context->extension());
...@@ -64,57 +58,28 @@ RequestResult AccessibilityPrivateHooksDelegate::HandleGetDisplayLanguage( ...@@ -64,57 +58,28 @@ RequestResult AccessibilityPrivateHooksDelegate::HandleGetDisplayLanguage(
DCHECK(parsed_arguments[0]->IsString()); DCHECK(parsed_arguments[0]->IsString());
DCHECK(parsed_arguments[1]->IsString()); DCHECK(parsed_arguments[1]->IsString());
std::string language_code_to_translate = const std::string locale =
gin::V8ToString(script_context->isolate(), parsed_arguments[0]); 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]); 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. std::string locale_result =
// Get list of available locales. Please see the ICU User Guide for more base::UTF16ToUTF8(l10n_util::GetDisplayNameForLocale(
// details: http://userguide.icu-project.org/locale. locale, display_locale, true /* is_ui */,
bool valid_arg1 = false; true /* disallow_default */));
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;
}
}
// If either of the language codes is invalid, we should return empty string. RequestResult result(RequestResult::HANDLED);
if (!(valid_arg1 && valid_arg2)) {
RequestResult empty_result(RequestResult::HANDLED);
empty_result.return_value = gin::StringToV8(script_context->isolate(), "");
return empty_result;
}
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 // Instead of returning "und", which is what the ICU Locale class returns for
// undetermined languages, we would simply like to return an empty string // undetermined locales, we would simply like to return an empty string to
// to communicate that we could not determine the display language. // communicate that we could not determine the display locale. In addition,
if (language_result == kUndeterminedLanguage) // ICU returns the |locale| as |result_locale| if it has no translation for a
language_result = ""; // valid locale. Also return an empty string for that case.
RequestResult result(RequestResult::HANDLED); if (locale_result == kUndeterminedLocale || locale_result == locale)
locale_result = std::string();
result.return_value = result.return_value =
gin::StringToV8(script_context->isolate(), language_result); gin::StringToV8(script_context->isolate(), locale_result);
return result; return result;
} }
......
...@@ -32,7 +32,7 @@ class AccessibilityPrivateHooksDelegate : public APIBindingHooksDelegate { ...@@ -32,7 +32,7 @@ class AccessibilityPrivateHooksDelegate : public APIBindingHooksDelegate {
private: private:
// Method handlers: // Method handlers:
APIBindingHooks::RequestResult HandleGetDisplayLanguage( APIBindingHooks::RequestResult HandleGetDisplayNameForLocale(
ScriptContext* script_context, ScriptContext* script_context,
const std::vector<v8::Local<v8::Value>>& parsed_arguments); const std::vector<v8::Local<v8::Value>>& parsed_arguments);
DISALLOW_COPY_AND_ASSIGN(AccessibilityPrivateHooksDelegate); DISALLOW_COPY_AND_ASSIGN(AccessibilityPrivateHooksDelegate);
......
...@@ -18,7 +18,7 @@ namespace extensions { ...@@ -18,7 +18,7 @@ namespace extensions {
using AccessibilityPrivateHooksDelegateTest = using AccessibilityPrivateHooksDelegateTest =
NativeExtensionBindingsSystemUnittest; NativeExtensionBindingsSystemUnittest;
TEST_F(AccessibilityPrivateHooksDelegateTest, TestGetDisplayLanguage) { TEST_F(AccessibilityPrivateHooksDelegateTest, TestGetDisplayNameForLocale) {
// Initialize bindings system. // Initialize bindings system.
bindings_system() bindings_system()
->api_system() ->api_system()
...@@ -50,30 +50,35 @@ TEST_F(AccessibilityPrivateHooksDelegateTest, TestGetDisplayLanguage) { ...@@ -50,30 +50,35 @@ TEST_F(AccessibilityPrivateHooksDelegateTest, TestGetDisplayLanguage) {
ASSERT_TRUE(availability.is_available()) << availability.message(); ASSERT_TRUE(availability.is_available()) << availability.message();
// Setup function that will run extension api. // 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); SCOPED_TRACE(args);
constexpr char kRunGetDisplayLanguageFunction[] = constexpr char kRunGetDisplayLocale[] =
R"((function() { R"((function() {
return chrome.accessibilityPrivate.getDisplayLanguage(%s); return chrome.accessibilityPrivate.getDisplayNameForLocale(%s);
}))"; }))";
v8::Local<v8::Function> function = FunctionFromString( 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); v8::Local<v8::Value> result = RunFunction(function, context, 0, nullptr);
return V8ToString(result, context); return V8ToString(result, context);
}; };
// Test behavior. // Test behavior.
EXPECT_EQ(R"("")", run_get_display_language("'',''")); EXPECT_EQ(R"("")", run_get_display_locale("'', ''"));
EXPECT_EQ(R"("")", run_get_display_language("'not a language code','ja-JP'")); EXPECT_EQ(R"("")", run_get_display_locale("'not a locale code', 'ja-JP'"));
EXPECT_EQ(R"("")", EXPECT_EQ(R"|("Chinese (Traditional)")|",
run_get_display_language("'zh-TW', 'not a language code'")); run_get_display_locale("'zh-TW', 'en'"));
EXPECT_EQ(R"("English")", run_get_display_language("'en','en'")); EXPECT_EQ(R"("")", run_get_display_locale("'zh-TW', 'not a locale code'"));
EXPECT_EQ(R"("English")", run_get_display_language("'en-US','en'")); EXPECT_EQ(R"("English")", run_get_display_locale("'en', 'en'"));
EXPECT_EQ(R"("français")", run_get_display_language("'fr','fr'")); EXPECT_EQ(R"|("English (United States)")|",
EXPECT_EQ(R"("español")", run_get_display_language("'es','es'")); run_get_display_locale("'en-US', 'en'"));
EXPECT_EQ(R"("日本語")", run_get_display_language("'ja','ja'")); EXPECT_EQ(R"("français")", run_get_display_locale("'fr', 'fr-FR'"));
EXPECT_EQ(R"("anglais")", run_get_display_language("'en','fr'")); EXPECT_EQ(R"|("français (France)")|",
EXPECT_EQ(R"("Japanese")", run_get_display_language("'ja','en'")); 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 } // 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 @@ ...@@ -6,4 +6,4 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
</head> </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 = { ...@@ -148,13 +148,13 @@ chrome.accessibilityPrivate.FocusType = {
chrome.accessibilityPrivate.FocusRingInfo; chrome.accessibilityPrivate.FocusRingInfo;
/** /**
* Called to translate languageCodeToTranslate into human-readable string in the * Called to translate localeCodeToTranslate into human-readable string in the
* language specified by targetLanguageCode * locale specified by displayLocaleCode
* @param {string} languageCodeToTranslate * @param {string} localeCodeToTranslate
* @param {string} targetLanguageCode * @param {string} displayLocaleCode
* @return {string} The human-readable language string in the provided language. * @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. * Called to request battery status from Chrome OS system.
......
...@@ -547,7 +547,8 @@ bool IsLocaleNameTranslated(const char* locale, ...@@ -547,7 +547,8 @@ bool IsLocaleNameTranslated(const char* locale,
base::string16 GetDisplayNameForLocale(const std::string& locale, base::string16 GetDisplayNameForLocale(const std::string& locale,
const std::string& display_locale, const std::string& display_locale,
bool is_for_ui) { bool is_for_ui,
bool disallow_default) {
std::string locale_code = locale; std::string locale_code = locale;
// Internally, we use the language code of zh-CN and zh-TW, but we want the // 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 // display names to be Chinese (Simplified) and Chinese (Traditional) instead
...@@ -594,6 +595,8 @@ base::string16 GetDisplayNameForLocale(const std::string& locale, ...@@ -594,6 +595,8 @@ base::string16 GetDisplayNameForLocale(const std::string& locale,
locale_code.c_str(), display_locale.c_str(), locale_code.c_str(), display_locale.c_str(),
base::WriteInto(&display_name, kBufferSize), kBufferSize - 1, &error); base::WriteInto(&display_name, kBufferSize), kBufferSize - 1, &error);
} }
if (disallow_default && U_USING_DEFAULT_WARNING == error)
return base::string16();
DCHECK(U_SUCCESS(error)); DCHECK(U_SUCCESS(error));
display_name.resize(actual_size); display_name.resize(actual_size);
} }
......
...@@ -71,7 +71,8 @@ bool IsLocaleSupportedByOS(const std::string& locale); ...@@ -71,7 +71,8 @@ bool IsLocaleSupportedByOS(const std::string& locale);
UI_BASE_EXPORT base::string16 GetDisplayNameForLocale( UI_BASE_EXPORT base::string16 GetDisplayNameForLocale(
const std::string& locale, const std::string& locale,
const std::string& display_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|. // Returns the display name of the |country_code| in |display_locale|.
UI_BASE_EXPORT base::string16 GetDisplayNameForCountry( 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