Multi language questions

One of the most important features of rumbas is that it is easy to create different language versions of the same question.

Create a new rumbas project

Task

  • Create a new folder multi_language_questions for this tutorial.
  • Create a .rumbasrc.yaml file (with the right content) in this folder.
  • Create a questions folder in this folder.
  • Create an exams folder in this folder.
  • Create a defaults folder in this folder.

Task

Copy the questions, exams and default from your using_defaults rumbas project to this project.

Translating the statement

Currently our question with variables is defined in yaml as follows:

---
type: normal
statement: How much is 9 * {a}?
advice: You could calculate 10 * {a} and then subtract {a}. This yields {a} * 10 - {a} = {a * 10} - {a} = {a*9}
parts:
  - type: number_entry
    prompt: Enter your answer in the inputbox
    marks: 5 
    answer: a * 9
variables:
  a: 3

Currently this question is only available in English. We want to make this question available in Dutch as well.

Let's take a look again at the structure of a questions.

Task

Take a good look at the (datatype of the) statement and advice field.

Reference

Question

fieldtypedescription
statementTranslationThe statement is a content area which appears at the top of the question, before any input boxes. Use the statement to set up the question and provide any information the student needs to answer it.
adviceTranslationAdvice is a content area which is shown when the student presses the Reveal button to reveal the question’s answers, or at the end of the exam. The advice area is normally used to present a worked solution to the question.
partsArray of QuestionPartA question consists of one or more parts. Each part can have a different type to create elaborate questions.
builtin_constantsBuiltinConstantsSpecifies which constants are enabled. You might want to disable the constant e so it can be used as a variable in the questions.
custom_constantsArray of CustomConstantCustom constants that are used in your question.
variablesMap from String to VariableRepresentationThe (ungrouped) variables that are used in this question.
grouped_variables"none" or Map from String to Map from String to VariableRepresentationThe (grouped) variables that are used in this question. This is a map from a group name to a map of variables. Should mainly be used to make it easier to template multiple variables at once.
variables_testVariablesTestThe test to which your variables should comply. Sometimes it’s hard to define randomised question variables so they’re guaranteed to produce a usable set of values. In these cases, it’s easier to state the condition you want the variables to satisfy, Variable values are generated until this condition passes.
While this tool allows you to pick sets of variables that would be hard to generate constructively, it’s a random process so you must be aware that there’s a chance no suitable set of values will ever be found.
functionsMap from String to FunctionThe functions that are used in this question
preamblePreambleSpecify custom javascript and css code that should be loaded.
navigationQuestionNavigationSpecify some navigation options for the question.
extensionsExtensionsUse this to enable the extensions that are used in the question
diagnostic_topic_namesArray of TranslationThe names of the topics used in diagnostic exams that this question belongs to
resourcesArray of ResourcePathThe paths to the resources
custom_part_typesArray of CustomPartTypeDefinitionPathThe custom part types used in this exam
rulesetsMap from String to JMERulesetItemThe rulesets defined in this question. A “ruleset” defines a list of named simplification rules used to manipulate mathematical expressions. https://numbas-editor.readthedocs.io/en/latest/question/reference.html#rulesets

We see that the statement and advice fields have the type Translation.

Reference

Translation

One of the following items:

typedescription
TranslationStructA structured translatable string with placeholders.
FileStringA simple filestring. This implies that it can also just be a string.

For now we will focus on the first option, the TranslationStruct.

Reference

TranslationStruct

fieldtypedescription
contentTranslationContentThe content with optional placeholders ({{placeholder-name}}).
placeholdersMap from String to TranslationThe values for the placeholders. It maps the placeholder-name to it's translatable value. The value for a placeholder can thus (if needed) be different for different locales.
---
content: # the content of form TranslationContent
placeholders: {} # empty for now

Let's now have a look at TranslationContent.

Reference

TranslationContent

One of the following items:

typedescription
Map from String to FileStringMap from locale to content. You can use this to specify different content for different locales.
FileStringA filestring. Possibly to a file that is placed in locale folders and is therefore localized.

The first option is the most important for now. It says that we can specify different versions of the content in different languages by using a hash.

---
content:
  en: english content
  nl: dutch content
placeholders: {} # empty for now

Task

Update the statement, advice and prompt fields of the first_question_with_variables.yaml question to make it available in Dutch as shown below.

---
type: normal
statement: 
  content: 
    en: How much is 9 * {a}?
    nl: Hoeveel is 9 * {a}?
  placeholders: {}
advice: 
  content: 
    en: You could calculate 10 * {a} and then subtract {a}. This yields {a} * 10 - {a} = {a * 10} - {a} = {a*9}
    nl: Je kan 10 * {a} berekenen en dan {a} daarvan aftrekken. Dit geeft {a} * 10 - {a} = {a * 10} - {a} = {a*9}
  placeholders: {}
parts:
  - type: number_entry
    prompt: 
      content:
        en: Enter your answer in the inputbox
        nl: Geef je antwoord in in het invulveld
      placeholders: {}
    marks: 5 
    answer: a * 9
variables:
  a: 3

Task

Recompile all exams

Danger

Take a good look at the output of the compilation. You should see that all successful compilations happened 'with locale en'.

Question

Any idea why this is the case?

Info

Each exams specifies for which locales it needs to be compiled.

Task

Take a look at the default values to see which locales are set.

---
- name: en # custom name
  numbas_locale: en-GB # English (United Kingdom)

Question

Do you see why we used the key en in the content field of the question?

Task

Add the nl locale to the locales.yaml file.

---
- name: en # custom name
  numbas_locale: en-GB # English (United Kingdom)
- name: nl # custom name
  numbas_locale: nl-NL # Dutch (The Netherlands)

Task

Recompile all exams. You should see that the exams are now compiled for both locales.

You can access the dutch exams at http://localhost:8000/nl/exams/first_question_with_variables/

You can also find the dutch exam and english exam in the online demo.

Placeholders

If we look at statement and advice we see that some values (mostly math expressions) are language independant.

---
type: normal
statement: 
  content: 
    en: How much is 9 * {a}?
    nl: Hoeveel is 9 * {a}?
  placeholders: {}
advice: 
  content: 
    en: You could calculate 10 * {a} and then subtract {a}. This yields {a} * 10 - {a} = {a * 10} - {a} = {a*9}
    nl: Je kan 10 * {a} berekenen en dan {a} daarvan aftrekken. Dit geeft {a} * 10 - {a} = {a * 10} - {a} = {a*9}
  placeholders: {}
parts:
  - type: number_entry
    prompt: 
      content:
        en: Enter your answer in the inputbox
        nl: Geef je antwoord in in het invulveld
      placeholders: {}
    marks: 5 
    answer: a * 9
variables:
  a: 3

This is where placeholders come in. Placeholders can be specified by name and then be used in the content field by writing {name}.

---
type: normal
statement: 
  content: 
    en: How much is {formula}?
    nl: Hoeveel is {formula}?
  placeholders:
      formula: 9 * {a}
advice: 
  content: 
    en: You could calculate {times10} and then subtract {a}. This yields {result}
    nl: Je kan {times10} berekenen en dan {a} daarvan aftrekken. Dit geeft {result}
  placeholders:
    times10: 10 * {a} 
    result: '{a} * 10 - {a} = {a * 10} - {a} = {a*9}'
parts:
  - type: number_entry
    prompt: 
      content:
        en: Enter your answer in the inputbox
        nl: Geef je antwoord in in het invulveld
      placeholders: {}
    marks: 5 
    answer: a * 9
variables:
  a: 3

Task

Recompile all exams. You should see no difference but there are less chances to have different formulas in the different languages.

Info

You can also use the placeholders the other way around. This is mainly useful when almost the whole string is maths and some small parts need to be translated. This might not work for all languages due to different grammar rules etc. For example:

---
content: "{command} $5x^2-10$."
placeholders:
  command: 
    content:
      nl: Ontbind in factoren
      en: Factorize
    placeholders: {}

Locale Folders

It might happen that you want to use the same text in different questions or exams. This is where the FileString type comes in.

Task

Take a look at the second option of the Translation type. `

Reference

Translation

One of the following items:

typedescription
TranslationStructA structured translatable string with placeholders.
FileStringA simple filestring. This implies that it can also just be a string.

Let's take a look at the FileString type.

Reference

FileString

One of the following items:

typedescription
StringA string of the form file:<filepath> where filepath is the relative path (within the exams or questions folder) to a file containing content. This content can be localized by placing it in locale folders. e.g. file:examples/basic-explanation.html will search for files in folders with following form: questions/examples/locale-<localename>/basic-explanation.html If a file isn't found for a specific locale, questions/examples/basic-explanation.html will be used.
StringA literal string.

Info

A filestring is thus one of the following:

  1. A string that starts with file: followed by a path to a file
  2. Another string that is interpreted literally as text.

In the first option, it is possible to use locale folders to specify different versions of the file for different locales.

Using the FileString type in combination with locale folders gives us a powerful mechanism to reuse text in different questions and exams.

Task

Move the statement content to localized files.

This yields following yaml code for the question

---
type: normal
statement: 
  content: how-much.html
  placeholders:
      formula: 9 * {a}
advice: 
  content: 
    en: You could calculate {times10} and then subtract {a}. This yields {result}
    nl: Je kan {times10} berekenen en dan {a} daarvan aftrekken. Dit geeft {result}
  placeholders:
    times10: 10 * {a} 
    result: '{a} * 10 - {a} = {a * 10} - {a} = {a*9}'
parts:
  - type: number_entry
    prompt: 
      content:
        en: Enter your answer in the inputbox
        nl: Geef je antwoord in in het invulveld
      placeholders: {}
    marks: 5 
    answer: a * 9
variables:
  a: 3

And questions/locale-nl/how-much.html file:

Hoeveel is {formula}?

And questions/locale-en/how-much.html file:

How much is {formula}?

Task

Update all questions and exams so they are available in both English and Dutch. If you don't speak dutch, use any other language. It is also possible to use the locales for different styles of instruction.