Commit a41edff6 by asadiqbal

asadiqbal08/WL-347: Add I18n Translations Support

parent bd93e485
......@@ -5,7 +5,7 @@ before_install:
- "export DISPLAY=:99"
- "sh -e /etc/init.d/xvfb start"
install:
- "pip install -e git://github.com/edx/xblock-sdk.git#egg=xblock-sdk"
- "pip install -e git://github.com/edx/xblock-sdk.git@v0.1.2#egg=xblock-sdk==v0.1.2"
- "cd $VIRTUAL_ENV/src/xblock-sdk/"
- "pip install -r $VIRTUAL_ENV/src/xblock-sdk/requirements/base.txt"
- "pip install -r $VIRTUAL_ENV/src/xblock-sdk/requirements/test.txt"
......
......@@ -237,3 +237,43 @@ If you want to grant members of other groups ability to view the results, you ca
names in the django settings using the `XBLOCK_POLL_EXTRA_VIEW_GROUPS` setting, for example:
XBLOCK_POLL_EXTRA_VIEW_GROUPS = ['poll_staff']
## Working with Translations
The following sections provide basic information about common use cases for working with translations. You can find additional information in [this section](http://edx.readthedocs.io/projects/xblock-tutorial/en/latest/edx_platform/edx_lms.html#adding-translated-strings-to-your-xblock) of the [Open edX XBlock Tutorial](http://edx.readthedocs.io/projects/xblock-tutorial/en/latest/index.html).
### Viewing the XBlock in a Different Language
To view this XBlock in a different language you first need to compile the `text.po` file for the language to a file called `text.mo`. Translation files for a given language are located in the `poll/translations/<locale>/LC_MESSAGES` directory. From the top-level directory of this repository, use the following commands to access the directory and compile the translation file (replacing `<lang_code>` with the appropriate language code):
```sh
cd poll/translations/<lang_code>/LC_MESSAGES/
msgfmt text.po -o text.mo
```
You can then navigate to a poll or survey in the LMS and view a translated version of it by adding `/?preview-lang=<lang_code>` to the URL. Note that user-editable content (such as questions and answers belonging to a poll) is not translated by default. To revert to the default language, add `/?clear-lang` to the URL.
### Adding Translations for Other Languages
To add translations for other languages, create a set of language directories for each of your locales within the `poll/translations` directory. You may specify either a general language code or a specific language locale code for the name of each directory. Include an `LC_MESSAGES` directory with each language directory. For example:
* `../poll/translations/ar/LC_MESSAGES/`
* `../poll/translations/es-es/LC_MESSAGES/`
For each language, create a domain file named `text.po` and compile it: You can use edX's [i18n-tools](https://github.com/edx/i18n-tools) to extract translations for multiple languages as follows:
```sh
i18n_tool extract
```
Open each `text.po` domain file and, for each `msgid` string, add a corresponding `msgstr` translation. You can then use edX's [i18n-tools](https://github.com/edx/i18n-tools) to compile the translation files as follows:
```sh
i18n_tool generate
```
Before running the `i18n_tool extract` or `i18n_tool generate` commands, appropriate settings for xblock-poll need to be added to `edx-platform/conf/locale/config.yaml`.
You should have to mention `poll` as a third_party app and `poll.po` in `django.po` list under `generate_merge` section.
### Updating Existing Translation Files
After changing existing strings in the source code (or adding new ones), you'll need to update the translation files. To do this, extract and compile the translations as described in the previous section. (There is no need to create additional directories if you are not adding translations for additional languages.)
......@@ -27,10 +27,12 @@
{{/each}}
</ul>
<input class="input-main" type="button" name="poll-submit" value="Submit" disabled>
<div class="poll-footnote">Results gathered from {{total}} respondent{{#if plural}}s{{/if}}.</div>
<div class="poll-footnote">
{{interpolate (i18n_ngettext "Results gathered from {total} respondent." "Results gathered from {total} respondents." total) total=total}}
</div>
{{#if feedback}}
<hr />
<h3 class="poll-header">Feedback</h3>
<h3 class="poll-header">{{i18n "Feedback" }}</h3>
<div class="poll-feedback">
{{{feedback}}}
</div>
......
......@@ -3,26 +3,25 @@
<li class="field comp-setting-entry is-set poll-{{noun}}-studio-item">
<div class="wrapper-comp-setting">
<div class="poll-move">
<button class="poll-move-up">&#9650;<span class="sr">&nbsp;move poll up</span></button>
<button class="poll-move-down">&#9660;<span class="sr">&nbsp;move poll down</span></button>
<button class="poll-move-up">&#9650;<span class="sr">&nbsp;{{i18n "move poll up"}}</span></button>
<button class="poll-move-down">&#9660;<span class="sr">&nbsp;{{i18n "move poll down"}}</span></button>
</div>
<button class="button action-button poll-delete-answer">Delete</button>
<button class="button action-button poll-delete-answer">{{i18n "Delete" }}</button>
<label class="label setting-label poll-setting-label" for="{{noun}}-label-{{key}}">{{noun}}</label>
<input class="input setting-input" name="{{noun}}-label-{{key}}" id="{{noun}}-label-{{key}}" value="{{text}}" type="text" /><br />
{{#if image}}
<label class="label setting-label" for="{{noun}}-img-{{key}}">Image URL</label>
<label class="label setting-label" for="{{noun}}-img-{{key}}">{{i18n "Image URL"}}</label>
<input class="input setting-input" name="{{noun}}-img-{{key}}" id="{{noun}}-img-{{key}}" value="{{img}}" type="text" /><br />
<label class="label setting-label" for="{{noun}}-img_alt-{{key}}">Image alternative text</label>
<label class="label setting-label" for="{{noun}}-img_alt-{{key}}">{{i18n "Image alternative text"}}</label>
<input class="input setting-input" name="{{noun}}-img_alt-{{key}}" id="{{noun}}-img_alt-{{key}}" value="{{img_alt}}" type="text" /><br />
{{/if}}
</div>
<span class="tip setting-help">
You can make limited use of Markdown in answer texts, preferably only bold and italics.
{{i18n "You can make limited use of Markdown in answer texts, preferably only bold and italics."}}
</span>
<span class="tip setting-help">
{{#if image}}
This must have an image URL or text, and can have both. If you add an image, you must also provide an alternative text
that describes the image in a way that would allow someone to answer the poll if the image did not load.
{{i18n "This must have an image URL or text, and can have both. If you add an image, you must also provide an alternative text that describes the image in a way that would allow someone to answer the poll if the image did not load."}}
{{/if}}
</span>
</li>
......
......@@ -26,10 +26,13 @@
{{/each}}
</table>
<input class="input-main" type="button" name="poll-submit" value="Submit" disabled>
<div class="poll-footnote">Results gathered from {{total}} respondent{{#if plural}}s{{/if}}.</div>
<div class="poll-footnote">
{{interpolate (i18n_ngettext "Results gathered from {total} respondent." "Results gathered from {total} respondents." total) total=total}}
</div>
{{#if feedback}}
<hr />
<h3 class="poll-header">Feedback</h3>
<h3 class="poll-header">{{i18n "Feedback" }}</h3>
<div class="poll-feedback">
{{{feedback}}}
</div>
......
......@@ -10,7 +10,35 @@ function PollUtil (runtime, element, pollType) {
this.votedUrl = runtime.handlerUrl(element, 'student_voted');
this.submit = $('input[type=button]', element);
this.answers = $('input[type=radio]', element);
// Set up gettext in case it isn't available in the client runtime:
if (typeof gettext == "undefined") {
window.gettext = function gettext_stub(string) { return string; };
window.ngettext = function ngettext_stub(strA, strB, n) { return n == 1 ? strA : strB; };
}
// Make gettext available in Handlebars templates
Handlebars.registerHelper('i18n', function(str) {
return gettext(str);
});
// Make ngettext available in Handlebars templates
Handlebars.registerHelper('i18n_ngettext', function(singular, plural, count) {
return ngettext(singular, plural, count);
});
// Add helper for interpolating values into strings in Handlebars templates
Handlebars.registerHelper('interpolate', function(formatString, parameters) {
parameters = parameters.hash;
return formatString.replace(/{\w+}/g,
function(parameter) {
var parameterName = parameter.slice(1, -1);
return String(parameters[parameterName]);
});
});
this.resultsTemplate = Handlebars.compile($("#" + pollType + "-results-template", element).html());
this.viewResultsButton = $('.view-results-button', element);
this.viewResultsButton.click(this.getResults);
......@@ -131,7 +159,7 @@ function PollUtil (runtime, element, pollType) {
thanks.fadeOut(0).fadeIn('slow', 'swing');
$('.poll-feedback-container', element).removeClass('poll-hidden');
if (can_vote) {
$('input[name="poll-submit"]', element).val('Resubmit');
$('input[name="poll-submit"]', element).val(gettext('Resubmit'));
} else {
$('input', element).attr('disabled', true)
}
......
......@@ -13,6 +13,31 @@ function PollEditUtil(runtime, element, pollType) {
this.init = function () {
// Set up the editing form for a Poll or Survey.
var temp = $('#poll-form-component', element).html();
// Set up gettext in case it isn't available in the client runtime:
if (typeof gettext == "undefined") {
window.gettext = function gettext_stub(string) { return string; };
window.ngettext = function ngettext_stub(strA, strB, n) { return n == 1 ? strA : strB; };
}
// Make gettext available in Handlebars templates
Handlebars.registerHelper('i18n', function(str) { return gettext(str); });
// Make ngettext available in Handlebars templates
Handlebars.registerHelper('i18n_ngettext', function(singular, plural, count) {
return ngettext(singular, plural, count);
});
// Add helper for interpolating values into strings in Handlebars templates
Handlebars.registerHelper('interpolate', function(formatString, parameters) {
parameters = parameters.hash;
return formatString.replace(/{\w+}/g,
function(parameter) {
var parameterName = parameter.slice(1, -1);
return String(parameters[parameterName]);
});
});
self.answerTemplate = Handlebars.compile(temp);
$(element).find('.cancel-button', element).bind('click', function() {
......@@ -231,7 +256,7 @@ function PollEditUtil(runtime, element, pollType) {
data['private_results'] = eval($('#poll-private-results', element).val());
if (notify) {
runtime.notify('save', {state: 'start', message: "Saving"});
runtime.notify('save', {state: 'start', message: gettext("Saving")});
}
$.ajax({
type: "POST",
......
This source diff could not be displayed because it is too large. You can view the blob instead.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"POT-Creation-Date: 2016-04-07 16:17+0500\n"
"PO-Revision-Date: 2016-04-07 16:17+0500\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.8.7\n"
"X-Poedit-Basepath: ../../..\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-SearchPath-0: poll.py\n"
#: poll.py
msgid "Whether or not to display results to the user."
msgstr ""
#: poll.py
msgid "The maximum number of times a user may send a submission."
msgstr ""
#: poll.py
msgid "Number of times the user has sent a submission."
msgstr ""
#: poll.py
msgid "Text to display after the user votes."
msgstr ""
#: poll.py
#, python-brace-format
msgid "'{field}' is not present, or not a JSON array."
msgstr ""
#: poll.py
#, python-brace-format
msgid "{noun} {item} not a javascript object!"
msgstr ""
#: poll.py
#, python-brace-format
msgid "{noun} {item} contains no key."
msgstr ""
#: poll.py
#, python-brace-format
msgid ""
"{noun} has no text or img. Please make sure all {noun_lower}s have one or the other, or "
"both."
msgstr ""
#: poll.py
#, python-brace-format
msgid ""
"{noun} was added with no label. All {noun_lower}s must have labels. Please check the "
"form. Check the form and explicitly delete {noun_lower}s if not needed."
msgstr ""
#: poll.py
msgid ""
"All images must have an alternative text describing the image in a way that "
"would allow someone to answer the poll if the image did not load."
msgstr ""
#: poll.py
#, python-brace-format
msgid "You must include at least one {noun_lower}."
msgstr ""
#: poll.py
msgid "Maximum Submissions missing or not an integer."
msgstr ""
#: poll.py
msgid "Private results may not be False when Maximum Submissions is not 1."
msgstr ""
#: poll.py
msgid "Total tally of answers from students."
msgstr ""
#: poll.py
msgid "The student's answer"
msgstr ""
#: poll.py
msgid "You have already voted in this poll."
msgstr ""
#: poll.py
msgid "Answer not included with request."
msgstr ""
#: poll.py
#, python-brace-format
msgid "No key \"{choice}\" in answers table."
msgstr ""
#: poll.py
msgid "You have already voted as many times as you are allowed."
msgstr ""
#: poll.py
msgid "You must specify a question."
msgstr ""
#: poll.py
msgid "Answer choices for this Survey"
msgstr ""
#: poll.py
msgid "Are you enjoying the course?"
msgstr ""
#: poll.py
msgid "Would you recommend this course to your friends?"
msgstr ""
#: poll.py
msgid "Do you think you will learn a lot?"
msgstr ""
#: poll.py
msgid "Questions for this Survey"
msgstr ""
#: poll.py
msgid "Survey"
msgstr ""
#: poll.py
msgid "The user's answers"
msgstr ""
#: poll.py
msgid ""
"Not all questions were included, or unknown questions were included. Try "
"refreshing and trying again."
msgstr ""
#: poll.py
#, python-format
msgid "Found unknown answer '{answer_key}' for question key '{question_key}'"
msgstr ""
#: poll.py
msgid "Poll"
msgstr ""
#: poll.py
msgid "What is your favorite color?"
msgstr ""
#: poll.py
msgid "Red"
msgstr ""
#: poll.py
msgid "Blue"
msgstr ""
#: poll.py
msgid "Other"
msgstr ""
#: poll.py
msgid "The answer options on this poll."
msgstr ""
#: poll.py
msgid "Answer"
msgstr ""
#: poll.py
msgid "Question"
msgstr ""
#: poll.py
msgid "Yes"
msgstr ""
#: poll.py
msgid "No"
msgstr ""
#: poll.py
msgid "Maybe"
msgstr ""
#: public/handlebars/poll_results.handlebars
msgid "Results gathered from {total} respondents."
msgstr ""
#: public/handlebars/poll_results.handlebars
msgid "Results gathered from {total} respondent."
msgstr ""
#: public/handlebars/poll_studio.handlebars
msgid "move poll up"
msgstr ""
#: public/handlebars/poll_studio.handlebars
msgid "move poll down"
msgstr ""
#: public/handlebars/poll_studio.handlebars
msgid "Delete"
msgstr ""
#: public/handlebars/poll_studio.handlebars
msgid "Image URL"
msgstr ""
#: public/handlebars/poll_studio.handlebars
msgid "Image alternative text"
msgstr ""
#: public/handlebars/poll_studio.handlebars
msgid ""
"You can make limited use of Markdown in answer texts, preferably only bold "
"and italics."
msgstr ""
#: public/handlebars/poll_studio.handlebars
msgid ""
"This must have an image URL or text, and can have both. If you add an "
"image, you must also provide an alternative text that describes the image "
"in a way that would allow someone to answer the poll if the image did not "
"load."
msgstr ""
# -*- coding: utf-8 -*-
#
# Make '_' a no-op so we can scrape strings
def _(text):
return text
......@@ -44,7 +44,7 @@ def package_data(pkg, roots):
setup(
name='xblock-poll',
version='1.0',
version='1.1',
description='An XBlock for polling users.',
packages=[
'poll',
......@@ -61,5 +61,5 @@ setup(
'survey = poll:SurveyBlock',
]
},
package_data=package_data("poll", ["static", "public"]),
package_data=package_data("poll", ["static", "public", "translations"]),
)
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