Ticket #4030: 4030_language_info.diff

File 4030_language_info.diff, 20.9 KB (added by Antti Kaihola, 16 years ago)

implementation of the 2008-04-22 design, with tests and documentation

  • django/conf/language_info.py

     
     1language_info = {'ar': {'bidi': True,
     2        'language_code': 'ar',
     3        'name': 'Arabic',
     4        'name_local': u'Arabic'},
     5 'bg': {'bidi': False,
     6        'language_code': 'bg',
     7        'name': 'Bulgarian',
     8        'name_local': u'\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438'},
     9 'bn': {'bidi': False,
     10        'language_code': 'bn',
     11        'name': 'Bengali',
     12        'name_local': u'\u09ac\u09be\u0982\u09b2\u09be'},
     13 'ca': {'bidi': False,
     14        'language_code': 'ca',
     15        'name': 'Catalan',
     16        'name_local': u'Catal\xe0'},
     17 'cs': {'bidi': False,
     18        'language_code': 'cs',
     19        'name': 'Czech',
     20        'name_local': u'\u010desky'},
     21 'cy': {'bidi': False,
     22        'language_code': 'cy',
     23        'name': 'Welsh',
     24        'name_local': u'Cymraeg'},
     25 'da': {'bidi': False,
     26        'language_code': 'da',
     27        'name': 'Danish',
     28        'name_local': u'Dansk'},
     29 'de': {'bidi': False,
     30        'language_code': 'de',
     31        'name': 'German',
     32        'name_local': u'Deutsch'},
     33 'el': {'bidi': False,
     34        'language_code': 'el',
     35        'name': 'Greek',
     36        'name_local': u'\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac'},
     37 'en': {'bidi': False,
     38        'language_code': 'en',
     39        'name': 'English',
     40        'name_local': u'English'},
     41 'es': {'bidi': False,
     42        'language_code': 'es',
     43        'name': 'Spanish',
     44        'name_local': u'Espa\xf1ol'},
     45 'es-ar': {'bidi': False,
     46           'language_code': 'es-ar',
     47           'name': 'Argentinean Spanish',
     48           'name_local': u'Espa\xf1ol Argentino'},
     49 'eu': {'bidi': False,
     50        'language_code': 'eu',
     51        'name': 'Basque',
     52        'name_local': u'Basque'},
     53 'fa': {'bidi': True,
     54        'language_code': 'fa',
     55        'name': 'Persian',
     56        'name_local': u'Persian'},
     57 'fi': {'bidi': False,
     58        'language_code': 'fi',
     59        'name': 'Finnish',
     60        'name_local': u'suomi'},
     61 'fr': {'bidi': False,
     62        'language_code': 'fr',
     63        'name': 'French',
     64        'name_local': u'Fran\xe7ais'},
     65 'ga': {'bidi': False,
     66        'language_code': 'ga',
     67        'name': 'Irish',
     68        'name_local': u'Irish'},
     69 'gl': {'bidi': False,
     70        'language_code': 'gl',
     71        'name': 'Galician',
     72        'name_local': u'galego'},
     73 'he': {'bidi': True,
     74        'language_code': 'he',
     75        'name': 'Hebrew',
     76        'name_local': u'\u05e2\u05d1\u05e8\u05d9\u05ea - Hebrew'},
     77 'hr': {'bidi': False,
     78        'language_code': 'hr',
     79        'name': 'Croatian',
     80        'name_local': u'Hrvatski'},
     81 'hu': {'bidi': False,
     82        'language_code': 'hu',
     83        'name': 'Hungarian',
     84        'name_local': u'Magyar'},
     85 'is': {'bidi': False,
     86        'language_code': 'is',
     87        'name': 'Icelandic',
     88        'name_local': u'\xcdslenska'},
     89 'it': {'bidi': False,
     90        'language_code': 'it',
     91        'name': 'Italian',
     92        'name_local': u'Italiano'},
     93 'ja': {'bidi': False,
     94        'language_code': 'ja',
     95        'name': 'Japanese',
     96        'name_local': u'\u65e5\u672c\u8a9e'},
     97 'ka': {'bidi': False,
     98        'language_code': 'ka',
     99        'name': 'Georgian',
     100        'name_local': u'\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8'},
     101 'km': {'bidi': False,
     102        'language_code': 'km',
     103        'name': 'Khmer',
     104        'name_local': u'Khmer'},
     105 'kn': {'bidi': False,
     106        'language_code': 'kn',
     107        'name': 'Kannada',
     108        'name_local': u'Kannada'},
     109 'ko': {'bidi': False,
     110        'language_code': 'ko',
     111        'name': 'Korean',
     112        'name_local': u'\ud55c\uad6d\uc5b4'},
     113 'lv': {'bidi': False,
     114        'language_code': 'lv',
     115        'name': 'Latvian',
     116        'name_local': u'Latvie\u0161u'},
     117 'mk': {'bidi': False,
     118        'language_code': 'mk',
     119        'name': 'Macedonian',
     120        'name_local': u'\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438'},
     121 'nl': {'bidi': False,
     122        'language_code': 'nl',
     123        'name': 'Dutch',
     124        'name_local': u'Nederlands'},
     125 'no': {'bidi': False,
     126        'language_code': 'no',
     127        'name': 'Norwegian',
     128        'name_local': u'Norsk'},
     129 'pl': {'bidi': False,
     130        'language_code': 'pl',
     131        'name': 'Polish',
     132        'name_local': u'Polski'},
     133 'pt': {'bidi': False,
     134        'language_code': 'pt',
     135        'name': 'Portugese',
     136        'name_local': u'Portugese'},
     137 'pt-br': {'bidi': False,
     138           'language_code': 'pt-br',
     139           'name': 'Brazilian Portuguese',
     140           'name_local': u'Portugu\xeas Brasileiro'},
     141 'ro': {'bidi': False,
     142        'language_code': 'ro',
     143        'name': 'Romanian',
     144        'name_local': u'Romana'},
     145 'ru': {'bidi': False,
     146        'language_code': 'ru',
     147        'name': 'Russian',
     148        'name_local': u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439'},
     149 'sk': {'bidi': False,
     150        'language_code': 'sk',
     151        'name': 'Slovak',
     152        'name_local': u'Slovensky'},
     153 'sl': {'bidi': False,
     154        'language_code': 'sl',
     155        'name': 'Slovenian',
     156        'name_local': u'Slovenski'},
     157 'sr': {'bidi': False,
     158        'language_code': 'sr',
     159        'name': 'Serbian',
     160        'name_local': u'Srpski'},
     161 'sv': {'bidi': False,
     162        'language_code': 'sv',
     163        'name': 'Swedish',
     164        'name_local': u'Svenska'},
     165 'ta': {'bidi': False,
     166        'language_code': 'ta',
     167        'name': 'Tamil',
     168        'name_local': u'\u0ba4\u0bae\u0bbf\u0bb4\u0bcd'},
     169 'te': {'bidi': False,
     170        'language_code': 'te',
     171        'name': 'Telugu',
     172        'name_local': u'Telugu'},
     173 'tr': {'bidi': False,
     174        'language_code': 'tr',
     175        'name': 'Turkish',
     176        'name_local': u'T\xfcrk\xe7e'},
     177 'uk': {'bidi': False,
     178        'language_code': 'uk',
     179        'name': 'Ukrainian',
     180        'name_local': u'Ukrainian'},
     181 'zh-cn': {'bidi': False,
     182           'language_code': 'zh-cn',
     183           'name': 'Simplified Chinese',
     184           'name_local': u'\u7b80\u4f53\u4e2d\u6587'},
     185 'zh-tw': {'bidi': False,
     186           'language_code': 'zh-tw',
     187           'name': 'Traditional Chinese',
     188           'name_local': u'\u7e41\u9ad4\u4e2d\u6587'}}
  • django/templatetags/i18n.py

    Property changes on: django/conf/language_info.py
    ___________________________________________________________________
    Name: svn:eol-style
       + native
    
     
    1717        context[self.variable] = [(k, translation.ugettext(v)) for k, v in settings.LANGUAGES]
    1818        return ''
    1919
     20class GetLanguageInfoNode(Node):
     21    def __init__(self, lang_code, variable):
     22        self.lang_code = Variable(lang_code)
     23        self.variable = variable
     24
     25    def render(self, context):
     26        lang_code = self.lang_code.resolve(context)
     27        context[self.variable] = translation.get_language_info(lang_code)
     28        return ''
     29
     30class GetLanguageInfoListNode(Node):
     31    def __init__(self, languages, variable):
     32        self.languages = Variable(languages)
     33        self.variable = variable
     34
     35    def get_language_info(self, language):
     36        # ``language`` is either a language code string or a sequence with the
     37        # language code as its first item
     38        if len(language[0]) > 1:
     39            return translation.get_language_info(language[0])
     40        else:
     41            return translation.get_language_info(str(language))
     42
     43    def render(self, context):
     44        languages = self.languages.resolve(context)
     45        context[self.variable] = [self.get_language_info(l) for l in languages]
     46        return ''
     47
    2048class GetCurrentLanguageNode(Node):
    2149    def __init__(self, variable):
    2250        self.variable = variable
     
    107135        raise TemplateSyntaxError, "'get_available_languages' requires 'as variable' (got %r)" % args
    108136    return GetAvailableLanguagesNode(args[2])
    109137
     138def do_get_language_info(parser, token):
     139    """
     140    This will store the language information dictionary for the given language
     141    code in a context variable.
     142
     143    Usage::
     144
     145        {% get_language_info for language_code as l %}
     146        {{ l.language_code }}
     147        {{ l.name }}
     148        {{ l.name_local }}
     149        {{ l.bidi|yesno:"bi-directional,uni-directional" }}
     150    """
     151    args = token.contents.split()
     152    if len(args) != 5 or args[1] != 'for' or args[3] != 'as':
     153        raise TemplateSyntaxError, "'%s' requires 'for string as variable' (got %r)" % (args[0], args[1:])
     154    return GetLanguageInfoNode(args[2], args[4])
     155
     156def do_get_language_info_list(parser, token):
     157    """
     158    This will store a list of language information dictionaries for the given
     159    language codes in a context variable.  The language codes can be specified
     160    ither as a list of strings or a settings.LANGUAGES style tuple (or any
     161    sequence of sequences whose first items are language codes).
     162
     163    Usage::
     164
     165        {% get_language_info_list for LANGUAGES as langs %}
     166        {% for l in langs %}
     167          {{ l.language_code }}
     168          {{ l.name }}
     169          {{ l.name_local }}
     170          {{ l.bidi|yesno:"bi-directional,uni-directional" }}
     171        {% endfor %}
     172    """
     173    args = token.contents.split()
     174    if len(args) != 5 or args[1] != 'for' or args[3] != 'as':
     175        raise TemplateSyntaxError, "'%s' requires 'for sequence as variable' (got %r)" % (args[0], args[1:])
     176    return GetLanguageInfoListNode(args[2], args[4])
     177
     178def language_name(lang_code):
     179    return translation.get_language_info(lang_code)['name']
     180
     181def language_name_local(lang_code):
     182    return translation.get_language_info(lang_code)['name_local']
     183
     184def language_bidi(lang_code):
     185    return translation.get_language_info(lang_code)['bidi']
     186
    110187def do_get_current_language(parser, token):
    111188    """
    112189    This will store the current language in the context.
     
    253330            counter)
    254331
    255332register.tag('get_available_languages', do_get_available_languages)
     333register.tag('get_language_info', do_get_language_info)
     334register.tag('get_language_info_list', do_get_language_info_list)
    256335register.tag('get_current_language', do_get_current_language)
    257336register.tag('get_current_language_bidi', do_get_current_language_bidi)
    258337register.tag('trans', do_translate)
    259338register.tag('blocktrans', do_block_translate)
     339
     340register.filter(language_name)
     341register.filter(language_name_local)
     342register.filter(language_bidi)
  • django/utils/translation/__init__.py

     
    33"""
    44from django.utils.functional import lazy
    55from django.utils.encoding import force_unicode
     6try:
     7    from django.conf.language_info import language_info
     8except ImportError:
     9    language_info = {}
     10    from warnings import warn
     11    warn("Module django.conf.language_info not found. Run django/bin/make-language-info.py to generate it and improve performance.")
    612
    713__all__ = ['gettext', 'gettext_noop', 'gettext_lazy', 'ngettext',
    814        'ngettext_lazy', 'string_concat', 'activate', 'deactivate',
     
    109115    """
    110116    return u''.join([force_unicode(s) for s in strings])
    111117string_concat = lazy(string_concat, unicode)
     118
     119def get_language_info(lang_code):
     120    if lang_code not in language_info:
     121        from django.conf import settings
     122        for code, name in settings.LANGUAGES:
     123            if code == lang_code:
     124                old_lang = get_language()
     125                activate(lang_code)
     126                info = {"language_code": lang_code,
     127                        "name": name,
     128                        "name_local": ugettext(name),
     129                        "bidi": lang_code in settings.LANGUAGES_BIDI}
     130                activate(old_lang)
     131                language_info[lang_code] = info
     132                break
     133        else:
     134            raise KeyError("Unknown language code %r." % lang_code)
     135    return language_info[lang_code]
  • django/bin/make-language-info.py

     
     1#!/usr/bin/env python
     2
     3LANGUAGE_INFO_PY_FILE = 'django/conf/language_info.py'
     4
     5# Need to ensure that the i18n framework is enabled
     6from django.conf import settings
     7settings.configure(USE_I18N = True)
     8
     9# Don't display the import warning about the missing language info file
     10import warnings
     11warnings.filterwarnings('ignore', category=UserWarning)
     12
     13from django.utils import translation
     14from pprint import pformat
     15
     16def make_language_info():
     17    language_info = {}
     18    for code, name in settings.LANGUAGES:
     19        translation.activate(code)
     20        language_info[code] = translation.get_language_info(code)
     21    from os.path import join, dirname, abspath
     22    target = abspath(join(dirname(__file__), '..', '..', LANGUAGE_INFO_PY_FILE))
     23    print 'Writing language information to\n%s' % target
     24    file(target, 'w').write('language_info = %s\n' % pformat(language_info))
     25    print 'Done.'
     26
     27
     28if __name__ == "__main__":
     29    make_language_info()
  • tests/regressiontests/i18n/language_info.py

    Property changes on: django/bin/make-language-info.py
    ___________________________________________________________________
    Name: svn:executable
       + *
    Name: svn:eol-style
       + native
    
     
     1tests = """
     2>>> from django.utils.translation import get_language_info
     3>>> from pprint import pprint
     4>>> pprint(get_language_info('de'))
     5{'bidi': False,
     6 'language_code': 'de',
     7 'name': 'German',
     8 'name_local': u'Deutsch'}
     9"""
  • tests/regressiontests/i18n/tests.py

    Property changes on: tests/regressiontests/i18n/language_info.py
    ___________________________________________________________________
    Name: svn:eol-style
       + native
    
     
    11# coding: utf-8
    2 import misc
     2import misc, language_info
    33
    44regressions = ur"""
    55Format string interpolation should work with *_lazy objects.
     
    6868
    6969__test__ = {
    7070    'regressions': regressions,
     71    'language_info': language_info.tests,
    7172    'misc': misc.tests,
    7273}
  • tests/regressiontests/templates/tests.py

     
    755755            'i18n17': ('{% load i18n %}{% blocktrans with anton|escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α & β'),
    756756            'i18n18': ('{% load i18n %}{% blocktrans with anton|force_escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α & β'),
    757757
     758            # retrieving language information
     759            'i18n19': ('{% load i18n %}{% get_language_info for "de" as l %}{{ l.language_code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}', {}, 'de: German/Deutsch bidi=False'),
     760            'i18n20': ('{% load i18n %}{% get_language_info for LANGUAGE_CODE as l %}{{ l.language_code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}', {'LANGUAGE_CODE': 'fi'}, 'fi: Finnish/suomi bidi=False'),
     761            'i18n21': ('{% load i18n %}{% get_language_info_list for langcodes as langs %}{% for l in langs %}{{ l.language_code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}; {% endfor %}', {'langcodes': ['it', 'no']}, 'it: Italian/Italiano bidi=False; no: Norwegian/Norsk bidi=False; '),
     762            'i18n22': ('{% load i18n %}{% get_language_info_list for langcodes as langs %}{% for l in langs %}{{ l.language_code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}; {% endfor %}', {'langcodes': (('sl', 'Slovenian'), ('fa', 'Persian'))}, 'sl: Slovenian/Slovenski bidi=False; fa: Persian/Persian bidi=True; '),
     763            'i18n23': ('{% load i18n %}{{ "hu"|language_name }} {{ "hu"|language_name_local }} {{ "hu"|language_bidi }}', {}, 'Hungarian Magyar False'),
     764            'i18n24': ('{% load i18n %}{{ langcode|language_name }} {{ langcode|language_name_local }} {{ langcode|language_bidi }}', {'langcode': 'nl'}, 'Dutch Nederlands False'),
     765
    758766            ### HANDLING OF TEMPLATE_STRING_IF_INVALID ###################################
    759767
    760768            'invalidstr01': ('{{ var|default:"Foo" }}', {}, ('Foo','INVALID')),
  • docs/i18n.txt

     
    266266Each ``RequestContext`` has access to three translation-specific variables:
    267267
    268268    * ``LANGUAGES`` is a list of tuples in which the first element is the
    269       language code and the second is the language name (in that language).
     269      language code and the second is the language name (in English).
    270270    * ``LANGUAGE_CODE`` is the current user's preferred language, as a string.
    271271      Example: ``en-us``. (See "How language preference is discovered", below.)
    272272    * ``LANGUAGE_BIDI`` is the current language's direction. If True, it's a
     
    640640.. _session: ../sessions/
    641641.. _request object: ../request_response/#httprequest-objects
    642642
     643Local names of languages
     644========================
     645
     646The ``get_language_info()`` function provides detailed information about
     647languages::
     648
     649    >>> form django.utils.translation import get_language_info
     650    >>> l = get_language_info('de')
     651    >>> print l['name'], l['name_local'], l['bidi']
     652    German Deutsch False
     653
     654The ``name`` and ``name_local`` attributes of the dictionary contain the name
     655of the language in English and the language itself, respectively.  The ``bidi``
     656attribute is True only for bi-directional languages.
     657
     658In templates, you can use special template tags or filters to retrieve the same
     659information. To get information about a single language, use the
     660``{% get_language_info %}`` tag::
     661
     662    {% get_language_info for LANGUAGE_CODE as lang %}
     663    {% get_language_info for "pl" as lang %}
     664
     665You can then access the information::
     666
     667    Language code: {{ lang.language_code }}<br />
     668    Name of language: {{ lang.name_local }}<br />
     669    Name in English: {{ lang.name }}<br />
     670    Bi-directional: {{ lang.bidi }}
     671
     672You can also use the ``{% get_language_info_list %}`` template tag to retrieve
     673information for a list of languages (e.g. active languages as specified in
     674``settings.LANGUAGES``).  See `The set_language redirect view`_ for an example
     675of how to display a language selector using ``{% get_language_info_list %}``.
     676
     677In addition to ``settings.LANGUAGES`` style nested tuples,
     678``{% get_language_info_list %}`` supports simple lists of language codes.  If
     679you do this in your view::
     680
     681  return render_to_response('mytemplate.html',
     682                            {'available_languages': ['en', 'es', 'fr']},
     683                            RequestContext(request))
     684
     685you can iterate those languages in the template::
     686
     687  {% get_language_info_list available_languages %}
     688  {% for l in available_languages %} ... {% endfor %}
     689
     690There are also simple filters available for convenience:
     691
     692    * ``{{ LANGUAGE_CODE|language_name }}``  ("German")
     693    * ``{{ LANGUAGE_CODE|language_name_local }}`` ("Deutsch")
     694    * ``{{ LANGUAGE_CODE|bidi }}`` (False)     
     695
     696The source of the language information is the ``django.conf.language_info``
     697module.  If you add a new translation, you can run the
     698``django/bin/make-language-info.py`` script to insert the information for the
     699added language.
     700
    643701Using translations in your own projects
    644702=======================================
    645703
     
    732790    * If that's empty -- say, if a user's browser suppresses that header --
    733791      then the user will be redirected to ``/`` (the site root) as a fallback.
    734792
    735 Here's example HTML template code::
     793Here's example HTML template code (see `Local names of languages`_ for more
     794information)::
    736795
     796    {% get_language_info_list for LANGUAGES as available_languages %}
    737797    <form action="/i18n/setlang/" method="post">
    738     <input name="next" type="hidden" value="/next/page/" />
    739     <select name="language">
    740     {% for lang in LANGUAGES %}
    741     <option value="{{ lang.0 }}">{{ lang.1 }}</option>
    742     {% endfor %}
    743     </select>
    744     <input type="submit" value="Go" />
     798      <input name="next" type="hidden" value="/next/page/" />
     799      <select name="language">
     800        {% for lang in available_languages %}
     801        <option value="{{ lang.language_code }}" {% ifequal lang.language_code LANGUAGE_CODE %}selected{% endifequal %}>
     802          {{ lang.name_local }}
     803        </option>
     804        {% endfor %}
     805      </select>
     806      <input type="submit" value="Go" />
    745807    </form>
    746808
    747809Translations and JavaScript
Back to Top