$30 off During Our Annual Pro Sale. View Details »

Gringo's Guide to Internationalization.

Jacob Burch
September 04, 2012

Gringo's Guide to Internationalization.

Jacob Burch

September 04, 2012
Tweet

More Decks by Jacob Burch

Other Decks in Technology

Transcript

  1. A Gringo’s Guide to
    Internationalization
    Going From Green to Experto with Django’s i18n
    Tools.
    Jacob Burch
    @jacobburch
    jacobb
    Tuesday, September 4, 12

    View Slide

  2. Me llamo Jacob
    • Engineer at Revolution Systems
    • Architect of CashStar Inc’s i18n efforts
    • 3 Years of C-Grade High School Spanish
    Tuesday, September 4, 12

    View Slide

  3. Who is Cashstar?
    Tuesday, September 4, 12

    View Slide

  4. Tuesday, September 4, 12

    View Slide

  5. Internationalization
    18 other characters
    i n
    s[1] + str(len(s[1:-1])) + s[-1]
    i18n
    + +
    Tuesday, September 4, 12

    View Slide

  6. gettext example
    #: constants.py:40
    msgid "Hello"
    msgstr "Hola"
    #: templates/base.html:41 templates/
    login.html:12
    msgid "%(username)s’ Account"
    msgstr "Cuenta de %(username)s"
    Tuesday, September 4, 12

    View Slide

  7. The Life Cycle of a Po
    File
    Tuesday, September 4, 12

    View Slide

  8. The Life Cycle of a Po
    File
    • Wrap text in translation function calls
    Tuesday, September 4, 12

    View Slide

  9. The Life Cycle of a Po
    File
    • Wrap text in translation function calls
    • django-admin.py makemessages
    Tuesday, September 4, 12

    View Slide

  10. The Life Cycle of a Po
    File
    • Wrap text in translation function calls
    • django-admin.py makemessages
    • Deliver po file to translation service
    Tuesday, September 4, 12

    View Slide

  11. The Life Cycle of a Po
    File
    • Wrap text in translation function calls
    • django-admin.py makemessages
    • Deliver po file to translation service
    • Receive file back
    Tuesday, September 4, 12

    View Slide

  12. The Life Cycle of a Po
    File
    • Wrap text in translation function calls
    • django-admin.py makemessages
    • Deliver po file to translation service
    • Receive file back
    • django-admin.py compilemessages
    Tuesday, September 4, 12

    View Slide

  13. The Life Cycle of a Po
    File
    • Wrap text in translation function calls
    • django-admin.py makemessages
    • Deliver po file to translation service
    • Receive file back
    • django-admin.py compilemessages
    • Fin
    Tuesday, September 4, 12

    View Slide

  14. Batteries Included
    •LocaleMiddleware
    •Template tags and python methods
    •manage.py makemessages
    Tuesday, September 4, 12

    View Slide

  15. gettext vs
    gettext_lazy
    >>> lazy_string = gettext_lazy("Hello")
    >>> normal_string = gettext("Hello")
    >>> print repr(lazy_string) #
    >>> print repr(normal-string) # "'Hello'"
    Tuesday, September 4, 12

    View Slide

  16. gettext best practices
    • Almost always use gettext_lazy
    • If you see a
    in HTML or debug code, You’re trying to use a
    gettext_lazy proxy string too soon.
    • Import gettext or gettext_lazy as _
    • If you need both in the same file, use _ and
    _lazy
    Tuesday, September 4, 12

    View Slide

  17. template tags
    • trans
    for short phrases with no variables
    • {% trans “Hello there!” %}
    • blocktrans
    for longer phrases containing
    variables
    • blocktrans
    for phrases containing variables
    Tuesday, September 4, 12

    View Slide

  18. using template tags
    • When possible, use trans
    • Avoid complicated blocktrans signatures
    • With blocktrans: whitespace matters. Keep it limited.
    Tuesday, September 4, 12

    View Slide

  19. {% blocktrans user=post.comments.recent.user
    user_post_count=post.comments.recent.user.post
    s.count %}
    {{ user }} has {{ user_post count }} posts
    {% endblocktrans %}
    BAD
    Tuesday, September 4, 12

    View Slide

  20. {% with user=post.comments.recent.user %}
    {% blocktrans user_post_count=user.posts.count %}
    {{ user }} has {{ user_post_count }} posts!
    {% endblocktrans %}
    OK
    {% blocktrans user_post_count=user.posts.count %}
    {{ user }} has {{ user_post_count }} posts!{%
    endblocktrans %}
    GOOD
    {% blocktrans %} {{ user }} has
    {{ user_post_count }} posts!{% endblocktrans %}
    Tuesday, September 4, 12

    View Slide

  21. The Second, Sad Life Of a .po file
    Tuesday, September 4, 12

    View Slide

  22. The Second, Sad Life Of a .po file
    • Get told by an non-English speaker that certain words
    aren’t translated
    Tuesday, September 4, 12

    View Slide

  23. The Second, Sad Life Of a .po file
    • Get told by an non-English speaker that certain words
    aren’t translated
    • Wrap missing text in translation function calls
    Tuesday, September 4, 12

    View Slide

  24. The Second, Sad Life Of a .po file
    • Get told by an non-English speaker that certain words
    aren’t translated
    • Wrap missing text in translation function calls
    • django-admin.py makemessages
    Tuesday, September 4, 12

    View Slide

  25. The Second, Sad Life Of a .po file
    • Get told by an non-English speaker that certain words
    aren’t translated
    • Wrap missing text in translation function calls
    • django-admin.py makemessages
    • Dig through translation to find the new ones
    Tuesday, September 4, 12

    View Slide

  26. The Second, Sad Life Of a .po file
    • Get told by an non-English speaker that certain words
    aren’t translated
    • Wrap missing text in translation function calls
    • django-admin.py makemessages
    • Dig through translation to find the new ones
    • Deliver po file to translation service
    Tuesday, September 4, 12

    View Slide

  27. The Second, Sad Life Of a .po file
    • Get told by an non-English speaker that certain words
    aren’t translated
    • Wrap missing text in translation function calls
    • django-admin.py makemessages
    • Dig through translation to find the new ones
    • Deliver po file to translation service
    • Receive file back, re-compile
    Tuesday, September 4, 12

    View Slide

  28. The Second, Sad Life Of a .po file
    • Get told by an non-English speaker that certain words
    aren’t translated
    • Wrap missing text in translation function calls
    • django-admin.py makemessages
    • Dig through translation to find the new ones
    • Deliver po file to translation service
    • Receive file back, re-compile
    • Have marketing ask you to change “just two words” in old translation
    Tuesday, September 4, 12

    View Slide

  29. The Second, Sad Life Of a .po file
    • Get told by an non-English speaker that certain words
    aren’t translated
    • Wrap missing text in translation function calls
    • django-admin.py makemessages
    • Dig through translation to find the new ones
    • Deliver po file to translation service
    • Receive file back, re-compile
    • Have marketing ask you to change “just two words” in old translation
    • Find out those words are located in the database
    Tuesday, September 4, 12

    View Slide

  30. The Second, Sad Life Of a .po file
    • Get told by an non-English speaker that certain words
    aren’t translated
    • Wrap missing text in translation function calls
    • django-admin.py makemessages
    • Dig through translation to find the new ones
    • Deliver po file to translation service
    • Receive file back, re-compile
    • Have marketing ask you to change “just two words” in old translation
    • Find out those words are located in the database
    • With great sadness, manually add those two words into the django.po file
    Tuesday, September 4, 12

    View Slide

  31. The Second, Sad Life Of a .po file
    • Get told by an non-English speaker that certain words
    aren’t translated
    • Wrap missing text in translation function calls
    • django-admin.py makemessages
    • Dig through translation to find the new ones
    • Deliver po file to translation service
    • Receive file back, re-compile
    • Have marketing ask you to change “just two words” in old translation
    • Find out those words are located in the database
    • With great sadness, manually add those two words into the django.po file
    • Get told by an non-English speaker that certain words aren’t translated
    Tuesday, September 4, 12

    View Slide

  32. The Second, Sad Life Of a .po file
    • Get told by an non-English speaker that certain words
    aren’t translated
    • Wrap missing text in translation function calls
    • django-admin.py makemessages
    • Dig through translation to find the new ones
    • Deliver po file to translation service
    • Receive file back, re-compile
    • Have marketing ask you to change “just two words” in old translation
    • Find out those words are located in the database
    • With great sadness, manually add those two words into the django.po file
    • Get told by an non-English speaker that certain words aren’t translated
    Tuesday, September 4, 12

    View Slide

  33. The Second, Sad Life Of a .po file
    • Get told by an non-English speaker that certain words
    aren’t translated
    • Wrap missing text in translation function calls
    • django-admin.py makemessages
    • Dig through translation to find the new ones
    • Deliver po file to translation service
    • Receive file back, re-compile
    • Have marketing ask you to change “just two words” in old translation
    • Find out those words are located in the database
    • With great sadness, manually add those two words into the django.po file
    • Get told by an non-English speaker that certain words aren’t translated
    Tuesday, September 4, 12

    View Slide

  34. poxx
    • poxx.py location/of/locale/django.po
    • Munges Translation to easily tell what’s
    been translated (and what hasn’t)
    • “your photo” becomes “ẏǿŭř ƥħǿŧǿ”
    speaker project plug time!
    Tuesday, September 4, 12

    View Slide

  35. poxx and ‘canonical’ translations
    • poxx.py location/of/locale/django.po -c location/of/locale/canon.po
    • Munges all translations not found in canon.po
    • poxx.py location/of/locale/django.po -c location/of/locale/canon.po --diff
    • Creates location/of/locale/django_diff.po file,
    containing all translations not found in canon.po
    Tuesday, September 4, 12

    View Slide

  36. django-dbgettext
    •python manage.py dbgettext_export
    • Creates new .po files containing text
    found in db in pre-defined model+fields
    Tuesday, September 4, 12

    View Slide

  37. Let’s Dive In Deeper
    Tuesday, September 4, 12

    View Slide

  38. LocaleMiddleware
    • The main engine behind translation.
    • Very “simple”: figures out which language to
    use, activates that language, and sets the
    appropriate request variables.
    Tuesday, September 4, 12

    View Slide

  39. LocaleMiddleware
    • Language is determined by:
    • URL Path (new in 1.4)
    , then
    • Session code, then
    • User’s cookie, then
    • Accept-Language header, then finally
    • Your project’s LANGUAGE_CODE setting
    django.utils.translation.trans_real.get_language_from_request
    Tuesday, September 4, 12

    View Slide

  40. • django.utils.translation.trans_real And You
    • DjangoTranslation
    • Utility functions:
    • to_language (en-us) and to_locale (en_US)
    Tuesday, September 4, 12

    View Slide

  41. Advanced i18n: Multi-Tenancy
    class SiteSpecificLocaleMiddleware(LocaleMiddleware):
    """
    Replaces LocaleMiddleware.
    Only use i18n infrastructure if our site is in settings.TRANSLATED_SITES
    request.site.url is set in an earlier middleware
    """
    def process_request(self, request):
    site_url = request.site.url
    if site_url in settings.TRANSLATED_SITES:
    super(SiteSpecificLocaleMiddleware, self).process_request(request)
    else:
    request.LANGUAGE_CODE = settings.LANGUAGE_CODE
    Tuesday, September 4, 12

    View Slide

  42. Advanced i18n: Site-specific translations
    class SiteSpecificLocaleMiddleware(LocaleMiddleware):
    """
    Replaces LocaleMiddleware.
    Only use i18n infrastructure if our site is in settings.TRANSLATED_SITES
    request.site.url is set in an earlier middleware
    """
    def process_request(self, request):
    site_url = request.site.url
    site_locale = os.path.join(SOME_PO_FILE_PATH, site_url)
    if os.path.isdir(site_specific_locale):
    site_specific_trans = _translation(
    site_specific_locale,
    request.LANGUAGE_CODE)
    if brand_specific_trans:
    curr_trans = getattr(_active, 'value', None)
    if curr_trans:
    curr_trans.merge(site_specific_trans)
    else:
    curr_trans = site_specific_trans
    _active.value = curr_trans
    Tuesday, September 4, 12

    View Slide

  43. Why Django i18n isn’t as
    pleasant as it could be
    and perhaps we could
    think of improvements
    “sucks” sounds so mean!
    Tuesday, September 4, 12

    View Slide

  44. The Future of i18n
    • USE_I18N?
    • Ability to add other non-gettext
    backends
    • Handling of dynamic translations
    • All contained within ticket #14974
    Tuesday, September 4, 12

    View Slide

  45. Questions?
    • https://github.com/jacobb/django_i18n_examples
    • https://github.com/jacobb/poxx
    • https://bitbucket.org/drmeers/django-dbgettext
    Tuesday, September 4, 12

    View Slide