Merge branch 'MDL-76331-master' of https://github.com/andrewnicols/moodle
This commit is contained in:
@@ -183,7 +183,7 @@ $string['tiny:horizontal_align'] = 'Horizontal align';
|
||||
$string['tiny:horizontal_line'] = 'Horizontal line';
|
||||
$string['tiny:horizontal_space'] = 'Horizontal space';
|
||||
$string['tiny:id'] = 'ID';
|
||||
$string['tiny:id_should_start_with_a_letter_followed_only_by_letters_numbers_dashes_dots_colons_or_underscores.'] = 'ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.';
|
||||
$string['tiny:id_should_start_with_a_letter_followed_only_by_letters_numbers_dashes_dots_colons_or_'] = 'ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.';
|
||||
$string['tiny:image_is_decorative'] = 'Image is decorative';
|
||||
$string['tiny:image_list'] = 'Image list';
|
||||
$string['tiny:image_title'] = 'Image title';
|
||||
@@ -274,7 +274,7 @@ $string['tiny:paste'] = 'Paste';
|
||||
$string['tiny:paste_as_text'] = 'Paste as text';
|
||||
$string['tiny:paste_column_after'] = 'Paste column after';
|
||||
$string['tiny:paste_column_before'] = 'Paste column before';
|
||||
$string['tiny:paste_is_now_in_plain_text_mode._contents_will_now_be_pasted_as_plain_text_until_you_toggle_this_option_off.'] = 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.';
|
||||
$string['tiny:paste_is_now_in_plain_text_mode._contents_will_now_be_pasted_as_plain_text_until_you_'] = 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.';
|
||||
$string['tiny:paste_or_type_a_link'] = 'Paste or type a link';
|
||||
$string['tiny:paste_row_after'] = 'Paste row after';
|
||||
$string['tiny:paste_row_before'] = 'Paste row before';
|
||||
@@ -308,7 +308,7 @@ $string['tiny:resize'] = 'Resize';
|
||||
$string['tiny:restore_last_draft'] = 'Restore last draft';
|
||||
$string['tiny:rich_text_area'] = 'Rich Text Area';
|
||||
$string['tiny:rich_text_area._press_alt-0_for_help.'] = 'Rich Text Area. Press ALT-0 for help.';
|
||||
$string['tiny:rich_text_area._press_alt-f9_for_menu._press_alt-f10_for_toolbar._press_alt-0_for_help'] = 'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help';
|
||||
$string['tiny:rich_text_area._press_alt-f9_for_menu._press_alt-f10_for_toolbar._press_alt-0_for_hel'] = 'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help';
|
||||
$string['tiny:ridge'] = 'Ridge';
|
||||
$string['tiny:right'] = 'Right';
|
||||
$string['tiny:right_to_left'] = 'Right to left';
|
||||
@@ -355,9 +355,9 @@ $string['tiny:templates'] = 'Templates';
|
||||
$string['tiny:text'] = 'Text';
|
||||
$string['tiny:text_color'] = 'Text color';
|
||||
$string['tiny:text_to_display'] = 'Text to display';
|
||||
$string['tiny:the_url_you_entered_seems_to_be_an_email_address._do_you_want_to_add_the_required_mailto_prefix'] = 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?';
|
||||
$string['tiny:the_url_you_entered_seems_to_be_an_external_link._do_you_want_to_add_the_required_http_prefix'] = 'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?';
|
||||
$string['tiny:the_url_you_entered_seems_to_be_an_external_link._do_you_want_to_add_the_required_https_prefix'] = 'The URL you entered seems to be an external link. Do you want to add the required https:// prefix?';
|
||||
$string['tiny:the_url_you_entered_seems_to_be_an_email_address._do_you_want_to_add_the_required_mai'] = 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?';
|
||||
$string['tiny:the_url_you_entered_seems_to_be_an_external_link._do_you_want_to_add_the_required_htt'] = 'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?';
|
||||
$string['tiny:the_url_you_entered_seems_to_be_an_external_link._do_you_want_to_add_the_required_ht1'] = 'The URL you entered seems to be an external link. Do you want to add the required https:// prefix?';
|
||||
$string['tiny:title'] = 'Title';
|
||||
$string['tiny:to_open_the_popup_press_shiftenter'] = 'To open the popup, press Shift+Enter';
|
||||
$string['tiny:tools'] = 'Tools';
|
||||
@@ -388,7 +388,7 @@ $string['tiny:yellow'] = 'Yellow';
|
||||
$string['tiny:yes'] = 'Yes';
|
||||
$string['tiny:you_are_using_0'] = 'You are using {0}';
|
||||
$string['tiny:you_have_unsaved_changes_are_you_sure_you_want_to_navigate_away'] = 'You have unsaved changes are you sure you want to navigate away?';
|
||||
$string['tiny:your_browser_doesnt_support_direct_access_to_the_clipboard._please_use_the_ctrlxcv_keyboard_shortcuts_instead.'] = 'Your browser doesn\'t support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.';
|
||||
$string['tiny:your_browser_doesnt_support_direct_access_to_the_clipboard._please_use_the_ctrlxcv_ke'] = 'Your browser doesn\'t support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.';
|
||||
$string['tiny:alignment1'] = 'alignment';
|
||||
$string['tiny:austral_sign'] = 'austral sign';
|
||||
$string['tiny:cedi_sign'] = 'cedi sign';
|
||||
|
||||
@@ -43,23 +43,23 @@
|
||||
|
||||
## Update procedure for included TinyMCE translations
|
||||
|
||||
1. Visit https://www.tiny.cloud/get-tiny/language-packages/ and download a translation which has been fully translated, for example the German translation.
|
||||
2. If you did not download the German translation, update the final line of `tools/getOriginals.mjs` to the language code for the relevant translation.
|
||||
1. Visit https://www.tiny.cloud/get-tiny/language-packages/ and download the "TinyMCE 6 All languages" zip file.
|
||||
2. Check the list of languages and confirm that the German translation is still at 100%. If not, then make a note of a language which is.
|
||||
3. Unzip the translation into a new directory:
|
||||
|
||||
```
|
||||
```bash
|
||||
langdir=`mktemp -d`
|
||||
cd "${langdir}"
|
||||
unzip path/to/de.zip
|
||||
unzip path/to/langs.zip
|
||||
```
|
||||
|
||||
4. Run the translation tool:
|
||||
|
||||
```
|
||||
node "${MOODLEDIR}/tools/getOriginals.mjs"
|
||||
```bash
|
||||
node "${MOODLEDIR}/tools/createLangStrings.mjs"
|
||||
```
|
||||
|
||||
This will generate two files
|
||||
This will generate a language file for each available Language, as well as a `tinystrings.json`, and a `strings.php` which will be used in the subsequent steps.
|
||||
|
||||
5. Copy the `tinystrings.json` file into the Moodle directory
|
||||
|
||||
@@ -75,9 +75,12 @@
|
||||
```
|
||||
|
||||
7. Commit changes
|
||||
8. If required, the remaining language strings can be fed into AMOS.
|
||||
|
||||
---
|
||||
|
||||
**Note:** A set of language files are also generated for all supported translations and may be submitted to AMOS if desired.
|
||||
|
||||
**Note:** You will need to manually check for any Moodle-updated language strings as part of this change (for example any from the en_fixes).
|
||||
|
||||
---
|
||||
|
||||
@@ -154,7 +154,7 @@
|
||||
"tiny:horizontal_line": "Horizontal line",
|
||||
"tiny:horizontal_space": "Horizontal space",
|
||||
"tiny:id": "ID",
|
||||
"tiny:id_should_start_with_a_letter_followed_only_by_letters_numbers_dashes_dots_colons_or_underscores.": "ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.",
|
||||
"tiny:id_should_start_with_a_letter_followed_only_by_letters_numbers_dashes_dots_colons_or_": "ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.",
|
||||
"tiny:image_is_decorative": "Image is decorative",
|
||||
"tiny:image_list": "Image list",
|
||||
"tiny:image_title": "Image title",
|
||||
@@ -245,7 +245,7 @@
|
||||
"tiny:paste_as_text": "Paste as text",
|
||||
"tiny:paste_column_after": "Paste column after",
|
||||
"tiny:paste_column_before": "Paste column before",
|
||||
"tiny:paste_is_now_in_plain_text_mode._contents_will_now_be_pasted_as_plain_text_until_you_toggle_this_option_off.": "Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.",
|
||||
"tiny:paste_is_now_in_plain_text_mode._contents_will_now_be_pasted_as_plain_text_until_you_": "Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.",
|
||||
"tiny:paste_or_type_a_link": "Paste or type a link",
|
||||
"tiny:paste_row_after": "Paste row after",
|
||||
"tiny:paste_row_before": "Paste row before",
|
||||
@@ -279,7 +279,7 @@
|
||||
"tiny:restore_last_draft": "Restore last draft",
|
||||
"tiny:rich_text_area": "Rich Text Area",
|
||||
"tiny:rich_text_area._press_alt-0_for_help.": "Rich Text Area. Press ALT-0 for help.",
|
||||
"tiny:rich_text_area._press_alt-f9_for_menu._press_alt-f10_for_toolbar._press_alt-0_for_help": "Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help",
|
||||
"tiny:rich_text_area._press_alt-f9_for_menu._press_alt-f10_for_toolbar._press_alt-0_for_hel": "Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help",
|
||||
"tiny:ridge": "Ridge",
|
||||
"tiny:right": "Right",
|
||||
"tiny:right_to_left": "Right to left",
|
||||
@@ -326,9 +326,9 @@
|
||||
"tiny:text": "Text",
|
||||
"tiny:text_color": "Text color",
|
||||
"tiny:text_to_display": "Text to display",
|
||||
"tiny:the_url_you_entered_seems_to_be_an_email_address._do_you_want_to_add_the_required_mailto_prefix": "The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?",
|
||||
"tiny:the_url_you_entered_seems_to_be_an_external_link._do_you_want_to_add_the_required_http_prefix": "The URL you entered seems to be an external link. Do you want to add the required http:// prefix?",
|
||||
"tiny:the_url_you_entered_seems_to_be_an_external_link._do_you_want_to_add_the_required_https_prefix": "The URL you entered seems to be an external link. Do you want to add the required https:// prefix?",
|
||||
"tiny:the_url_you_entered_seems_to_be_an_email_address._do_you_want_to_add_the_required_mai": "The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?",
|
||||
"tiny:the_url_you_entered_seems_to_be_an_external_link._do_you_want_to_add_the_required_htt": "The URL you entered seems to be an external link. Do you want to add the required http:// prefix?",
|
||||
"tiny:the_url_you_entered_seems_to_be_an_external_link._do_you_want_to_add_the_required_ht1": "The URL you entered seems to be an external link. Do you want to add the required https:// prefix?",
|
||||
"tiny:title": "Title",
|
||||
"tiny:to_open_the_popup_press_shiftenter": "To open the popup, press Shift+Enter",
|
||||
"tiny:tools": "Tools",
|
||||
@@ -359,7 +359,7 @@
|
||||
"tiny:yes": "Yes",
|
||||
"tiny:you_are_using_0": "You are using {0}",
|
||||
"tiny:you_have_unsaved_changes_are_you_sure_you_want_to_navigate_away": "You have unsaved changes are you sure you want to navigate away?",
|
||||
"tiny:your_browser_doesnt_support_direct_access_to_the_clipboard._please_use_the_ctrlxcv_keyboard_shortcuts_instead.": "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.",
|
||||
"tiny:your_browser_doesnt_support_direct_access_to_the_clipboard._please_use_the_ctrlxcv_ke": "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.",
|
||||
"tiny:alignment1": "alignment",
|
||||
"tiny:austral_sign": "austral sign",
|
||||
"tiny:cedi_sign": "cedi sign",
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import {readdir, readFile, writeFile, mkdir} from 'fs/promises';
|
||||
import {join as joinPath} from 'path';
|
||||
|
||||
const fullyTranslatedLanguage = 'de';
|
||||
const maxStringIdentifierLength = 90;
|
||||
|
||||
const readStringsFromLanguages = async (language) => {
|
||||
const fileContent = await readFile(`./langs/${language}.js`, 'utf-8');
|
||||
|
||||
let translations = {};
|
||||
const tinymce = {
|
||||
addI18n: (language, strings) => {
|
||||
translations = strings;
|
||||
},
|
||||
};
|
||||
|
||||
eval(fileContent);
|
||||
|
||||
return Object.keys(translations).sort().reduce((sortedTranslations, key) => {
|
||||
sortedTranslations[key] = translations[key];
|
||||
return sortedTranslations;
|
||||
}, {});
|
||||
};
|
||||
|
||||
const getStringMap = (strings) => {
|
||||
const stringMap = {};
|
||||
|
||||
const getUniqueKeyForString = (string, modifier = 0) => {
|
||||
let stringKey = string.toLowerCase()
|
||||
.replaceAll(' ', '_')
|
||||
.replaceAll(/\{(\d)\}/g, '$1')
|
||||
.replaceAll('#', 'hash')
|
||||
.replaceAll(/[^a-z0-9_\-\.]/g, '')
|
||||
;
|
||||
|
||||
if (stringKey === '') {
|
||||
throw new Error(`The calculated key for '${string}' was empty`);
|
||||
}
|
||||
|
||||
stringKey = `tiny:${stringKey}`;
|
||||
|
||||
if (stringKey.length > maxStringIdentifierLength) {
|
||||
const modifierLength = modifier === 0 ? 0 : `${modifier}`.length;
|
||||
stringKey = stringKey.slice(0, maxStringIdentifierLength - modifierLength);
|
||||
}
|
||||
|
||||
if (modifier > 0) {
|
||||
stringKey = `${stringKey}${modifier}`;
|
||||
}
|
||||
|
||||
if (typeof stringMap[stringKey] !== 'undefined') {
|
||||
return getUniqueKeyForString(string, ++modifier);
|
||||
}
|
||||
|
||||
return stringKey;
|
||||
};
|
||||
|
||||
strings.forEach((string) => {
|
||||
const stringKey = getUniqueKeyForString(string);
|
||||
if (typeof stringMap[stringKey] !== 'undefined') {
|
||||
throw new Error(`Found existing key ${stringKey}`);
|
||||
}
|
||||
|
||||
stringMap[stringKey] = string;
|
||||
});
|
||||
|
||||
return stringMap;
|
||||
};
|
||||
|
||||
const getPhpStrings = (stringMap, translatedStrings) => Object.entries(stringMap).map(([stringKey, englishString]) => {
|
||||
if (translatedStrings[englishString].length === 0) {
|
||||
return null;
|
||||
}
|
||||
return `$string['${stringKey}'] = '${translatedStrings[englishString].replaceAll("'", "\\\'")}';`
|
||||
})
|
||||
.filter((value) => value !== null)
|
||||
.join("\n");
|
||||
|
||||
const storeEnglishStrings = async(stringMap) => {
|
||||
const englishStrings = Object.entries(stringMap).map(([stringKey, stringValue]) => {
|
||||
return `$string['${stringKey}'] = '${stringValue.replace("'", "\\\'")}';`
|
||||
}).join("\n");
|
||||
await writeFile('./strings.php', englishStrings + "\n");
|
||||
await writeFile('./tinystrings.json', JSON.stringify(stringMap, null, ' '));
|
||||
}
|
||||
|
||||
const constructTranslationFile = async(language, englishStringMap = null) => {
|
||||
const strings = await readStringsFromLanguages(language);
|
||||
console.log(`Generating translation data for ${language} with ${Object.keys(strings).length} strings`);
|
||||
const stringMap = englishStringMap === null ? getStringMap(Object.keys(strings)) : englishStringMap;
|
||||
|
||||
const langDir = joinPath('lang', language);
|
||||
await mkdir(langDir, {recursive: true});
|
||||
|
||||
const fileContent = `<?php
|
||||
|
||||
${getPhpStrings(stringMap, strings)}
|
||||
`;
|
||||
|
||||
await writeFile(joinPath(langDir, `editor_tiny.php`), fileContent);
|
||||
|
||||
return {
|
||||
strings,
|
||||
stringMap,
|
||||
};
|
||||
};
|
||||
|
||||
const constructTranslationFiles = async() => {
|
||||
const {stringMap} = await constructTranslationFile(fullyTranslatedLanguage);
|
||||
storeEnglishStrings(stringMap);
|
||||
|
||||
readdir('./langs/').then((files) => {
|
||||
files.forEach(async(file) => {
|
||||
const langIdent = file.replace('.js', '');
|
||||
if (langIdent === fullyTranslatedLanguage) {
|
||||
// This language is already done.
|
||||
return;
|
||||
}
|
||||
await constructTranslationFile(langIdent, stringMap);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
constructTranslationFiles();
|
||||
@@ -1,86 +0,0 @@
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import {readFile, writeFile} from 'fs/promises';
|
||||
|
||||
const readStringsFromLanguages = async (language) => {
|
||||
const fileContent = await readFile(`./langs/${language}.js`, 'utf-8');
|
||||
|
||||
const translations = [];
|
||||
const tinymce = {
|
||||
addI18n: (language, strings) => {
|
||||
translations.push(...(Object.keys(strings)));
|
||||
},
|
||||
};
|
||||
|
||||
eval(fileContent);
|
||||
|
||||
return translations.sort();
|
||||
};
|
||||
|
||||
const getStringMap = (strings) => {
|
||||
const stringMap = {};
|
||||
|
||||
const getUniqueKeyForString = (string, modifier = 0) => {
|
||||
let stringKey = string.toLowerCase()
|
||||
.replaceAll(' ', '_')
|
||||
.replaceAll(/\{(\d)\}/g, '$1')
|
||||
.replaceAll('#', 'hash')
|
||||
.replaceAll(/[^a-z0-9_\-\.]/g, '')
|
||||
;
|
||||
|
||||
if (stringKey === '') {
|
||||
throw new Error(`The calculated key for '${string}' was empty`);
|
||||
}
|
||||
|
||||
stringKey = `tiny:${stringKey}`;
|
||||
|
||||
if (modifier > 0) {
|
||||
stringKey = `${stringKey}${modifier}`;
|
||||
}
|
||||
|
||||
if (typeof stringMap[stringKey] !== 'undefined') {
|
||||
return getUniqueKeyForString(string, ++modifier);
|
||||
}
|
||||
|
||||
return stringKey;
|
||||
};
|
||||
|
||||
strings.forEach((string) => {
|
||||
const stringKey = getUniqueKeyForString(string);
|
||||
if (typeof stringMap[stringKey] !== 'undefined') {
|
||||
throw new Error(`Found existing key ${stringKey}`);
|
||||
}
|
||||
|
||||
stringMap[stringKey] = string;
|
||||
});
|
||||
|
||||
return stringMap;
|
||||
};
|
||||
|
||||
const getPhpStrings = (stringMap) => Object.entries(stringMap).map(([stringKey, stringValue]) => {
|
||||
return `$string['${stringKey}'] = '${stringValue.replace("'", "\\\'")}';`
|
||||
}).join("\n");
|
||||
|
||||
|
||||
const constructTranslationFile = async(language) => {
|
||||
const strings = await readStringsFromLanguages(language);
|
||||
const stringMap = getStringMap(strings);
|
||||
|
||||
await writeFile('./strings.php', getPhpStrings(stringMap) + "\n");
|
||||
await writeFile('./tinystrings.json', JSON.stringify(stringMap, null, ' '));
|
||||
};
|
||||
|
||||
constructTranslationFile('de');
|
||||
Reference in New Issue
Block a user