Django

Code

Ticket #4030: 4030_language_info.diff

File 4030_language_info.diff, 20.9 kB (added by akaihola, 2 months ago)

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

  • django/conf/language_info.py

    old new  
     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

    old new  
    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

    old new  
    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

    old new  
     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

    old new  
     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

    old new  
    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

    old new  
    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

    old new  
    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