Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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
  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
  3. 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
  4. The Life Cycle of a Po File • Wrap text

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

    in translation function calls • django-admin.py makemessages Tuesday, September 4, 12
  6. 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
  7. 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
  8. 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
  9. 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
  10. gettext vs gettext_lazy >>> lazy_string = gettext_lazy("Hello") >>> normal_string =

    gettext("Hello") >>> print repr(lazy_string) # <django.utils.functional...> >>> print repr(normal-string) # "'Hello'" Tuesday, September 4, 12
  11. gettext best practices • Almost always use gettext_lazy • If

    you see a <django.utils.functional...> 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
  12. 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
  13. using template tags • When possible, use trans • Avoid

    complicated blocktrans signatures • With blocktrans: whitespace matters. Keep it limited. Tuesday, September 4, 12
  14. {% 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
  15. {% 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  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 • 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
  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 • 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
  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 • 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
  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 • 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
  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 • 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. • django.utils.translation.trans_real And You • DjangoTranslation • Utility functions: •

    to_language (en-us) and to_locale (en_US) Tuesday, September 4, 12
  34. 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
  35. 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
  36. 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
  37. 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