mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-08-11 22:10:00 -04:00
Merge pull request #2289 from ericrosenbaum/bugfix/localize-tts-languages
Localize the Text to Speech extension "set language" menu
This commit is contained in:
commit
24116e5514
4 changed files with 2507 additions and 2447 deletions
4876
package-lock.json
generated
4876
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -44,7 +44,7 @@
|
|||
"nets": "3.2.0",
|
||||
"scratch-parser": "5.0.0",
|
||||
"scratch-sb1-converter": "0.2.7",
|
||||
"scratch-translate-extension-languages": "0.0.20190416132834",
|
||||
"scratch-translate-extension-languages": "0.0.20191118205314",
|
||||
"socket.io-client": "2.0.4",
|
||||
"text-encoding": "0.7.0",
|
||||
"worker-loader": "^1.1.1"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const formatMessage = require('format-message');
|
||||
const nets = require('nets');
|
||||
const languageNames = require('scratch-translate-extension-languages');
|
||||
|
||||
const ArgumentType = require('../../extension-support/argument-type');
|
||||
const BlockType = require('../../extension-support/block-type');
|
||||
|
@ -267,7 +268,7 @@ class Scratch3Text2SpeechBlocks {
|
|||
},
|
||||
[JAPANESE_ID]: {
|
||||
name: 'Japanese',
|
||||
locales: ['ja', 'ja-Hira'],
|
||||
locales: ['ja', 'ja-hira'],
|
||||
speechSynthLocale: 'ja-JP'
|
||||
},
|
||||
[KOREAN_ID]: {
|
||||
|
@ -487,8 +488,9 @@ class Scratch3Text2SpeechBlocks {
|
|||
* @return {string} a Scratch locale code.
|
||||
*/
|
||||
getEditorLanguage () {
|
||||
return formatMessage.setup().locale ||
|
||||
const locale = formatMessage.setup().locale ||
|
||||
navigator.language || navigator.userLanguage || this.DEFAULT_LANGUAGE;
|
||||
return locale.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -518,6 +520,15 @@ class Scratch3Text2SpeechBlocks {
|
|||
stage.textToSpeechLanguage = this._getExtensionLocaleForSupportedLocale(locale);
|
||||
}
|
||||
|
||||
// Support language names dropped onto the menu via reporter block
|
||||
// such as a variable containing a language name (in any language),
|
||||
// or the translate extension's language reporter.
|
||||
const localeForDroppedName = languageNames.nameMap[locale.toLowerCase()];
|
||||
if (localeForDroppedName && this.isSupportedLanguage(localeForDroppedName)) {
|
||||
stage.textToSpeechLanguage =
|
||||
this._getExtensionLocaleForSupportedLocale(localeForDroppedName);
|
||||
}
|
||||
|
||||
// If the language is null, set it to the default language.
|
||||
// This can occur e.g. if the extension was loaded with the editor
|
||||
// set to a language that is not in the list.
|
||||
|
@ -584,14 +595,49 @@ class Scratch3Text2SpeechBlocks {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the menu of languages for the "set language" block.
|
||||
* Get the localized menu of languages for the "set language" block.
|
||||
* For each language:
|
||||
* if there is a custom translated spoken language name, use that;
|
||||
* otherwise use the translation in the languageNames menuMap;
|
||||
* otherwise fall back to the untranslated name in LANGUAGE_INFO.
|
||||
* @return {array} the text and value for each menu item.
|
||||
*/
|
||||
getLanguageMenu () {
|
||||
return Object.keys(this.LANGUAGE_INFO).map(key => ({
|
||||
text: this.LANGUAGE_INFO[key].name,
|
||||
value: key
|
||||
}));
|
||||
const editorLanguage = this.getEditorLanguage();
|
||||
// Get the array of localized language names
|
||||
const localizedNameMap = {};
|
||||
let nameArray = languageNames.menuMap[editorLanguage];
|
||||
if (nameArray) {
|
||||
// Also get any localized names of spoken languages
|
||||
let spokenNameArray = [];
|
||||
if (languageNames.spokenLanguages) {
|
||||
spokenNameArray = languageNames.spokenLanguages[editorLanguage];
|
||||
nameArray = nameArray.concat(spokenNameArray);
|
||||
}
|
||||
// Create a map of language code to localized name
|
||||
// The localized spoken language names have been concatenated onto
|
||||
// the end of the name array, so the result of the forEach below is
|
||||
// when there is both a written language name (e.g. 'Chinese
|
||||
// (simplified)') and a spoken language name (e.g. 'Chinese
|
||||
// (Mandarin)', we always use the spoken version.
|
||||
nameArray.forEach(lang => {
|
||||
localizedNameMap[lang.code] = lang.name;
|
||||
});
|
||||
}
|
||||
|
||||
return Object.keys(this.LANGUAGE_INFO).map(key => {
|
||||
let name = this.LANGUAGE_INFO[key].name;
|
||||
const localizedName = localizedNameMap[key];
|
||||
if (localizedName) {
|
||||
name = localizedName;
|
||||
}
|
||||
// Uppercase the first character of the name
|
||||
name = name.charAt(0).toUpperCase() + name.slice(1);
|
||||
return {
|
||||
text: name,
|
||||
value: key
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,8 +23,22 @@ test('if an unsupported language is dropped onto the set language block, use def
|
|||
t.end();
|
||||
});
|
||||
|
||||
test('if a supported language name is dropped onto the set language block, use it', t => {
|
||||
ext.setLanguage({LANGUAGE: 'español'});
|
||||
t.strictEqual(ext.getCurrentLanguage(), 'es');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('get the extension locale for a supported locale that differs', t => {
|
||||
ext.setLanguage({LANGUAGE: 'ja-Hira'});
|
||||
ext.setLanguage({LANGUAGE: 'ja-hira'});
|
||||
t.strictEqual(ext.getCurrentLanguage(), 'ja');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('use localized spoken language name in place of localized written language name', t => {
|
||||
ext.getEditorLanguage = () => 'es';
|
||||
const languageMenu = ext.getLanguageMenu();
|
||||
const localizedNameForChineseInSpanish = languageMenu.find(el => el.value === 'zh-cn').text;
|
||||
t.strictEqual(localizedNameForChineseInSpanish, 'Chino (Mandarín)'); // i.e. should not be 'Chino (simplificado)'
|
||||
t.end();
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue