Commit 98c2aa1e by Tim Krones

Merge pull request #10 from asadiqbal08/asadiqbal08/WL-347

WL-347 Translations Poll and Survey XBlock
parents bd93e485 a41edff6
...@@ -5,7 +5,7 @@ before_install: ...@@ -5,7 +5,7 @@ before_install:
- "export DISPLAY=:99" - "export DISPLAY=:99"
- "sh -e /etc/init.d/xvfb start" - "sh -e /etc/init.d/xvfb start"
install: 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/" - "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/base.txt"
- "pip install -r $VIRTUAL_ENV/src/xblock-sdk/requirements/test.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 ...@@ -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: names in the django settings using the `XBLOCK_POLL_EXTRA_VIEW_GROUPS` setting, for example:
XBLOCK_POLL_EXTRA_VIEW_GROUPS = ['poll_staff'] 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.)
...@@ -36,6 +36,7 @@ from xblock.fragment import Fragment ...@@ -36,6 +36,7 @@ from xblock.fragment import Fragment
from xblockutils.publish_event import PublishEventMixin from xblockutils.publish_event import PublishEventMixin
from xblockutils.resources import ResourceLoader from xblockutils.resources import ResourceLoader
from xblockutils.settings import XBlockWithSettingsMixin, ThemableXBlockMixin from xblockutils.settings import XBlockWithSettingsMixin, ThemableXBlockMixin
from .utils import _
try: try:
# pylint: disable=import-error # pylint: disable=import-error
...@@ -83,17 +84,18 @@ class ResourceMixin(XBlockWithSettingsMixin, ThemableXBlockMixin): ...@@ -83,17 +84,18 @@ class ResourceMixin(XBlockWithSettingsMixin, ThemableXBlockMixin):
@XBlock.wants('settings') @XBlock.wants('settings')
@XBlock.needs('i18n')
class PollBase(XBlock, ResourceMixin, PublishEventMixin): class PollBase(XBlock, ResourceMixin, PublishEventMixin):
""" """
Base class for Poll-like XBlocks. Base class for Poll-like XBlocks.
""" """
event_namespace = 'xblock.pollbase' event_namespace = 'xblock.pollbase'
private_results = Boolean(default=False, help="Whether or not to display results to the user.") private_results = Boolean(default=False, help=_("Whether or not to display results to the user."))
max_submissions = Integer(default=1, help="The maximum number of times a user may send a submission.") max_submissions = Integer(default=1, help=_("The maximum number of times a user may send a submission."))
submissions_count = Integer( submissions_count = Integer(
default=0, help="Number of times the user has sent a submission.", scope=Scope.user_state default=0, help=_("Number of times the user has sent a submission."), scope=Scope.user_state
) )
feedback = String(default='', help="Text to display after the user votes.") feedback = String(default='', help=_("Text to display after the user votes."))
def send_vote_event(self, choice_data): def send_vote_event(self, choice_data):
# Let the LMS know the user has answered the poll. # Let the LMS know the user has answered the poll.
...@@ -139,8 +141,11 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin): ...@@ -139,8 +141,11 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin):
if field not in data or not isinstance(data[field], list): if field not in data or not isinstance(data[field], list):
source_items = [] source_items = []
result['success'] = False result['success'] = False
result['errors'].append( error_message = self.ugettext(
"'{0}' is not present, or not a JSON array.".format(field)) # Translators: {field} is either "answers" or "questions".
"'{field}' is not present, or not a JSON array."
).format(field=field)
result['errors'].append(error_message)
else: else:
source_items = data[field] source_items = data[field]
...@@ -148,45 +153,63 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin): ...@@ -148,45 +153,63 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin):
for item in source_items: for item in source_items:
if not isinstance(item, dict): if not isinstance(item, dict):
result['success'] = False result['success'] = False
result['errors'].append( error_message = self.ugettext(
"{0} {1} not a javascript object!".format(noun, item)) # Translators: {noun} is either "Answer" or "Question". {item} identifies the answer or question.
"{noun} {item} not a javascript object!"
).format(noun=noun, item=item)
result['errors'].append(error_message)
continue continue
key = item.get('key', '').strip() key = item.get('key', '').strip()
if not key: if not key:
result['success'] = False result['success'] = False
result['errors'].append( error_message = self.ugettext(
"{0} {1} contains no key.".format(noun, item)) # Translators: {noun} is either "Answer" or "Question". {item} identifies the answer or question.
"{noun} {item} contains no key."
).format(noun=noun, item=item)
result['errors'].append(error_message)
image_link = item.get('img', '').strip() image_link = item.get('img', '').strip()
image_alt = item.get('img_alt', '').strip() image_alt = item.get('img_alt', '').strip()
label = item.get('label', '').strip() label = item.get('label', '').strip()
if not label: if not label:
if image and not image_link: if image and not image_link:
result['success'] = False result['success'] = False
result['errors'].append( error_message = self.ugettext(
"{0} has no text or img. Please make sure all {1}s " # Translators: {noun} is either "Answer" or "Question".
"have one or the other, or both.".format(noun, noun.lower())) # {noun_lower} is the lowercase version of {noun}.
"{noun} has no text or img. Please make sure all {noun_lower}s have one or the other, or both."
).format(noun=noun, noun_lower=noun.lower())
result['errors'].append(error_message)
elif not image: elif not image:
result['success'] = False result['success'] = False
# If there's a bug in the code or the user just forgot to relabel a question, # If there's a bug in the code or the user just forgot to relabel a question,
# votes could be accidentally lost if we assume the omission was an # votes could be accidentally lost if we assume the omission was an
# intended deletion. # intended deletion.
result['errors'].append("{0} was added with no label. " error_message = self.ugettext(
"All {1}s must have labels. Please check the form. " # Translators: {noun} is either "Answer" or "Question".
"Check the form and explicitly delete {1}s " # {noun_lower} is the lowercase version of {noun}.
"if not needed.".format(noun, noun.lower())) "{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."
).format(noun=noun, noun_lower=noun.lower())
result['errors'].append(error_message)
if image_link and not image_alt and self.img_alt_mandatory(): if image_link and not image_alt and self.img_alt_mandatory():
result['success'] = False result['success'] = False
result['errors'].append( result['errors'].append(
"All images must have an alternative text describing the image in a way that " self.ugettext(
"would allow someone to answer the poll if the image did not load.") "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."
)
)
if image: if image:
items.append((key, {'label': label, 'img': image_link, 'img_alt': image_alt})) items.append((key, {'label': label, 'img': image_link, 'img_alt': image_alt}))
else: else:
items.append([key, label]) items.append([key, label])
if not items: if not items:
result['errors'].append( error_message = self.ugettext(
"You must include at least one {0}.".format(noun.lower())) # Translators: "{noun_lower} is either "answer" or "question".
"You must include at least one {noun_lower}."
).format(noun_lower=noun.lower())
result['errors'].append(error_message)
result['success'] = False result['success'] = False
return items return items
...@@ -221,7 +244,7 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin): ...@@ -221,7 +244,7 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin):
return GroupProfile.objects.filter(group_id__in=group_ids, name__in=group_names).exists() return GroupProfile.objects.filter(group_id__in=group_ids, name__in=group_names).exists()
@staticmethod @staticmethod
def get_max_submissions(data, result, private_results): def get_max_submissions(ugettext, data, result, private_results):
""" """
Gets the value of 'max_submissions' from studio submitted AJAX data, and checks for conflicts Gets the value of 'max_submissions' from studio submitted AJAX data, and checks for conflicts
with private_results, which may not be False when max_submissions is not 1, since that would mean with private_results, which may not be False when max_submissions is not 1, since that would mean
...@@ -232,12 +255,12 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin): ...@@ -232,12 +255,12 @@ class PollBase(XBlock, ResourceMixin, PublishEventMixin):
except (ValueError, KeyError): except (ValueError, KeyError):
max_submissions = 1 max_submissions = 1
result['success'] = False result['success'] = False
result['errors'].append('Maximum Submissions missing or not an integer.') result['errors'].append(ugettext('Maximum Submissions missing or not an integer.'))
# Better to send an error than to confuse the user by thinking this would work. # Better to send an error than to confuse the user by thinking this would work.
if (max_submissions != 1) and not private_results: if (max_submissions != 1) and not private_results:
result['success'] = False result['success'] = False
result['errors'].append("Private results may not be False when Maximum Submissions is not 1.") result['errors'].append(ugettext("Private results may not be False when Maximum Submissions is not 1."))
return max_submissions return max_submissions
@classmethod @classmethod
...@@ -272,23 +295,23 @@ class PollBlock(PollBase): ...@@ -272,23 +295,23 @@ class PollBlock(PollBase):
""" """
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
display_name = String(default='Poll') display_name = String(default=_('Poll'))
question = String(default='What is your favorite color?') question = String(default=_('What is your favorite color?'))
# This will be converted into an OrderedDict. # This will be converted into an OrderedDict.
# Key, (Label, Image path) # Key, (Label, Image path)
answers = List( answers = List(
default=[ default=[
('R', {'label': 'Red', 'img': None, 'img_alt': None}), ('R', {'label': _('Red'), 'img': None, 'img_alt': None}),
('B', {'label': 'Blue', 'img': None, 'img_alt': None}), ('B', {'label': _('Blue'), 'img': None, 'img_alt': None}),
('G', {'label': 'Green', 'img': None, 'img_alt': None}), ('G', {'label': _('Green'), 'img': None, 'img_alt': None}),
('O', {'label': 'Other', 'img': None, 'img_alt': None}), ('O', {'label': _('Other'), 'img': None, 'img_alt': None}),
], ],
scope=Scope.settings, help="The answer options on this poll." scope=Scope.settings, help=_("The answer options on this poll.")
) )
tally = Dict(default={'R': 0, 'B': 0, 'G': 0, 'O': 0}, tally = Dict(default={'R': 0, 'B': 0, 'G': 0, 'O': 0},
scope=Scope.user_state_summary, scope=Scope.user_state_summary,
help="Total tally of answers from students.") help=_("Total tally of answers from students."))
choice = String(scope=Scope.user_state, help="The student's answer") choice = String(scope=Scope.user_state, help=_("The student's answer"))
event_namespace = 'xblock.poll' event_namespace = 'xblock.poll'
def clean_tally(self): def clean_tally(self):
...@@ -452,18 +475,22 @@ class PollBlock(PollBase): ...@@ -452,18 +475,22 @@ class PollBlock(PollBase):
result = {'success': False, 'errors': []} result = {'success': False, 'errors': []}
old_choice = self.get_choice() old_choice = self.get_choice()
if (old_choice is not None) and not self.private_results: if (old_choice is not None) and not self.private_results:
result['errors'].append('You have already voted in this poll.') result['errors'].append(self.ugettext('You have already voted in this poll.'))
return result return result
try: try:
choice = data['choice'] choice = data['choice']
except KeyError: except KeyError:
result['errors'].append('Answer not included with request.') result['errors'].append(self.ugettext('Answer not included with request.'))
return result return result
# Just to show data coming in... # Just to show data coming in...
try: try:
OrderedDict(self.answers)[choice] OrderedDict(self.answers)[choice]
except KeyError: except KeyError:
result['errors'].append('No key "{choice}" in answers table.'.format(choice=choice)) result['errors'].append(
self.ugettext(
# Translators: {choice} uniquely identifies a specific answer belonging to a poll or survey.
'No key "{choice}" in answers table.'
).format(choice=choice))
return result return result
if old_choice is None: if old_choice is None:
...@@ -471,7 +498,7 @@ class PollBlock(PollBase): ...@@ -471,7 +498,7 @@ class PollBlock(PollBase):
self.submissions_count = 0 self.submissions_count = 0
if not self.can_vote(): if not self.can_vote():
result['errors'].append('You have already voted as many times as you are allowed.') result['errors'].append(self.ugettext('You have already voted as many times as you are allowed.'))
return result return result
self.clean_tally() self.clean_tally()
...@@ -497,14 +524,14 @@ class PollBlock(PollBase): ...@@ -497,14 +524,14 @@ class PollBlock(PollBase):
feedback = data.get('feedback', '').strip() feedback = data.get('feedback', '').strip()
private_results = bool(data.get('private_results', False)) private_results = bool(data.get('private_results', False))
max_submissions = self.get_max_submissions(data, result, private_results) max_submissions = self.get_max_submissions(self.ugettext, data, result, private_results)
display_name = data.get('display_name', '').strip() display_name = data.get('display_name', '').strip()
if not question: if not question:
result['errors'].append("You must specify a question.") result['errors'].append(self.ugettext("You must specify a question."))
result['success'] = False result['success'] = False
answers = self.gather_items(data, result, 'Answer', 'answers') answers = self.gather_items(data, result, self.ugettext('Answer'), 'answers')
if not result['success']: if not result['success']:
return result return result
...@@ -554,32 +581,36 @@ class PollBlock(PollBase): ...@@ -554,32 +581,36 @@ class PollBlock(PollBase):
class SurveyBlock(PollBase): class SurveyBlock(PollBase):
# pylint: disable=too-many-instance-attributes # pylint: disable=too-many-instance-attributes
display_name = String(default='Survey') display_name = String(default=_('Survey'))
# The display name affects how the block is labeled in the studio, # The display name affects how the block is labeled in the studio,
# but either way we want it to say 'Poll' by default on the page. # but either way we want it to say 'Poll' by default on the page.
block_name = String(default='Poll') block_name = String(default=_('Poll'))
answers = List( answers = List(
default=( default=(
('Y', 'Yes'), ('N', 'No'), ('Y', _('Yes')), ('N', _('No')),
('M', 'Maybe')), ('M', _('Maybe'))),
scope=Scope.settings, help="Answer choices for this Survey" scope=Scope.settings, help=_("Answer choices for this Survey")
) )
questions = List( questions = List(
default=[ default=[
('enjoy', {'label': 'Are you enjoying the course?', 'img': None, 'img_alt': None}), ('enjoy', {'label': _('Are you enjoying the course?'), 'img': None, 'img_alt': None}),
('recommend', {'label': 'Would you recommend this course to your friends?', 'img': None, 'img_alt': None}), ('recommend', {
('learn', {'label': 'Do you think you will learn a lot?', 'img': None, 'img_alt': None}), 'label': _('Would you recommend this course to your friends?'),
'img': None,
'img_alt': None
}),
('learn', {'label': _('Do you think you will learn a lot?'), 'img': None, 'img_alt': None}),
], ],
scope=Scope.settings, help="Questions for this Survey" scope=Scope.settings, help=_("Questions for this Survey")
) )
tally = Dict( tally = Dict(
default={ default={
'enjoy': {'Y': 0, 'N': 0, 'M': 0}, 'recommend': {'Y': 0, 'N': 0, 'M': 0}, 'enjoy': {'Y': 0, 'N': 0, 'M': 0}, 'recommend': {'Y': 0, 'N': 0, 'M': 0},
'learn': {'Y': 0, 'N': 0, 'M': 0}}, 'learn': {'Y': 0, 'N': 0, 'M': 0}},
scope=Scope.user_state_summary, scope=Scope.user_state_summary,
help="Total tally of answers from students." help=_("Total tally of answers from students.")
) )
choices = Dict(help="The user's answers", scope=Scope.user_state) choices = Dict(help=_("The user's answers"), scope=Scope.user_state)
event_namespace = 'xblock.survey' event_namespace = 'xblock.survey'
def student_view(self, context=None): def student_view(self, context=None):
...@@ -811,7 +842,7 @@ class SurveyBlock(PollBase): ...@@ -811,7 +842,7 @@ class SurveyBlock(PollBase):
choices = self.get_choices() choices = self.get_choices()
if choices and not self.private_results: if choices and not self.private_results:
result['success'] = False result['success'] = False
result['errors'].append("You have already voted in this poll.") result['errors'].append(self.ugettext("You have already voted in this poll."))
if not choices: if not choices:
# Reset submissions count if choices are bogus. # Reset submissions count if choices are bogus.
...@@ -819,15 +850,17 @@ class SurveyBlock(PollBase): ...@@ -819,15 +850,17 @@ class SurveyBlock(PollBase):
if not self.can_vote(): if not self.can_vote():
result['success'] = False result['success'] = False
result['errors'].append('You have already voted as many times as you are allowed.') result['errors'].append(self.ugettext('You have already voted as many times as you are allowed.'))
# Make sure the user has included all questions, and hasn't included # Make sure the user has included all questions, and hasn't included
# anything extra, which might indicate the questions have changed. # anything extra, which might indicate the questions have changed.
if not sorted(data.keys()) == sorted(questions.keys()): if not sorted(data.keys()) == sorted(questions.keys()):
result['success'] = False result['success'] = False
result['errors'].append( result['errors'].append(
"Not all questions were included, or unknown questions were " self.ugettext(
"included. Try refreshing and trying again." "Not all questions were included, or unknown questions were included. "
"Try refreshing and trying again."
)
) )
# Make sure the answer values are sane. # Make sure the answer values are sane.
...@@ -835,7 +868,11 @@ class SurveyBlock(PollBase): ...@@ -835,7 +868,11 @@ class SurveyBlock(PollBase):
if value not in answers.keys(): if value not in answers.keys():
result['success'] = False result['success'] = False
result['errors'].append( result['errors'].append(
"Found unknown answer '%s' for question key '%s'" % (key, value)) self.ugettext(
# Translators: {answer_key} uniquely identifies a specific answer belonging to a poll or survey.
# {question_key} uniquely identifies a specific question belonging to a poll or survey.
"Found unknown answer '{answer_key}' for question key '{question_key}'"
).format(answer_key=key, question_key=value))
if not result['success']: if not result['success']:
result['can_vote'] = self.can_vote() result['can_vote'] = self.can_vote()
...@@ -865,10 +902,10 @@ class SurveyBlock(PollBase): ...@@ -865,10 +902,10 @@ class SurveyBlock(PollBase):
feedback = data.get('feedback', '').strip() feedback = data.get('feedback', '').strip()
block_name = data.get('display_name', '').strip() block_name = data.get('display_name', '').strip()
private_results = bool(data.get('private_results', False)) private_results = bool(data.get('private_results', False))
max_submissions = self.get_max_submissions(data, result, private_results) max_submissions = self.get_max_submissions(self.ugettext, data, result, private_results)
answers = self.gather_items(data, result, 'Answer', 'answers', image=False) answers = self.gather_items(data, result, self.ugettext('Answer'), 'answers', image=False)
questions = self.gather_items(data, result, 'Question', 'questions') questions = self.gather_items(data, result, self.ugettext('Question'), 'questions')
if not result['success']: if not result['success']:
return result return result
......
...@@ -27,10 +27,12 @@ ...@@ -27,10 +27,12 @@
{{/each}} {{/each}}
</ul> </ul>
<input class="input-main" type="button" name="poll-submit" value="Submit" disabled> <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}} {{#if feedback}}
<hr /> <hr />
<h3 class="poll-header">Feedback</h3> <h3 class="poll-header">{{i18n "Feedback" }}</h3>
<div class="poll-feedback"> <div class="poll-feedback">
{{{feedback}}} {{{feedback}}}
</div> </div>
......
...@@ -3,26 +3,25 @@ ...@@ -3,26 +3,25 @@
<li class="field comp-setting-entry is-set poll-{{noun}}-studio-item"> <li class="field comp-setting-entry is-set poll-{{noun}}-studio-item">
<div class="wrapper-comp-setting"> <div class="wrapper-comp-setting">
<div class="poll-move"> <div class="poll-move">
<button class="poll-move-up">&#9650;<span class="sr">&nbsp;move poll up</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;move poll down</span></button> <button class="poll-move-down">&#9660;<span class="sr">&nbsp;{{i18n "move poll down"}}</span></button>
</div> </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> <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 /> <input class="input setting-input" name="{{noun}}-label-{{key}}" id="{{noun}}-label-{{key}}" value="{{text}}" type="text" /><br />
{{#if image}} {{#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 /> <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 /> <input class="input setting-input" name="{{noun}}-img_alt-{{key}}" id="{{noun}}-img_alt-{{key}}" value="{{img_alt}}" type="text" /><br />
{{/if}} {{/if}}
</div> </div>
<span class="tip setting-help"> <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>
<span class="tip setting-help"> <span class="tip setting-help">
{{#if image}} {{#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 {{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."}}
that describes the image in a way that would allow someone to answer the poll if the image did not load.
{{/if}} {{/if}}
</span> </span>
</li> </li>
......
...@@ -26,10 +26,13 @@ ...@@ -26,10 +26,13 @@
{{/each}} {{/each}}
</table> </table>
<input class="input-main" type="button" name="poll-submit" value="Submit" disabled> <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}} {{#if feedback}}
<hr /> <hr />
<h3 class="poll-header">Feedback</h3> <h3 class="poll-header">{{i18n "Feedback" }}</h3>
<div class="poll-feedback"> <div class="poll-feedback">
{{{feedback}}} {{{feedback}}}
</div> </div>
......
...@@ -10,7 +10,35 @@ function PollUtil (runtime, element, pollType) { ...@@ -10,7 +10,35 @@ function PollUtil (runtime, element, pollType) {
this.votedUrl = runtime.handlerUrl(element, 'student_voted'); this.votedUrl = runtime.handlerUrl(element, 'student_voted');
this.submit = $('input[type=button]', element); this.submit = $('input[type=button]', element);
this.answers = $('input[type=radio]', 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.resultsTemplate = Handlebars.compile($("#" + pollType + "-results-template", element).html());
this.viewResultsButton = $('.view-results-button', element); this.viewResultsButton = $('.view-results-button', element);
this.viewResultsButton.click(this.getResults); this.viewResultsButton.click(this.getResults);
...@@ -131,7 +159,7 @@ function PollUtil (runtime, element, pollType) { ...@@ -131,7 +159,7 @@ function PollUtil (runtime, element, pollType) {
thanks.fadeOut(0).fadeIn('slow', 'swing'); thanks.fadeOut(0).fadeIn('slow', 'swing');
$('.poll-feedback-container', element).removeClass('poll-hidden'); $('.poll-feedback-container', element).removeClass('poll-hidden');
if (can_vote) { if (can_vote) {
$('input[name="poll-submit"]', element).val('Resubmit'); $('input[name="poll-submit"]', element).val(gettext('Resubmit'));
} else { } else {
$('input', element).attr('disabled', true) $('input', element).attr('disabled', true)
} }
......
...@@ -13,6 +13,31 @@ function PollEditUtil(runtime, element, pollType) { ...@@ -13,6 +13,31 @@ function PollEditUtil(runtime, element, pollType) {
this.init = function () { this.init = function () {
// Set up the editing form for a Poll or Survey. // Set up the editing form for a Poll or Survey.
var temp = $('#poll-form-component', element).html(); 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); self.answerTemplate = Handlebars.compile(temp);
$(element).find('.cancel-button', element).bind('click', function() { $(element).find('.cancel-button', element).bind('click', function() {
...@@ -231,7 +256,7 @@ function PollEditUtil(runtime, element, pollType) { ...@@ -231,7 +256,7 @@ function PollEditUtil(runtime, element, pollType) {
data['private_results'] = eval($('#poll-private-results', element).val()); data['private_results'] = eval($('#poll-private-results', element).val());
if (notify) { if (notify) {
runtime.notify('save', {state: 'start', message: "Saving"}); runtime.notify('save', {state: 'start', message: gettext("Saving")});
} }
$.ajax({ $.ajax({
type: "POST", 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 ""
#
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"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: eo\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.8.7\n"
"X-Poedit-Basepath: ../../..\n"
"X-Poedit-SearchPath-0: poll.py\n"
#: poll.py
msgid "Whether or not to display results to the user."
msgstr ""
"Whéthér ör nöt tö dïspläý résülts tö thé üsér. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυя α#"
#: poll.py
msgid "The maximum number of times a user may send a submission."
msgstr ""
"Thé mäxïmüm nümßér öf tïmés ä üsér mäý sénd ä süßmïssïön. Ⱡ'σяєм ιρѕυм ∂σłσя"
" ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: poll.py
msgid "Number of times the user has sent a submission."
msgstr ""
"Nümßér öf tïmés thé üsér häs sént ä süßmïssïön. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт,"
" ¢σηѕє¢тєтυя α#"
#: poll.py
msgid "Text to display after the user votes."
msgstr ""
"Téxt tö dïspläý äftér thé üsér vötés. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυ#"
#: poll.py
#, python-brace-format
msgid "'{field}' is not present, or not a JSON array."
msgstr ""
"'{field}' ïs nöt présént, ör nöt ä JSÖN ärräý. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυя #"
#: poll.py
#, python-brace-format
msgid "{noun} {item} not a javascript object!"
msgstr ""
"{noun} {item} nöt ä jäväsçrïpt ößjéçt! Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тє#"
#: poll.py
#, python-brace-format
msgid "{noun} {item} contains no key."
msgstr "{noun} {item} çöntäïns nö kéý. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢ση#"
#: 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 ""
"{noun} häs nö téxt ör ïmg. Pléäsé mäké süré äll {noun_lower}s hävé öné ör thé öthér, ör "
"ßöth. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тє#"
#: 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 ""
"{noun} wäs äddéd wïth nö läßél. Àll {noun_lower}s müst hävé läßéls. Pléäsé çhéçk thé "
"förm. Çhéçk thé förm änd éxplïçïtlý délété {noun_lower}s ïf nöt néédéd. Ⱡ'σяєм ιρѕυм "
"∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕмσ∂ тємρσя "
"ιη¢ι∂ι∂υηт υт łαвσяє єт ∂σłσяє мαgηα αłιqυα. υт єηιм α∂ мιηιм νєηιαм, qυιѕ "
"ησѕтяυ∂ єχєя¢ιтαтιση υłłαм¢σ łαвσяιѕ ηιѕι υт αłιqυιρ єχ єα ¢σммσ∂σ "
"¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє ∂σłσя ιη яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт єѕѕє "
"¢ιłłυм ∂σłσяє єυ ƒυgιαт ηυłłα ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт "
"ηση ρяσι∂єηт, ѕυηт ιη ¢υłρα qυι σƒƒι¢ια ∂єѕєяυηт мσłłιт αηιм ι∂ єѕт łαвσяυ#"
#: 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 ""
"Àll ïmägés müst hävé än ältérnätïvé téxt désçrïßïng thé ïmägé ïn ä wäý thät "
"wöüld ällöw söméöné tö änswér thé pöll ïf thé ïmägé dïd nöt löäd. Ⱡ'σяєм "
"ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕмσ∂ тємρσя "
"ιη¢ι∂ι∂υηт υт łαвσяє єт ∂σłσяє мαgηα αłιqυα. υт єηιм α∂ мιηιм νєηιαм, qυιѕ "
"ησѕтяυ∂ єχєя¢ιтαтιση υłłαм¢σ łαвσяιѕ ηιѕι υт αłιqυιρ єχ єα ¢σммσ∂σ "
"¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє ∂σłσя ιη яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт єѕѕє "
"¢ιłłυм ∂σłσяє єυ ƒυgιαт ηυłłα ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт "
"ηση ρяσι∂єηт, ѕυηт ιη ¢υłρα qυι σƒƒι¢ια ∂єѕєяυηт мσłłιт αηιм ι∂ єѕт #"
#: poll.py
#, python-brace-format
msgid "You must include at least one {noun_lower}."
msgstr ""
"Ýöü müst ïnçlüdé ät léäst öné {noun_lower}. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєт#"
#: poll.py
msgid "Maximum Submissions missing or not an integer."
msgstr ""
"Mäxïmüm Süßmïssïöns mïssïng ör nöt än ïntégér. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυя α#"
#: poll.py
msgid "Private results may not be False when Maximum Submissions is not 1."
msgstr ""
"Prïväté résülts mäý nöt ßé Fälsé whén Mäxïmüm Süßmïssïöns ïs nöt 1. Ⱡ'σяєм "
"ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя #"
#: poll.py
msgid "Total tally of answers from students."
msgstr ""
"Tötäl tällý öf änswérs fröm stüdénts. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυ#"
#: poll.py
msgid "The student's answer"
msgstr "Thé stüdént's änswér Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, #"
#: poll.py
msgid "You have already voted in this poll."
msgstr ""
"Ýöü hävé älréädý vötéd ïn thïs pöll. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυ#"
#: poll.py
msgid "Answer not included with request."
msgstr ""
"Ànswér nöt ïnçlüdéd wïth réqüést. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тє#"
#: poll.py
#, python-brace-format
msgid "No key \"{choice}\" in answers table."
msgstr ""
"Nö kéý \"{choice}\" ïn änswérs täßlé. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢т#"
#: poll.py
msgid "You have already voted as many times as you are allowed."
msgstr ""
"Ýöü hävé älréädý vötéd äs mäný tïmés äs ýöü äré ällöwéd. Ⱡ'σяєм ιρѕυм ∂σłσя "
"ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: poll.py
msgid "You must specify a question."
msgstr "Ýöü müst spéçïfý ä qüéstïön. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢#"
#: poll.py
msgid "Answer choices for this Survey"
msgstr "Ànswér çhöïçés för thïs Sürvéý Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢т#"
#: poll.py
msgid "Are you enjoying the course?"
msgstr "Àré ýöü énjöýïng thé çöürsé? Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢#"
#: poll.py
msgid "Would you recommend this course to your friends?"
msgstr ""
"Wöüld ýöü réçömménd thïs çöürsé tö ýöür frïénds? Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт "
"αмєт, ¢σηѕє¢тєтυя α#"
#: poll.py
msgid "Do you think you will learn a lot?"
msgstr ""
"Dö ýöü thïnk ýöü wïll léärn ä löt? Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєт#"
#: poll.py
msgid "Questions for this Survey"
msgstr "Qüéstïöns för thïs Sürvéý Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕ#"
#: poll.py
msgid "Survey"
msgstr "Sürvéý Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕ#"
#: poll.py
msgid "The user's answers"
msgstr "Thé üsér's änswérs Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт#"
#: poll.py
msgid ""
"Not all questions were included, or unknown questions were included. Try "
"refreshing and trying again."
msgstr ""
"Nöt äll qüéstïöns wéré ïnçlüdéd, ör ünknöwn qüéstïöns wéré ïnçlüdéd. Trý "
"réfréshïng änd trýïng ägäïn. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмє#"
#: poll.py
#, python-format
msgid "Found unknown answer '{answer_key}' for question key '{question_key}'"
msgstr ""
"Föünd ünknöwn änswér '{answer_key}' för qüéstïön kéý '{question_key}' Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт,"
" ¢σηѕє¢тєтυя α#"
#: poll.py
msgid "Poll"
msgstr "Pöll Ⱡ'σяєм ι#"
#: poll.py
msgid "What is your favorite color?"
msgstr "Whät ïs ýöür fävörïté çölör? Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢#"
#: poll.py
msgid "Red"
msgstr "Réd Ⱡ'σяєм#"
#: poll.py
msgid "Blue"
msgstr "Blüé Ⱡ'σяєм ι#"
#: poll.py
msgid "Other"
msgstr "Öthér Ⱡ'σяєм ιρѕ#"
#: poll.py
msgid "The answer options on this poll."
msgstr ""
"Thé änswér öptïöns ön thïs pöll. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тє#"
#: poll.py
msgid "Answer"
msgstr "Ànswér Ⱡ'σяєм ιρѕυ#"
#: poll.py
msgid "Question"
msgstr "Qüéstïön Ⱡ'σяєм ιρѕυм ∂#"
#: poll.py
msgid "Yes"
msgstr "Ýés Ⱡ'σяєм#"
#: poll.py
msgid "No"
msgstr "Nö Ⱡ'σя#"
#: poll.py
msgid "Maybe"
msgstr "Mäýßé Ⱡ'σяєм ιρѕ#"
#: public/handlebars/poll_results.handlebars
msgid "Results gathered from {total} respondents."
msgstr ""
"Résülts gäthéréd fröm {total} réspöndénts. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυя#"
#: public/handlebars/poll_results.handlebars
msgid "Results gathered from {total} respondent."
msgstr ""
"Résülts gäthéréd fröm {total} réspöndént. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυ#"
#: public/handlebars/poll_studio.handlebars
msgid "move poll up"
msgstr "mövé pöll üp Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
#: public/handlebars/poll_studio.handlebars
msgid "move poll down"
msgstr "mövé pöll döwn Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт#"
#: public/handlebars/poll_studio.handlebars
msgid "Delete"
msgstr "Délété Ⱡ'σяєм ιρѕυ#"
#: public/handlebars/poll_studio.handlebars
msgid "Image URL"
msgstr "Ìmägé ÛRL Ⱡ'σяєм ιρѕυм ∂σł#"
#: public/handlebars/poll_studio.handlebars
msgid "Image alternative text"
msgstr "Ìmägé ältérnätïvé téxt Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢#"
#: public/handlebars/poll_studio.handlebars
msgid ""
"You can make limited use of Markdown in answer texts, preferably only bold "
"and italics."
msgstr ""
"Ýöü çän mäké lïmïtéd üsé öf Märkdöwn ïn änswér téxts, préféräßlý önlý ßöld "
"änd ïtälïçs. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє#"
#: 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 ""
"Thïs müst hävé än ïmägé ÛRL ör téxt, änd çän hävé ßöth. Ìf ýöü ädd än "
"ïmägé, ýöü müst älsö prövïdé än ältérnätïvé téxt thät désçrïßés thé ïmägé ïn"
" ä wäý thät wöüld ällöw söméöné tö änswér thé pöll ïf thé ïmägé dïd nöt "
"löäd. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ "
"єιυѕмσ∂ тємρσя ιη¢ι∂ι∂υηт υт łαвσяє єт ∂σłσяє мαgηα αłιqυα. υт єηιм α∂ мιηιм"
" νєηιαм, qυιѕ ησѕтяυ∂ єχєя¢ιтαтιση υłłαм¢σ łαвσяιѕ ηιѕι υт αłιqυιρ єχ єα "
"¢σммσ∂σ ¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє ∂σłσя ιη яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт"
" єѕѕє ¢ιłłυм ∂σłσяє єυ ƒυgιαт #"
# -*- 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): ...@@ -44,7 +44,7 @@ def package_data(pkg, roots):
setup( setup(
name='xblock-poll', name='xblock-poll',
version='1.0', version='1.1',
description='An XBlock for polling users.', description='An XBlock for polling users.',
packages=[ packages=[
'poll', 'poll',
...@@ -61,5 +61,5 @@ setup( ...@@ -61,5 +61,5 @@ setup(
'survey = poll:SurveyBlock', '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