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") {
"background/logging/log.css",
"background/logging/log.html",
"i_tutorial/components/tutorial_common.js",
"i_tutorial/lessons/basic_navigation.html",
"i_tutorial/lessons/jump_commands.html",
"i_tutorial/lessons/selects.html",
"i_tutorial/lessons/text_fields.html",
"i_tutorial/practice_areas/jump_commands.html",
"i_tutorial/practice_areas/selects.html",
"images/chromevox-128.png",
"images/chromevox-16.png",
"images/chromevox-19.png",
......
......@@ -101,27 +101,32 @@ cr-button {
<div id="mainMenu"
hidden$="[[ shouldHideMainMenu(activeScreen) ]]">
<h1 id="mainMenuHeader" tabindex="-1" aria-describedby="mainMenuHeaderHint">
[[ mainMenuHeader ]]
[[ getMsg('tutorial_main_menu_header') ]]
</h1>
<div id="mainMenuHeaderHint" hidden>[[ mainMenuHeaderHint ]]</div>
<div id="mainMenuHeaderHint" hidden>
[[ getMsg('tutorial_main_menu_header_description') ]]
</div>
<div id="mainMenuButtons">
<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 id="essentialKeysButton" class="hr" on-click="chooseCurriculum"
label="[[ essentialKeys ]]">
label="[[ getMsg('tutorial_essential_keys_title') ]]">
</cr-link-row>
<cr-link-row id="navigationButton" class="hr" on-click="chooseCurriculum"
label="[[ navigation ]]">
label="[[ getMsg('tutorial_navigation_title') ]]">
</cr-link-row>
<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 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 id="resourcesButton" class="hr" on-click="chooseCurriculum"
label="[[ resources ]]">
label="[[ getMsg('tutorial_resources_title') ]]">
</cr-link-row>
</div>
</div>
......@@ -132,7 +137,9 @@ cr-button {
tabindex="-1" aria-describedby="lessonMenuHeaderHint">
[[ computeLessonMenuHeader(curriculum) ]]
</h1>
<div id="lessonMenuHeaderHint" hidden>[[ lessonMenuHeaderHint ]]</div>
<div id="lessonMenuHeaderHint" hidden>
[[ getMsg('tutorial_lesson_menu_header_description') ]]
</div>
<div id="lessonShortcuts"></div>
</div>
......@@ -168,12 +175,12 @@ cr-button {
<cr-button id="nextLesson" class="action-button" on-click="showNextLesson"
hidden$="[[
shouldHideNextLessonButton(activeLessonIndex, activeScreen) ]]">
[[ nextLesson ]]
[[ getMsg('tutorial_next_lesson_button') ]]
</cr-button>
<cr-button on-click="showPreviousLesson"
hidden$="[[
shouldHidePreviousLessonButton(activeLessonIndex, activeScreen) ]]">
[[ previousLesson ]]
[[ getMsg('tutorial_previous_lesson_button') ]]
</cr-button>
</div>
<div id="menuNav">
......@@ -181,18 +188,18 @@ cr-button {
hidden$="[[
shouldHideRestartQuickOrientationButton(activeLessonIndex, activeScreen)
]]">
[[ restartQuickOrientation ]]
[[ getMsg('tutorial_restart_quick_orientation_button') ]]
</cr-button>
<cr-button on-click="showLessonMenu"
hidden$="[[ shouldHideLessonMenuButton(activeScreen) ]]">
[[ lessonMenu ]]
[[ getMsg('tutorial_lesson_menu_button') ]]
</cr-button>
<cr-button on-click="showMainMenu"
hidden$="[[ shouldHideMainMenuButton(activeScreen) ]]">
[[ mainMenu ]]
[[ getMsg('tutorial_main_menu_button') ]]
</cr-button>
<cr-button id="exitTutorial" on-click="exit">
[[ exitTutorial ]]
[[ getMsg('tutorial_exit_button') ]]
</cr-button>
</div>
</div>
......
......@@ -20,13 +20,6 @@ export const TutorialCommon = {
* @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);
}
};
......@@ -33,10 +33,10 @@ a {
text-align: start
}
#content a,
#content p,
#practiceInstructions,
#practiceBody {
color: black;
font-size: 22px;
font-weight: normal;
line-height: 28px;
......@@ -45,6 +45,12 @@ a {
text-align: start
}
#content p,
#practiceInstructions,
#practiceBody {
color: black;
}
/* Practice area */
#practiceTitle {
......@@ -94,7 +100,9 @@ a {
<h1 id="title" tabindex="-1" aria-describedby="titleHint">
[[ getMsg(title) ]]
</h1>
<div id="titleHint" hidden>[[ titleHint ]]</div>
<div id="titleHint" hidden>
[[ getMsg('tutorial_lesson_title_description') ]]
</div>
</template>
<div id="content">
<template id="contentTemplate" is="dom-repeat" items="[[ content ]]"
......@@ -104,18 +112,22 @@ a {
</div>
<cr-dialog id="practice">
<div id="practiceTitle" class="title" slot="title" tabindex="-1">
[[ practiceTitle ]]
[[ getMsg(practiceTitle) ]]
</div>
<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>
<div class="button-container" slot="button-container">
<cr-button id="closePractice" on-click="endPractice">
Close
[[ getMsg('tutorial_practice_area_close_button') ]]
</cr-button>
</div>
</cr-dialog>
<cr-button id="startPractice" on-click="startPractice"
hidden$="[[ shouldHidePracticeButton() ]]">Practice Area</cr-button>
hidden$="[[ shouldHidePracticeButton() ]]">
[[ getMsg('tutorial_practice_area_open_button') ]]
</cr-button>
</div>
......@@ -53,16 +53,6 @@ export const TutorialLesson = Polymer({
// Observed properties.
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 */
......@@ -144,12 +134,13 @@ export const TutorialLesson = Polymer({
* @private
*/
populatePracticeContent() {
const path = '../i_tutorial/lessons/' + this.practiceFile + '.html';
const path = '../i_tutorial/practice_areas/' + this.practiceFile + '.html';
const xhr = new XMLHttpRequest();
xhr.open('GET', path, true);
xhr.onload = (evt) => {
if (xhr.readyState === 4 && xhr.status === 200) {
this.$.practiceContent.innerHTML = xhr.responseText;
this.localizePracticeAreaContent();
} else {
console.error(xhr.statusText);
}
......@@ -235,12 +226,6 @@ export const TutorialLesson = Polymer({
onGoalStateReached() {
const previousState = this.goalStateReached;
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.
......@@ -289,5 +274,15 @@ export const TutorialLesson = Polymer({
/** @return {string} */
getTitleText() {
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.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<label id="selectLabel">Choose your favorite season</label>
<select class="md-select" aria-labelledby="selectLabel">
<option>Spring</option>
<option>Summer</option>
<option>Fall</option>
<option>Winter</option>
</select>
<h5 msgid="tutorial_jump_first_heading"></h5>
<h5 msgid="tutorial_jump_second_heading"></h5>
<h5 msgid="tutorial_jump_wrap_heading"></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. -->
<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() {
mockFeedback
.expectSpeech(
'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'))
.expectSpeech('Quick orientation', 'Link')
.call(doCmd('nextObject'))
......@@ -152,8 +153,8 @@ TEST_F('ChromeVoxTutorialTest', 'LessonSetTest', function() {
.call(doCmd('forceClickOnCurrentItem'))
.expectSpeech(/Quick Orientation Tutorial, [0-9]+ Lessons/)
.expectSpeech(
'Press Search + left/right arrow to browse lessons for ' +
'this topic')
' Press Search + Right Arrow, or Search + Left Arrow to browse ' +
'lessons for this topic ')
.call(doCmd('nextObject'))
.expectSpeech('Welcome to ChromeVox!')
.call(() => {
......@@ -192,7 +193,8 @@ TEST_F('ChromeVoxTutorialTest', 'NoPracticeAreaTest', function() {
})
.expectSpeech(
'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'))
.expectSpeech('Next lesson')
.replay();
......@@ -219,7 +221,7 @@ TEST_F('ChromeVoxTutorialTest', 'HasPracticeAreaTest', function() {
})
.expectSpeech('Jump Commands', 'Heading 1')
.call(doCmd('nextButton'))
.expectSpeech('Practice Area')
.expectSpeech('Practice area')
.replay();
});
});
......@@ -278,7 +280,7 @@ TEST_F('ChromeVoxTutorialTest', 'DISABLED_PracticeAreaNudgesTest', function() {
})
.expectSpeech('Basic Navigation', 'Heading 1')
.call(doCmd('nextButton'))
.expectSpeech('Practice Area')
.expectSpeech('Practice area')
.call(doCmd('forceClickOnCurrentItem'))
.expectSpeech(/Try using basic navigation to navigate/)
.call(giveNudge)
......@@ -454,7 +456,9 @@ TEST_F('ChromeVoxTutorialTest', 'LessonHint', function() {
tutorial.showLesson(0);
})
.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();
});
});
......@@ -539,7 +543,7 @@ TEST_F('ChromeVoxTutorialTest', 'QuickOrientationLessonTest', function() {
// the next lesson.
.call(simulateKeyPress.bind(this, KeyCode.SPACE, {}))
.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(() => {
assertEquals(1, tutorial.activeLessonNum);
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