Commit e6b2b6d5 authored by Akihiro Ota's avatar Akihiro Ota Committed by Commit Bot

ChromeVox Tutorial: Add ability to localize strings.

The interactive tutorial uses hard-coded strings to specify text
content. Included in this patch:

1. Add tutorial_common.js, which can be used to share code between
the tutorial and lesson components
2. Add a function, getMsg(), to fetch localized strings
3. Change some hard coded strings to message IDs
4. Clean up BUILD.gn

fr and es) and verify that content gets translated.

Bug: 1075752
Test: Manually load the tutorial in several locales (I used
Change-Id: I5784f1466f3646e31a41145478db9dfcb766f70e
AX-Relnotes: N/A
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2254345
Commit-Queue: Akihiro Ota <akihiroota@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#813361}
parent 1f847b3e
......@@ -192,8 +192,7 @@ run_jsbundler("chromevox_copied_files") {
"background/keymaps/default_keymap.json",
"background/logging/log.css",
"background/logging/log.html",
"i_tutorial/components/i_tutorial.js",
"i_tutorial/components/tutorial_lesson.js",
"i_tutorial/components/tutorial_common.js",
"i_tutorial/lessons/basic_navigation.html",
"i_tutorial/lessons/jump_commands.html",
"i_tutorial/lessons/selects.html",
......@@ -496,7 +495,7 @@ if (is_chromeos) {
copy("lesson_component") {
sources = [ "$chromevox_gen_dir/i_tutorial/components/tutorial_lesson.js" ]
outputs = [ "$chromevox_out_dir/i_tutorial/tutorial_lesson.js" ]
outputs = [ "$chromevox_out_dir/i_tutorial/components/tutorial_lesson.js" ]
deps = [
"i_tutorial/components:closure_compile",
"i_tutorial/components:components",
......@@ -505,7 +504,7 @@ copy("lesson_component") {
copy("tutorial_component") {
sources = [ "$chromevox_gen_dir/i_tutorial/components/i_tutorial.js" ]
outputs = [ "$chromevox_out_dir/i_tutorial/i_tutorial.js" ]
outputs = [ "$chromevox_out_dir/i_tutorial/components/i_tutorial.js" ]
deps = [
"i_tutorial/components:closure_compile",
......
......@@ -16,6 +16,7 @@ js_type_check("closure_compile") {
is_polymer3 = true
deps = [
":i_tutorial",
":tutorial_common",
":tutorial_lesson",
]
}
......@@ -31,3 +32,6 @@ js_library("i_tutorial") {
"//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
]
}
js_library("tutorial_common") {
}
......@@ -9,7 +9,9 @@
import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {TutorialCommon} from './tutorial_common.js';
import {TutorialLesson} from './tutorial_lesson.js';
/** @enum {string} */
......@@ -64,6 +66,8 @@ Polymer({
_template: html`{__html_template__}`,
behaviors: [TutorialCommon],
properties: {
curriculum: {
type: String,
......@@ -312,38 +316,22 @@ Polymer({
},
{
title: 'On, Off, and Stop',
content: [
'To temporarily stop ChromeVox from speaking, press the Control ' +
'key.',
'To turn ChromeVox on or off, use Control+Alt+Z.',
],
title: 'tutorial_on_off_heading',
content: ['tutorial_control', 'tutorial_on_off'],
medium: InteractionMedium.KEYBOARD,
curriculums: [Curriculum.ESSENTIAL_KEYS],
},
{
title: 'The ChromeVox Modifier Key',
content: [
'In ChromeVox, the Search key is the modifier key. ' +
'Most ChromeVox shortcuts start with the Search key. ' +
'You’ll also use the arrow keys for navigation.',
'On the Chromebook, the Search key is immediately above the ' +
'left Shift key.',
],
title: 'tutorial_modifier_heading',
content: ['tutorial_modifier', 'tutorial_chromebook_search'],
medium: InteractionMedium.KEYBOARD,
curriculums: [Curriculum.ESSENTIAL_KEYS],
},
{
title: 'Basic Navigation',
content: [
'To move forward between items on a page, press Search + Right ' +
'Arrow, or Search + Left Arrow to jump back.',
'To go to the next line, press Search + Down Arrow. ' +
'To get to the previous line, use Search + Up Arrow.',
'If you reach an item you want to click, press Search + Space.',
],
title: 'tutorial_basic_navigation_heading',
content: ['tutorial_basic_navigation'],
medium: InteractionMedium.KEYBOARD,
curriculums: [Curriculum.NAVIGATION],
practiceTitle: 'Basic Navigation Practice',
......@@ -364,15 +352,11 @@ Polymer({
},
{
title: 'Jump Commands',
title: 'tutorial_jump_heading',
content: [
'Use jump commands to skip to specific types of elements.',
'To jump forward between headings, press Search + H, or to ' +
'jump backward, press Search + Shift + H.',
'To jump forward between buttons, press Search + B, or to ' +
'jump backward, press Search + Shift + B',
'To jump foorward beetween links, press Search + L, or to ' +
'jump backward, press Search + Shift + L'
'tutorial_jump',
'tutorial_jump_second_heading',
'tutorial_jump_wrap_heading',
],
medium: InteractionMedium.KEYBOARD,
curriculums: [Curriculum.NAVIGATION],
......@@ -403,48 +387,27 @@ Polymer({
},
{
title: 'The ChromeVox Menu',
title: 'tutorial_menus_heading',
content: [
'To explore all ChromeVox commands and shortcuts, press ' +
'Search + Period, then use the Arrow keys to navigate the ' +
'menus, and Enter to activate a command. Return here by ' +
'pressing Search+o then t.',
'tutorial_menus',
],
medium: InteractionMedium.KEYBOARD,
curriculums: [Curriculum.COMMAND_REFERENCES]
},
{
title: 'Helpful Chrome Shortcuts',
title: 'tutorial_chrome_shortcuts_heading',
content: [
'The next few shortcuts aren’t ChromeVox commands, but they are ' +
'still very useful for getting the most out of Chrome.',
'To navigate forward through actionable items like buttons and ' +
'links, press the Tab key. To navigate backwards, press ' +
'Shift+Tab.',
'To enter the Chrome browser address box, also called the ' +
'omnibox, press Control + L.',
'To open and go to a new tab automatically, press Control+T. ' +
'Your cursor will be in the omnibox.',
' To close a tab, press Control+W.',
'To move forward between open tabs, use Control+Tab.',
'To open the Chrome browser menu, press Alt+F.',
'To open the full list of keyboard shortcuts, press ' +
'Control + Alt + /'
'tutorial_chrome_shortcuts',
'tutorial_chromebook_ctrl_forward',
],
medium: InteractionMedium.KEYBOARD,
curriculums: [Curriculum.COMMAND_REFERENCES]
},
{
title: 'Sounds',
content:
['ChromeVox uses sounds to give you essential and additional ' +
'information. You can use these sounds to navigate more ' +
'quickly by learning what each sound means. Once you get ' +
'more comfortable, you can turn off verbose descriptions in ' +
'speech and rely on them for essential information about the ' +
'page. Here is a complete list of sounds and what they mean'],
title: 'tutorial_earcon_page_title',
content: ['tutorial_earcon_page_body'],
medium: InteractionMedium.KEYBOARD,
curriculums: [Curriculum.SOUNDS_AND_SETTINGS]
},
......@@ -471,14 +434,10 @@ Polymer({
},
{
title: 'Resources',
title: 'tutorial_learn_more_heading',
content: [
'You’ve learned the essentials to use ChromeVox successfully. ' +
'Remember that you can open the ChromeVox command menu at ' +
'any time by pressing Search+Period. To learn even more ' +
'about ChromeVox and Chrome OS, visit the following articles.',
'If you are done with the tutorial, use ChromeVox to navigate ' +
'to the Quit button and click it.'
'tutorial_learn_more', 'next_command_reference',
'chrome_keyboard_shortcuts', 'touchscreen_accessibility'
],
medium: InteractionMedium.KEYBOARD,
curriculums: [Curriculum.RESOURCES],
......@@ -661,7 +620,7 @@ Polymer({
for (const lesson of this.includedLessons) {
const button = document.createElement('cr-button');
button.addEventListener('click', this.showLesson.bind(this, count - 1));
button.textContent = lesson.title;
button.textContent = this.getMsg(lesson.title);
this.$.lessonShortcuts.appendChild(button);
count += 1;
}
......@@ -963,7 +922,7 @@ Polymer({
readCurrentLessonTitle() {
const lesson = this.getCurrentLesson();
this.requestSpeech(
lesson.title, QueueMode.INTERJECT, {doNotInterrupt: true});
this.getMsg(lesson.title), QueueMode.INTERJECT, {doNotInterrupt: true});
},
/**
......@@ -975,21 +934,22 @@ Polymer({
const lesson = this.getCurrentLesson();
for (const text of lesson.content) {
// Queue lesson content so it is read after the lesson title.
this.requestSpeech(text, QueueMode.QUEUE);
this.requestSpeech(this.getMsg(text), QueueMode.QUEUE);
}
},
/**
* @private
* @suppress {undefinedVars|missingProperties} For referencing
* EarconDescription and Msgs, which are defined on the Panel window.
* EarconDescription, which is defined on the Panel window.
*/
buildEarconLesson() {
// Find earcon lesson.
let earconLesson;
const elements = this.$.lessonContainer.children;
for (const element of elements) {
if (element.is === 'tutorial-lesson' && element.title === 'Sounds') {
if (element.is === 'tutorial-lesson' &&
element.title === 'tutorial_earcon_page_title') {
earconLesson = element;
}
}
......@@ -1002,7 +962,7 @@ Polymer({
for (const earconId in EarconDescription) {
const msgid = EarconDescription[earconId];
const earconElement = document.createElement('p');
earconElement.innerText = Msgs.getMsg(msgid);
earconElement.innerText = this.getMsg(msgid);
earconElement.setAttribute('tabindex', -1);
earconElement.addEventListener(
'focus', this.requestEarcon.bind(this, earconId));
......@@ -1017,5 +977,5 @@ Polymer({
requestEarcon(earconId) {
this.dispatchEvent(
new CustomEvent('requestearcon', {composed: true, detail: {earconId}}));
}
},
});
// Copyright 2020 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.
/**
* @fileoverview Defines TutorialCommon, a Polymer behavior to perform generic
* tutorial-related functions.
*/
/**
* @polymerBehavior
* @suppress {undefinedVars|missingProperties}
*/
export const TutorialCommon = {
/**
* Returns the message with the given message id from the ChromeVox namespace.
* @param {string} idOrValue The id of the string, or the hard-coded string
* value.
* @param {Array<string>=} opt_subs Substitution strings.
* @return {string} The localized message.
*/
getMsg(idOrValue, opt_subs) {
// TODO(akihiroota): Remove this check after adding all strings to
// chromevox_strings.grdp.
// If we get a string that doesn't include a '_', then it's a hard-coded
// string value. Return it, since a message id doesn't exist for it yet.
if (!idOrValue.includes('_')) {
return idOrValue;
}
return Msgs.getMsg(idOrValue, opt_subs);
}
};
......@@ -94,12 +94,12 @@ a {
<div id="container" hidden>
<template is="dom-if" if="[[ title ]]">
<h1 id="title" tabindex="-1">[[ title ]]</h1>
<h1 id="title" tabindex="-1">[[ getMsg(title) ]]</h1>
</template>
<div id="content">
<template id="contentTemplate" is="dom-repeat" items="[[ content ]]"
as="text">
<p tabindex="-1">[[ text ]]</p>
<p tabindex="-1">[[ getMsg(text) ]]</p>
</template>
</div>
<cr-dialog id="practice" close-text="Exit practice area"
......
......@@ -11,12 +11,15 @@ import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {TutorialCommon} from './tutorial_common.js';
export const TutorialLesson = Polymer({
is: 'tutorial-lesson',
_template: html`{__html_template__}`,
behaviors: [TutorialCommon],
properties: {
lessonNum: {type: Number},
......@@ -267,5 +270,10 @@ export const TutorialLesson = Polymer({
/** @return {Element} */
get contentDiv() {
return this.$.content;
},
/** @return {string} */
getTitleText() {
return this.$.title.textContent;
}
});
\ No newline at end of file
......@@ -1166,10 +1166,10 @@ Panel = class {
*/
static createITutorial(curriculum) {
const tutorialScript = document.createElement('script');
tutorialScript.src = '../i_tutorial/i_tutorial.js';
tutorialScript.src = '../i_tutorial/components/i_tutorial.js';
tutorialScript.setAttribute('type', 'module');
const lessonScript = document.createElement('script');
lessonScript.src = '../i_tutorial/tutorial_lesson.js';
lessonScript.src = '../i_tutorial/components/tutorial_lesson.js';
lessonScript.setAttribute('type', 'module');
document.body.appendChild(tutorialScript);
document.body.appendChild(lessonScript);
......
......@@ -393,7 +393,7 @@ TEST_F('ChromeVoxTutorialTest', 'NextPreviousButtons', function() {
.call(doCmd('nextButton'))
.expectSpeech('Next lesson')
.call(doCmd('forceClickOnCurrentItem'))
.expectSpeech('The ChromeVox Modifier Key', 'Heading 1')
.expectSpeech('The ChromeVox modifier key', 'Heading 1')
.call(this.assertActiveLessonIndex.bind(this, 1))
.call(doCmd('nextButton'))
.expectSpeech('Previous lesson')
......
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