Commit 1f2b3e26 authored by Akihiro Ota's avatar Akihiro Ota Committed by Commit Bot

ChromeVox Tutorial: Localize all strings.

This change:
1. Adds logic to localize the practice area
2. Adds tutorial strings to chromevox_strings.grdp
3. Removes unused strings, such as those spoken after a key sequence
is pressed.
4. Removes the text fields lesson, which was initially added as a
proof-of-concept.
5. Removes practice area state announcements, which was also
initially added as proof-of-concept.

Bug: 1075752, 1143480
Change-Id: Ia564736023530b87a604d1e12164eda1ad96528a
AX-Relnotes: N/A
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2520168
Commit-Queue: Akihiro Ota <akihiroota@chromium.org>
Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#825506}
parent 26ae35fd
...@@ -193,10 +193,8 @@ run_jsbundler("chromevox_copied_files") { ...@@ -193,10 +193,8 @@ run_jsbundler("chromevox_copied_files") {
"background/logging/log.css", "background/logging/log.css",
"background/logging/log.html", "background/logging/log.html",
"i_tutorial/components/tutorial_common.js", "i_tutorial/components/tutorial_common.js",
"i_tutorial/lessons/basic_navigation.html", "i_tutorial/practice_areas/jump_commands.html",
"i_tutorial/lessons/jump_commands.html", "i_tutorial/practice_areas/selects.html",
"i_tutorial/lessons/selects.html",
"i_tutorial/lessons/text_fields.html",
"images/chromevox-128.png", "images/chromevox-128.png",
"images/chromevox-16.png", "images/chromevox-16.png",
"images/chromevox-19.png", "images/chromevox-19.png",
......
...@@ -101,27 +101,32 @@ cr-button { ...@@ -101,27 +101,32 @@ cr-button {
<div id="mainMenu" <div id="mainMenu"
hidden$="[[ shouldHideMainMenu(activeScreen) ]]"> hidden$="[[ shouldHideMainMenu(activeScreen) ]]">
<h1 id="mainMenuHeader" tabindex="-1" aria-describedby="mainMenuHeaderHint"> <h1 id="mainMenuHeader" tabindex="-1" aria-describedby="mainMenuHeaderHint">
[[ mainMenuHeader ]] [[ getMsg('tutorial_main_menu_header') ]]
</h1> </h1>
<div id="mainMenuHeaderHint" hidden>[[ mainMenuHeaderHint ]]</div> <div id="mainMenuHeaderHint" hidden>
[[ getMsg('tutorial_main_menu_header_description') ]]
</div>
<div id="mainMenuButtons"> <div id="mainMenuButtons">
<cr-link-row id="quickOrientationButton" class="hr" <cr-link-row id="quickOrientationButton" class="hr"
on-click="chooseCurriculum" label="[[ quickOrientation ]]"> on-click="chooseCurriculum"
label="[[ getMsg('tutorial_quick_orientation_title') ]]">
</cr-link-row> </cr-link-row>
<cr-link-row id="essentialKeysButton" class="hr" on-click="chooseCurriculum" <cr-link-row id="essentialKeysButton" class="hr" on-click="chooseCurriculum"
label="[[ essentialKeys ]]"> label="[[ getMsg('tutorial_essential_keys_title') ]]">
</cr-link-row> </cr-link-row>
<cr-link-row id="navigationButton" class="hr" on-click="chooseCurriculum" <cr-link-row id="navigationButton" class="hr" on-click="chooseCurriculum"
label="[[ navigation ]]"> label="[[ getMsg('tutorial_navigation_title') ]]">
</cr-link-row> </cr-link-row>
<cr-link-row id="commandReferencesButton" class="hr" <cr-link-row id="commandReferencesButton" class="hr"
on-click="chooseCurriculum" label="[[ commandReferences ]]"> on-click="chooseCurriculum"
label="[[ getMsg('tutorial_command_references_title') ]]">
</cr-link-row> </cr-link-row>
<cr-link-row id="soundsAndSettingsButton" class="hr" <cr-link-row id="soundsAndSettingsButton" class="hr"
on-click="chooseCurriculum" label="[[ soundsAndSettings ]]"> on-click="chooseCurriculum"
label="[[ getMsg('tutorial_sounds_and_settings_title') ]]">
</cr-link-row> </cr-link-row>
<cr-link-row id="resourcesButton" class="hr" on-click="chooseCurriculum" <cr-link-row id="resourcesButton" class="hr" on-click="chooseCurriculum"
label="[[ resources ]]"> label="[[ getMsg('tutorial_resources_title') ]]">
</cr-link-row> </cr-link-row>
</div> </div>
</div> </div>
...@@ -132,7 +137,9 @@ cr-button { ...@@ -132,7 +137,9 @@ cr-button {
tabindex="-1" aria-describedby="lessonMenuHeaderHint"> tabindex="-1" aria-describedby="lessonMenuHeaderHint">
[[ computeLessonMenuHeader(curriculum) ]] [[ computeLessonMenuHeader(curriculum) ]]
</h1> </h1>
<div id="lessonMenuHeaderHint" hidden>[[ lessonMenuHeaderHint ]]</div> <div id="lessonMenuHeaderHint" hidden>
[[ getMsg('tutorial_lesson_menu_header_description') ]]
</div>
<div id="lessonShortcuts"></div> <div id="lessonShortcuts"></div>
</div> </div>
...@@ -168,12 +175,12 @@ cr-button { ...@@ -168,12 +175,12 @@ cr-button {
<cr-button id="nextLesson" class="action-button" on-click="showNextLesson" <cr-button id="nextLesson" class="action-button" on-click="showNextLesson"
hidden$="[[ hidden$="[[
shouldHideNextLessonButton(activeLessonIndex, activeScreen) ]]"> shouldHideNextLessonButton(activeLessonIndex, activeScreen) ]]">
[[ nextLesson ]] [[ getMsg('tutorial_next_lesson_button') ]]
</cr-button> </cr-button>
<cr-button on-click="showPreviousLesson" <cr-button on-click="showPreviousLesson"
hidden$="[[ hidden$="[[
shouldHidePreviousLessonButton(activeLessonIndex, activeScreen) ]]"> shouldHidePreviousLessonButton(activeLessonIndex, activeScreen) ]]">
[[ previousLesson ]] [[ getMsg('tutorial_previous_lesson_button') ]]
</cr-button> </cr-button>
</div> </div>
<div id="menuNav"> <div id="menuNav">
...@@ -181,18 +188,18 @@ cr-button { ...@@ -181,18 +188,18 @@ cr-button {
hidden$="[[ hidden$="[[
shouldHideRestartQuickOrientationButton(activeLessonIndex, activeScreen) shouldHideRestartQuickOrientationButton(activeLessonIndex, activeScreen)
]]"> ]]">
[[ restartQuickOrientation ]] [[ getMsg('tutorial_restart_quick_orientation_button') ]]
</cr-button> </cr-button>
<cr-button on-click="showLessonMenu" <cr-button on-click="showLessonMenu"
hidden$="[[ shouldHideLessonMenuButton(activeScreen) ]]"> hidden$="[[ shouldHideLessonMenuButton(activeScreen) ]]">
[[ lessonMenu ]] [[ getMsg('tutorial_lesson_menu_button') ]]
</cr-button> </cr-button>
<cr-button on-click="showMainMenu" <cr-button on-click="showMainMenu"
hidden$="[[ shouldHideMainMenuButton(activeScreen) ]]"> hidden$="[[ shouldHideMainMenuButton(activeScreen) ]]">
[[ mainMenu ]] [[ getMsg('tutorial_main_menu_button') ]]
</cr-button> </cr-button>
<cr-button id="exitTutorial" on-click="exit"> <cr-button id="exitTutorial" on-click="exit">
[[ exitTutorial ]] [[ getMsg('tutorial_exit_button') ]]
</cr-button> </cr-button>
</div> </div>
</div> </div>
......
...@@ -20,13 +20,6 @@ export const TutorialCommon = { ...@@ -20,13 +20,6 @@ export const TutorialCommon = {
* @return {string} The localized message. * @return {string} The localized message.
*/ */
getMsg(idOrValue, opt_subs) { 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); return Msgs.getMsg(idOrValue, opt_subs);
} }
}; };
...@@ -33,10 +33,10 @@ a { ...@@ -33,10 +33,10 @@ a {
text-align: start text-align: start
} }
#content a,
#content p, #content p,
#practiceInstructions, #practiceInstructions,
#practiceBody { #practiceBody {
color: black;
font-size: 22px; font-size: 22px;
font-weight: normal; font-weight: normal;
line-height: 28px; line-height: 28px;
...@@ -45,6 +45,12 @@ a { ...@@ -45,6 +45,12 @@ a {
text-align: start text-align: start
} }
#content p,
#practiceInstructions,
#practiceBody {
color: black;
}
/* Practice area */ /* Practice area */
#practiceTitle { #practiceTitle {
...@@ -94,7 +100,9 @@ a { ...@@ -94,7 +100,9 @@ a {
<h1 id="title" tabindex="-1" aria-describedby="titleHint"> <h1 id="title" tabindex="-1" aria-describedby="titleHint">
[[ getMsg(title) ]] [[ getMsg(title) ]]
</h1> </h1>
<div id="titleHint" hidden>[[ titleHint ]]</div> <div id="titleHint" hidden>
[[ getMsg('tutorial_lesson_title_description') ]]
</div>
</template> </template>
<div id="content"> <div id="content">
<template id="contentTemplate" is="dom-repeat" items="[[ content ]]" <template id="contentTemplate" is="dom-repeat" items="[[ content ]]"
...@@ -104,18 +112,22 @@ a { ...@@ -104,18 +112,22 @@ a {
</div> </div>
<cr-dialog id="practice"> <cr-dialog id="practice">
<div id="practiceTitle" class="title" slot="title" tabindex="-1"> <div id="practiceTitle" class="title" slot="title" tabindex="-1">
[[ practiceTitle ]] [[ getMsg(practiceTitle) ]]
</div> </div>
<div id="practiceBody" class="body" slot="body" tabindex="-1"> <div id="practiceBody" class="body" slot="body" tabindex="-1">
<p id="practiceInstructions" tabindex="-1">[[ practiceInstructions ]]</p> <p id="practiceInstructions" tabindex="-1">
[[ getMsg(practiceInstructions) ]]
</p>
<div id="practiceContent"></div> <div id="practiceContent"></div>
</div> </div>
<div class="button-container" slot="button-container"> <div class="button-container" slot="button-container">
<cr-button id="closePractice" on-click="endPractice"> <cr-button id="closePractice" on-click="endPractice">
Close [[ getMsg('tutorial_practice_area_close_button') ]]
</cr-button> </cr-button>
</div> </div>
</cr-dialog> </cr-dialog>
<cr-button id="startPractice" on-click="startPractice" <cr-button id="startPractice" on-click="startPractice"
hidden$="[[ shouldHidePracticeButton() ]]">Practice Area</cr-button> hidden$="[[ shouldHidePracticeButton() ]]">
</div> [[ getMsg('tutorial_practice_area_open_button') ]]
\ No newline at end of file </cr-button>
</div>
...@@ -53,16 +53,6 @@ export const TutorialLesson = Polymer({ ...@@ -53,16 +53,6 @@ export const TutorialLesson = Polymer({
// Observed properties. // Observed properties.
activeLessonNum: {type: Number, observer: 'setVisibility'}, activeLessonNum: {type: Number, observer: 'setVisibility'},
titleHint: {
type: String,
value: 'Press Search + left/right arrow to navigate the lesson'
},
practiceTitleHint: {
type: String,
value: 'Press Search + left/right arrow to navigate the practice area'
}
}, },
/** @override */ /** @override */
...@@ -144,12 +134,13 @@ export const TutorialLesson = Polymer({ ...@@ -144,12 +134,13 @@ export const TutorialLesson = Polymer({
* @private * @private
*/ */
populatePracticeContent() { populatePracticeContent() {
const path = '../i_tutorial/lessons/' + this.practiceFile + '.html'; const path = '../i_tutorial/practice_areas/' + this.practiceFile + '.html';
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('GET', path, true); xhr.open('GET', path, true);
xhr.onload = (evt) => { xhr.onload = (evt) => {
if (xhr.readyState === 4 && xhr.status === 200) { if (xhr.readyState === 4 && xhr.status === 200) {
this.$.practiceContent.innerHTML = xhr.responseText; this.$.practiceContent.innerHTML = xhr.responseText;
this.localizePracticeAreaContent();
} else { } else {
console.error(xhr.statusText); console.error(xhr.statusText);
} }
...@@ -235,12 +226,6 @@ export const TutorialLesson = Polymer({ ...@@ -235,12 +226,6 @@ export const TutorialLesson = Polymer({
onGoalStateReached() { onGoalStateReached() {
const previousState = this.goalStateReached; const previousState = this.goalStateReached;
this.goalStateReached = true; this.goalStateReached = true;
if (previousState === false) {
// Only perform when crossing the threshold from not reached to reached.
this.requestSpeech(
'You have passed this tutorial lesson. Find and press the exit ' +
'practice area button to continue');
}
}, },
// Miscellaneous methods. // Miscellaneous methods.
...@@ -289,5 +274,15 @@ export const TutorialLesson = Polymer({ ...@@ -289,5 +274,15 @@ export const TutorialLesson = Polymer({
/** @return {string} */ /** @return {string} */
getTitleText() { getTitleText() {
return this.$.title.textContent; return this.$.title.textContent;
},
/** @private */
localizePracticeAreaContent() {
const root = this.$.practiceContent;
const elements = root.querySelectorAll('[msgid]');
for (const element of elements) {
const msgId = element.getAttribute('msgid');
element.textContent = this.getMsg(msgId);
}
} }
}); });
\ No newline at end of file
<!-- 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. -->
<script type="module"
src="chrome://resources/cr_elements/cr_button/cr_button.m.js">
</script>
<h3>This is a heading</h3>
<cr-button>This is a button</cr-button>
<p>You're almost there</p>
<p>You're very close to the button</p>
<cr-button id="goal">Click me</cr-button>
<p>You're very close to the button</p>
<p>You're almost there</p>
<cr-button>This is another button</cr-button>
<h3>This is another heading</h3>
<!-- 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. -->
<h5>This is the first heading. Press Search + H to go to the next heading</h5>
<h5>
This is the second heading. Keep going; either press Search+H or Search+Shift+H
</h5>
<h5>This is the last heading. Press Search+H to wrap to the first heading, or
Search+Shift+H to go to the second heading on this page.
</h5>
\ No newline at end of file
<!-- 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. -->
<p>There is an input below</p>
<input id="input" aria-lable="Edit me">
<p>There is a rich multiline text fields below, which appears in Gmail.</p>
<div id="editable" role="textbox" contenteditable="true" aria-label="Edit me">
</div>
\ No newline at end of file
<!-- Copyright 2020 The Chromium Authors. All rights reserved. <!-- Copyright 2020 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. --> found in the LICENSE file. -->
<label id="selectLabel">Choose your favorite season</label> <h5 msgid="tutorial_jump_first_heading"></h5>
<select class="md-select" aria-labelledby="selectLabel"> <h5 msgid="tutorial_jump_second_heading"></h5>
<option>Spring</option> <h5 msgid="tutorial_jump_wrap_heading"></h5>
<option>Summer</option> \ No newline at end of file
<option>Fall</option>
<option>Winter</option>
</select>
<!-- 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. -->
<label id="selectLabel" msgid="tutorial_quick_orientation_lists_practice_label">
</label>
<select class="md-select" aria-labelledby="selectLabel">
<option msgid="tutorial_quick_orientation_lists_practice_spring"></option>
<option msgid="tutorial_quick_orientation_lists_practice_summer"></option>
<option msgid="tutorial_quick_orientation_lists_practice_fall"></option>
<option msgid="tutorial_quick_orientation_lists_practice_winter"></option>
</select>
...@@ -120,7 +120,8 @@ TEST_F('ChromeVoxTutorialTest', 'BasicTest', function() { ...@@ -120,7 +120,8 @@ TEST_F('ChromeVoxTutorialTest', 'BasicTest', function() {
mockFeedback mockFeedback
.expectSpeech( .expectSpeech(
'ChromeVox tutorial', 'Heading 1', 'ChromeVox tutorial', 'Heading 1',
'Press Search + left/right arrow to browse topics') ' Press Search + Right Arrow, or Search + Left Arrow to browse' +
' topics ')
.call(doCmd('nextObject')) .call(doCmd('nextObject'))
.expectSpeech('Quick orientation', 'Link') .expectSpeech('Quick orientation', 'Link')
.call(doCmd('nextObject')) .call(doCmd('nextObject'))
...@@ -152,8 +153,8 @@ TEST_F('ChromeVoxTutorialTest', 'LessonSetTest', function() { ...@@ -152,8 +153,8 @@ TEST_F('ChromeVoxTutorialTest', 'LessonSetTest', function() {
.call(doCmd('forceClickOnCurrentItem')) .call(doCmd('forceClickOnCurrentItem'))
.expectSpeech(/Quick Orientation Tutorial, [0-9]+ Lessons/) .expectSpeech(/Quick Orientation Tutorial, [0-9]+ Lessons/)
.expectSpeech( .expectSpeech(
'Press Search + left/right arrow to browse lessons for ' + ' Press Search + Right Arrow, or Search + Left Arrow to browse ' +
'this topic') 'lessons for this topic ')
.call(doCmd('nextObject')) .call(doCmd('nextObject'))
.expectSpeech('Welcome to ChromeVox!') .expectSpeech('Welcome to ChromeVox!')
.call(() => { .call(() => {
...@@ -192,7 +193,8 @@ TEST_F('ChromeVoxTutorialTest', 'NoPracticeAreaTest', function() { ...@@ -192,7 +193,8 @@ TEST_F('ChromeVoxTutorialTest', 'NoPracticeAreaTest', function() {
}) })
.expectSpeech( .expectSpeech(
'On, Off, and Stop', 'Heading 1', 'On, Off, and Stop', 'Heading 1',
'Press Search + left/right arrow to navigate the lesson') ' Press Search + Right Arrow, or Search + Left Arrow to navigate ' +
'this lesson ')
.call(doCmd('nextButton')) .call(doCmd('nextButton'))
.expectSpeech('Next lesson') .expectSpeech('Next lesson')
.replay(); .replay();
...@@ -219,7 +221,7 @@ TEST_F('ChromeVoxTutorialTest', 'HasPracticeAreaTest', function() { ...@@ -219,7 +221,7 @@ TEST_F('ChromeVoxTutorialTest', 'HasPracticeAreaTest', function() {
}) })
.expectSpeech('Jump Commands', 'Heading 1') .expectSpeech('Jump Commands', 'Heading 1')
.call(doCmd('nextButton')) .call(doCmd('nextButton'))
.expectSpeech('Practice Area') .expectSpeech('Practice area')
.replay(); .replay();
}); });
}); });
...@@ -278,7 +280,7 @@ TEST_F('ChromeVoxTutorialTest', 'DISABLED_PracticeAreaNudgesTest', function() { ...@@ -278,7 +280,7 @@ TEST_F('ChromeVoxTutorialTest', 'DISABLED_PracticeAreaNudgesTest', function() {
}) })
.expectSpeech('Basic Navigation', 'Heading 1') .expectSpeech('Basic Navigation', 'Heading 1')
.call(doCmd('nextButton')) .call(doCmd('nextButton'))
.expectSpeech('Practice Area') .expectSpeech('Practice area')
.call(doCmd('forceClickOnCurrentItem')) .call(doCmd('forceClickOnCurrentItem'))
.expectSpeech(/Try using basic navigation to navigate/) .expectSpeech(/Try using basic navigation to navigate/)
.call(giveNudge) .call(giveNudge)
...@@ -454,7 +456,9 @@ TEST_F('ChromeVoxTutorialTest', 'LessonHint', function() { ...@@ -454,7 +456,9 @@ TEST_F('ChromeVoxTutorialTest', 'LessonHint', function() {
tutorial.showLesson(0); tutorial.showLesson(0);
}) })
.expectSpeech('On, Off, and Stop', 'Heading 1') .expectSpeech('On, Off, and Stop', 'Heading 1')
.expectSpeech('Press Search + left/right arrow to navigate the lesson') .expectSpeech(
' Press Search + Right Arrow, or Search + Left Arrow to navigate' +
' this lesson ')
.replay(); .replay();
}); });
}); });
...@@ -539,7 +543,7 @@ TEST_F('ChromeVoxTutorialTest', 'QuickOrientationLessonTest', function() { ...@@ -539,7 +543,7 @@ TEST_F('ChromeVoxTutorialTest', 'QuickOrientationLessonTest', function() {
// the next lesson. // the next lesson.
.call(simulateKeyPress.bind(this, KeyCode.SPACE, {})) .call(simulateKeyPress.bind(this, KeyCode.SPACE, {}))
.expectSpeech('Essential Keys: Control') .expectSpeech('Essential Keys: Control')
.expectSpeech(/Let's start with a few keys you'll use regularly:/) .expectSpeech(/Let's start with a few keys you'll use regularly./)
.call(() => { .call(() => {
assertEquals(1, tutorial.activeLessonNum); assertEquals(1, tutorial.activeLessonNum);
assertNotEquals(firstLessonNode, getRangeStartNode()); assertNotEquals(firstLessonNode, getRangeStartNode());
......
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