__init__.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. """
  2. Internationalization support.
  3. """
  4. from __future__ import unicode_literals
  5. import re
  6. from django.utils.encoding import force_text
  7. from django.utils.functional import lazy
  8. from django.utils import six
  9. __all__ = [
  10. 'activate', 'deactivate', 'override', 'deactivate_all',
  11. 'get_language', 'get_language_from_request',
  12. 'get_language_info', 'get_language_bidi',
  13. 'check_for_language', 'to_locale', 'templatize', 'string_concat',
  14. 'gettext', 'gettext_lazy', 'gettext_noop',
  15. 'ugettext', 'ugettext_lazy', 'ugettext_noop',
  16. 'ngettext', 'ngettext_lazy',
  17. 'ungettext', 'ungettext_lazy',
  18. 'pgettext', 'pgettext_lazy',
  19. 'npgettext', 'npgettext_lazy',
  20. 'LANGUAGE_SESSION_KEY',
  21. ]
  22. LANGUAGE_SESSION_KEY = '_language'
  23. class TranslatorCommentWarning(SyntaxWarning):
  24. pass
  25. # Here be dragons, so a short explanation of the logic won't hurt:
  26. # We are trying to solve two problems: (1) access settings, in particular
  27. # settings.USE_I18N, as late as possible, so that modules can be imported
  28. # without having to first configure Django, and (2) if some other code creates
  29. # a reference to one of these functions, don't break that reference when we
  30. # replace the functions with their real counterparts (once we do access the
  31. # settings).
  32. class Trans(object):
  33. """
  34. The purpose of this class is to store the actual translation function upon
  35. receiving the first call to that function. After this is done, changes to
  36. USE_I18N will have no effect to which function is served upon request. If
  37. your tests rely on changing USE_I18N, you can delete all the functions
  38. from _trans.__dict__.
  39. Note that storing the function with setattr will have a noticeable
  40. performance effect, as access to the function goes the normal path,
  41. instead of using __getattr__.
  42. """
  43. def __getattr__(self, real_name):
  44. from django.conf import settings
  45. if settings.USE_I18N:
  46. from django.utils.translation import trans_real as trans
  47. else:
  48. from django.utils.translation import trans_null as trans
  49. setattr(self, real_name, getattr(trans, real_name))
  50. return getattr(trans, real_name)
  51. _trans = Trans()
  52. # The Trans class is no more needed, so remove it from the namespace.
  53. del Trans
  54. def gettext_noop(message):
  55. return _trans.gettext_noop(message)
  56. ugettext_noop = gettext_noop
  57. def gettext(message):
  58. return _trans.gettext(message)
  59. def ngettext(singular, plural, number):
  60. return _trans.ngettext(singular, plural, number)
  61. def ugettext(message):
  62. return _trans.ugettext(message)
  63. def ungettext(singular, plural, number):
  64. return _trans.ungettext(singular, plural, number)
  65. def pgettext(context, message):
  66. return _trans.pgettext(context, message)
  67. def npgettext(context, singular, plural, number):
  68. return _trans.npgettext(context, singular, plural, number)
  69. gettext_lazy = lazy(gettext, str)
  70. ugettext_lazy = lazy(ugettext, six.text_type)
  71. pgettext_lazy = lazy(pgettext, six.text_type)
  72. def lazy_number(func, resultclass, number=None, **kwargs):
  73. if isinstance(number, six.integer_types):
  74. kwargs['number'] = number
  75. proxy = lazy(func, resultclass)(**kwargs)
  76. else:
  77. class NumberAwareString(resultclass):
  78. def __mod__(self, rhs):
  79. if isinstance(rhs, dict) and number:
  80. try:
  81. number_value = rhs[number]
  82. except KeyError:
  83. raise KeyError('Your dictionary lacks key \'%s\'. '
  84. 'Please provide it, because it is required to '
  85. 'determine whether string is singular or plural.'
  86. % number)
  87. else:
  88. number_value = rhs
  89. kwargs['number'] = number_value
  90. translated = func(**kwargs)
  91. try:
  92. translated = translated % rhs
  93. except TypeError:
  94. # String doesn't contain a placeholder for the number
  95. pass
  96. return translated
  97. proxy = lazy(lambda **kwargs: NumberAwareString(), NumberAwareString)(**kwargs)
  98. return proxy
  99. def ngettext_lazy(singular, plural, number=None):
  100. return lazy_number(ngettext, str, singular=singular, plural=plural, number=number)
  101. def ungettext_lazy(singular, plural, number=None):
  102. return lazy_number(ungettext, six.text_type, singular=singular, plural=plural, number=number)
  103. def npgettext_lazy(context, singular, plural, number=None):
  104. return lazy_number(npgettext, six.text_type, context=context, singular=singular, plural=plural, number=number)
  105. def activate(language):
  106. return _trans.activate(language)
  107. def deactivate():
  108. return _trans.deactivate()
  109. class override(object):
  110. def __init__(self, language, deactivate=False):
  111. self.language = language
  112. self.deactivate = deactivate
  113. self.old_language = get_language()
  114. def __enter__(self):
  115. if self.language is not None:
  116. activate(self.language)
  117. else:
  118. deactivate_all()
  119. def __exit__(self, exc_type, exc_value, traceback):
  120. if self.deactivate:
  121. deactivate()
  122. else:
  123. activate(self.old_language)
  124. def get_language():
  125. return _trans.get_language()
  126. def get_language_bidi():
  127. return _trans.get_language_bidi()
  128. def check_for_language(lang_code):
  129. return _trans.check_for_language(lang_code)
  130. def to_locale(language):
  131. return _trans.to_locale(language)
  132. def get_language_from_request(request, check_path=False):
  133. return _trans.get_language_from_request(request, check_path)
  134. def get_language_from_path(path):
  135. return _trans.get_language_from_path(path)
  136. def templatize(src, origin=None):
  137. return _trans.templatize(src, origin)
  138. def deactivate_all():
  139. return _trans.deactivate_all()
  140. def _string_concat(*strings):
  141. """
  142. Lazy variant of string concatenation, needed for translations that are
  143. constructed from multiple parts.
  144. """
  145. return ''.join(force_text(s) for s in strings)
  146. string_concat = lazy(_string_concat, six.text_type)
  147. def get_language_info(lang_code):
  148. from django.conf.locale import LANG_INFO
  149. try:
  150. return LANG_INFO[lang_code]
  151. except KeyError:
  152. if '-' not in lang_code:
  153. raise KeyError("Unknown language code %s." % lang_code)
  154. generic_lang_code = lang_code.split('-')[0]
  155. try:
  156. return LANG_INFO[generic_lang_code]
  157. except KeyError:
  158. raise KeyError("Unknown language code %s and %s." % (lang_code, generic_lang_code))
  159. trim_whitespace_re = re.compile('\s*\n\s*')
  160. def trim_whitespace(s):
  161. return trim_whitespace_re.sub(' ', s.strip())