Skip to content
This repository has been archived by the owner on May 18, 2021. It is now read-only.

i18n tooling using Transifex #2

Merged
merged 1 commit into from
Oct 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
*.pyc
oppia_xblock.egg-info/*

# Translation files
*.pot
14 changes: 14 additions & 0 deletions .tx/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[main]
host = https://www.transifex.com

[xblocks.xblock-oppia]
source_file = oppia/translations/en/LC_MESSAGES/text.po
file_filter = oppia/translations/<lang>/LC_MESSAGES/text.po
source_lang = en
type = PO

[xblocks.xblock-oppia-js]
source_file = oppia/translations/en/LC_MESSAGES/textjs.po
file_filter = oppia/translations/<lang>/LC_MESSAGES/textjs.po
source_lang = en
type = PO
62 changes: 62 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
.PHONY: dummy_translations extract_translations fake_translations help pull_translations push_translations


.DEFAULT_GOAL := help

help: ## display this help message
@echo "Please use \`make <target>' where <target> is one of"
@perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m %-25s\033[0m %s\n", $$1, $$2}'

clean: ## remove generated byte code, coverage reports, and build artifacts
find . -name '__pycache__' -exec rm -rf {} +
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
find . -name '*~' -exec rm -f {} +
find . -name '*.pot' -exec rm -f {} +

rm -f locale/en/LC_MESSAGES/*

find oppia/translations -name djangojs.mo -exec rm -f {} +
find oppia/translations -name djangojs.po -exec rm -f {} +
find oppia/translations -name textjs.mo -exec rm -f {} +

rm -fr build/
rm -fr dist/
rm -fr *.egg-info

requirements: ## install development environment requirements
pip install -r requirements.txt --exists-action w
pip install -r requirements-dev.txt --exists-action w

selfcheck: ## check that the Makefile is well-formed
@echo "The Makefile is well-formed."

## Localization targets

extract_translations: clean ## extract strings to be translated, outputting .po files
python manage.py makemessages -l en -d django
python manage.py makemessages -l en -d djangojs -e js --ignore oppia/static/js/translations
mv locale/en/LC_MESSAGES/django.po locale/en/LC_MESSAGES/text.po
mv locale/en/LC_MESSAGES/djangojs.po locale/en/LC_MESSAGES/textjs.po

compile_translations: ## compile translation files, outputting .mo files for each supported language
django-admin compilemessages
python manage.py compilejsi18n
make clean

detect_changed_source_translations: ## determines if the source translation files are up-to-date, otherwise exit with a non-zero code.
i18n_tool changed

pull_translations: ## pull translations from Transifex
i18n_tool transifex pull
make compile_translations

push_translations: extract_translations ## push source translation files (.po) to Transifex
i18n_tool transifex push

dummy_translations: ## generate dummy translation (.po) files
i18n_tool dummy

build_dummy_translations: extract_translations dummy_translations compile_translations ## generate and compile dummy translation files

validate_translations: build_dummy_translations detect_changed_source_translations ## validate translations
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,35 @@ Notes:
1. You will need to add the value "oppia" to the `advanced_modules` field in Studio's 'Advanced Settings' menu.
2. On devstack, the XBlock does not show up in Studio as a live preview in the editor. However, it does show up in the LMS.
3. This XBlock also comes with default logging capabilities that make use of edX's `event-tracking` library (documented [here](http://edx.readthedocs.org/projects/edx-developer-guide/en/latest/analytics.html#event-tracking)).


## Working with Translations

For information about working with translations, see the [Internationalization Support](http://edx.readthedocs.io/projects/xblock-tutorial/en/latest/edx_platform/edx_lms.html#internationalization-support) section of the [Open edX XBlock Tutorial](https://xblock-tutorial.readthedocs.io/en/latest/).


### Working with Transifex
Prepare your environment:

```
$ mkvirtualenv poll-xblock
$ make requirements
```

Also ensure that the [Transifex client has the proper authentication](https://docs.transifex.com/client/init)
in the `~/.transifexrc` file.

Push new strings to Transifex:
```
$ make push_translations
```

To get the latest translations from Transifex:
```
$ make pull_translations
```

For testing purposes it's faster to avoid Transifex and work on dummy Esperanto translations:
```
$ make build_dummy_translations
```
1 change: 1 addition & 0 deletions locale
10 changes: 10 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oppia.settings")

from django.core.management import execute_from_command_line

execute_from_command_line(sys.argv)
59 changes: 46 additions & 13 deletions oppia/oppia.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,42 @@

"""This XBlock embeds an instance of Oppia in the OpenEdX platform."""

import os
import pkg_resources

from django.template import Context
from django import utils

from xblock.core import XBlock
from xblock.fields import Scope, String
from xblock.fragment import Fragment
from xblockutils.resources import ResourceLoader


from .utils import _


@XBlock.needs("i18n")
class OppiaXBlock(XBlock):
"""
An XBlock providing an embedded Oppia exploration.
"""
loader = ResourceLoader(__name__)

_EVENT_NAME_EXPLORATION_LOADED = 'oppia.exploration.loaded'
_EVENT_NAME_EXPLORATION_COMPLETED = 'oppia.exploration.completed'
_EVENT_NAME_STATE_TRANSITION = 'oppia.exploration.state.changed'

display_name = String(
help="Display name of the component",
default="Oppia Exploration",
help=_("Display name of the component"),
default=_("Oppia Exploration"),
scope=Scope.content)
oppiaid = String(
help="ID of the Oppia exploration to embed",
help=_("ID of the Oppia exploration to embed"),
default="4",
scope=Scope.content)
src = String(
help="Source URL of the site",
help=_("Source URL of the site"),
default="https://www.oppia.org",
scope=Scope.content)

Expand All @@ -49,13 +60,31 @@ def resource_string(self, path):
data = pkg_resources.resource_string(__name__, path)
return data.decode("utf8")

def render_template(self, path, context):
return self.loader.render_django_template(
os.path.join('templates', path),
context=Context(context),
i18n_service=self.runtime.service(self, "i18n"),
)

def get_translation_content(self):
try:
return self.resource_string('static/js/translations/{lang}/textjs.js'.format(
lang=utils.translation.get_language(),
))
except IOError:
return self.resource_string('static/js/translations/en/textjs.js')

def student_view(self, context=None):
"""
The primary view of the OppiaXBlock, shown to students
when viewing courses.
"""
html = self.resource_string("static/html/oppia.html")
frag = Fragment(html.format(self=self))
frag = Fragment(self.render_template("oppia.html", {
'src': self.src,
'oppiaid': self.oppiaid,
}))
frag.add_javascript(self.get_translation_content())
frag.add_javascript(
self.resource_string('static/lib/oppia-player-0.0.1-modified.js'))
frag.add_javascript(self.resource_string("static/js/oppia.js"))
Expand All @@ -68,8 +97,11 @@ def author_view(self, context=None):
reason, the student_view() does not display, so we show a placeholder
instead.
"""
html = self.resource_string("static/html/oppia_preview.html")
frag = Fragment(html.format(self=self))
frag = Fragment(self.render_template("oppia_preview.html", {
'src': self.src,
'oppiaid': self.oppiaid,
}))
frag.add_javascript(self.get_translation_content())
return frag

def _log(self, event_name, payload):
Expand Down Expand Up @@ -108,12 +140,13 @@ def studio_view(self, context):
"""
Create a fragment used to display the edit view in the Studio.
"""
html_str = pkg_resources.resource_string(
__name__, "static/html/oppia_edit.html")
oppiaid = self.oppiaid or ''
frag = Fragment(unicode(html_str).format(
oppiaid=oppiaid, src=self.src, display_name=self.display_name))
frag = Fragment(self.render_template("oppia_edit.html", {
'src': self.src,
'oppiaid': self.oppiaid or '',
'display_name': self.display_name,
}))

frag.add_javascript(self.get_translation_content())
js_str = pkg_resources.resource_string(
__name__, "static/js/oppia_edit.js")
frag.add_javascript(unicode(js_str))
Expand Down
82 changes: 82 additions & 0 deletions oppia/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
"""
Django settings for the Oppia XBlock project.
For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
import yaml

BASE_DIR = os.path.dirname(os.path.dirname(__file__))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
# This is just a container for running tests, it's okay to allow it to be
# defaulted here if not present in environment settings
SECRET_KEY = os.environ.get('SECRET_KEY', '",cB3Jr.?xu[x_Ci]!%HP>#^AVmWi@r/W3u,w?pY+~J!R>;WN+,3}Sb{K=Jp~;&k')

# SECURITY WARNING: don't run with debug turned on in production!
# This is just a container for running tests
DEBUG = True

TEMPLATE_DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = (
'statici18n',
'oppia',
)

# Internationalization
# https://docs.djangoproject.com/en/1.6/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.6/howto/static-files/

STATIC_URL = '/static/'

LOCALE_PATHS = [
os.path.join(BASE_DIR, 'oppia/translations'),
]

# statici18n
# http://django-statici18n.readthedocs.io/en/latest/settings.html

with open(os.path.join(BASE_DIR, 'oppia/translations/config.yaml'), 'r') as locale_config_file:
locale_config = yaml.load(locale_config_file)

LANGUAGES = [
(code, code,)
for code in locale_config['locales'] + locale_config['dummy_locales']
]

STATICI18N_DOMAIN = 'textjs'

# `OppiaI18N` might collide with other modules, using `OppiaXBlockI18N` to be more explicit
STATICI18N_NAMESPACE = 'OppiaXBlockI18N'
STATICI18N_PACKAGES = (
'oppia',
)
STATICI18N_ROOT = 'oppia/static/js'
STATICI18N_OUTPUT_DIR = 'translations'
35 changes: 0 additions & 35 deletions oppia/static/html/oppia_edit.html

This file was deleted.

10 changes: 0 additions & 10 deletions oppia/static/html/oppia_preview.html

This file was deleted.

Loading