diff -r f700b623b8db -r 07ed5e4869c0 django/conf/locale/language_info.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/django/conf/locale/language_info.py	Thu Dec 02 12:22:29 2010 -0300
@@ -0,0 +1,260 @@
+# This file was generated with the makelanginfo Django management command and
+# is not meant to be edited by hand. For new translations, add them following
+# the steps described in the internals/contributing.txt document ('Submitting
+# and maintaining translations' section) and finally re-run
+# 'django-admin.py makelanginfo' to generate this file.
+
+lang_info = {
+'ar': {'bidi': True,
+        'language_code': 'ar',
+        'name': 'Arabic',
+        'name_local': u'\u0627\u0644\u0639\u0631\u0628\u064a\u0651\u0629'},
+ 'bg': {'bidi': False,
+        'language_code': 'bg',
+        'name': 'Bulgarian',
+        'name_local': u'\u0431\u044a\u043b\u0433\u0430\u0440\u0441\u043a\u0438'},
+ 'bn': {'bidi': False,
+        'language_code': 'bn',
+        'name': 'Bengali',
+        'name_local': u'\u09ac\u09be\u0982\u09b2\u09be'},
+ 'bs': {'bidi': False,
+        'language_code': 'bs',
+        'name': 'Bosnian',
+        'name_local': u'bosanski'},
+ 'ca': {'bidi': False,
+        'language_code': 'ca',
+        'name': 'Catalan',
+        'name_local': u'catal\xe0'},
+ 'cs': {'bidi': False,
+        'language_code': 'cs',
+        'name': 'Czech',
+        'name_local': u'\u010desky'},
+ 'cy': {'bidi': False,
+        'language_code': 'cy',
+        'name': 'Welsh',
+        'name_local': u'Cymraeg'},
+ 'da': {'bidi': False,
+        'language_code': 'da',
+        'name': 'Danish',
+        'name_local': u'Dansk'},
+ 'de': {'bidi': False,
+        'language_code': 'de',
+        'name': 'German',
+        'name_local': u'Deutsch'},
+ 'el': {'bidi': False,
+        'language_code': 'el',
+        'name': 'Greek',
+        'name_local': u'\u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac'},
+ 'en': {'bidi': False,
+        'language_code': 'en',
+        'name': 'English',
+        'name_local': u'English'},
+ 'en-gb': {'bidi': False,
+           'language_code': 'en-gb',
+           'name': 'British English',
+           'name_local': u'British English'},
+ 'es': {'bidi': False,
+        'language_code': 'es',
+        'name': 'Spanish',
+        'name_local': u'espa\xf1ol'},
+ 'es-ar': {'bidi': False,
+           'language_code': 'es-ar',
+           'name': 'Argentinian Spanish',
+           'name_local': u'espa\xf1ol de Argentina'},
+ 'et': {'bidi': False,
+        'language_code': 'et',
+        'name': 'Estonian',
+        'name_local': u'eesti'},
+ 'eu': {'bidi': False,
+        'language_code': 'eu',
+        'name': 'Basque',
+        'name_local': u'Basque'},
+ 'fa': {'bidi': True,
+        'language_code': 'fa',
+        'name': 'Persian',
+        'name_local': u'\u0641\u0627\u0631\u0633\u06cc'},
+ 'fi': {'bidi': False,
+        'language_code': 'fi',
+        'name': 'Finnish',
+        'name_local': u'suomi'},
+ 'fr': {'bidi': False,
+        'language_code': 'fr',
+        'name': 'French',
+        'name_local': u'Fran\xe7ais'},
+ 'fy-nl': {'bidi': False,
+           'language_code': 'fy-nl',
+           'name': 'Frisian',
+           'name_local': u'Frisian'},
+ 'ga': {'bidi': False,
+        'language_code': 'ga',
+        'name': 'Irish',
+        'name_local': u'Gaeilge'},
+ 'gl': {'bidi': False,
+        'language_code': 'gl',
+        'name': 'Galician',
+        'name_local': u'galego'},
+ 'he': {'bidi': True,
+        'language_code': 'he',
+        'name': 'Hebrew',
+        'name_local': u'\u05e2\u05d1\u05e8\u05d9\u05ea'},
+ 'hi': {'bidi': False,
+        'language_code': 'hi',
+        'name': 'Hindi',
+        'name_local': u'Hindi'},
+ 'hr': {'bidi': False,
+        'language_code': 'hr',
+        'name': 'Croatian',
+        'name_local': u'Hrvatski'},
+ 'hu': {'bidi': False,
+        'language_code': 'hu',
+        'name': 'Hungarian',
+        'name_local': u'Magyar'},
+ 'id': {'bidi': False,
+        'language_code': 'id',
+        'name': 'Indonesian',
+        'name_local': u'Bahasa Indonesia'},
+ 'is': {'bidi': False,
+        'language_code': 'is',
+        'name': 'Icelandic',
+        'name_local': u'\xcdslenska'},
+ 'it': {'bidi': False,
+        'language_code': 'it',
+        'name': 'Italian',
+        'name_local': u'italiano'},
+ 'ja': {'bidi': False,
+        'language_code': 'ja',
+        'name': 'Japanese',
+        'name_local': u'\u65e5\u672c\u8a9e'},
+ 'ka': {'bidi': False,
+        'language_code': 'ka',
+        'name': 'Georgian',
+        'name_local': u'\u10e5\u10d0\u10e0\u10d7\u10e3\u10da\u10d8'},
+ 'km': {'bidi': False,
+        'language_code': 'km',
+        'name': 'Khmer',
+        'name_local': u'Khmer'},
+ 'kn': {'bidi': False,
+        'language_code': 'kn',
+        'name': 'Kannada',
+        'name_local': u'Kannada'},
+ 'ko': {'bidi': False,
+        'language_code': 'ko',
+        'name': 'Korean',
+        'name_local': u'\ud55c\uad6d\uc5b4'},
+ 'lt': {'bidi': False,
+        'language_code': 'lt',
+        'name': 'Lithuanian',
+        'name_local': u'Lithuanian'},
+ 'lv': {'bidi': False,
+        'language_code': 'lv',
+        'name': 'Latvian',
+        'name_local': u'latvie\u0161u'},
+ 'mk': {'bidi': False,
+        'language_code': 'mk',
+        'name': 'Macedonian',
+        'name_local': u'\u041c\u0430\u043a\u0435\u0434\u043e\u043d\u0441\u043a\u0438'},
+ 'ml': {'bidi': False,
+        'language_code': 'ml',
+        'name': 'Malayalam',
+        'name_local': u'Malayalam'},
+ 'mn': {'bidi': False,
+        'language_code': 'mn',
+        'name': 'Mongolian',
+        'name_local': u'Mongolian'},
+ 'nb': {'bidi': False,
+        'language_code': 'nb',
+        'name': 'Norwegian Bokmal',
+        'name_local': u'Norsk (bokm\xe5l)'},
+ 'nl': {'bidi': False,
+        'language_code': 'nl',
+        'name': 'Dutch',
+        'name_local': u'Nederlands'},
+ 'nn': {'bidi': False,
+        'language_code': 'nn',
+        'name': 'Norwegian Nynorsk',
+        'name_local': u'Norsk (nynorsk)'},
+ 'no': {'bidi': False,
+        'language_code': 'no',
+        'name': 'Norwegian',
+        'name_local': u'Norsk'},
+ 'pa': {'bidi': False,
+        'language_code': 'pa',
+        'name': 'Punjabi',
+        'name_local': u'Punjabi'},
+ 'pl': {'bidi': False,
+        'language_code': 'pl',
+        'name': 'Polish',
+        'name_local': u'polski'},
+ 'pt': {'bidi': False,
+        'language_code': 'pt',
+        'name': 'Portuguese',
+        'name_local': u'Portugu\xeas'},
+ 'pt-br': {'bidi': False,
+           'language_code': 'pt-br',
+           'name': 'Brazilian Portuguese',
+           'name_local': u'Portugu\xeas Brasileiro'},
+ 'ro': {'bidi': False,
+        'language_code': 'ro',
+        'name': 'Romanian',
+        'name_local': u'Rom\xe2n\u0103'},
+ 'ru': {'bidi': False,
+        'language_code': 'ru',
+        'name': 'Russian',
+        'name_local': u'\u0420\u0443\u0441\u0441\u043a\u0438\u0439'},
+ 'sk': {'bidi': False,
+        'language_code': 'sk',
+        'name': 'Slovak',
+        'name_local': u'slovensk\xfd'},
+ 'sl': {'bidi': False,
+        'language_code': 'sl',
+        'name': 'Slovenian',
+        'name_local': u'Sloven\u0161\u010dina'},
+ 'sq': {'bidi': False,
+        'language_code': 'sq',
+        'name': 'Albanian',
+        'name_local': u'Albanian'},
+ 'sr': {'bidi': False,
+        'language_code': 'sr',
+        'name': 'Serbian',
+        'name_local': u'\u0441\u0440\u043f\u0441\u043a\u0438'},
+ 'sr-latn': {'bidi': False,
+             'language_code': 'sr-latn',
+             'name': 'Serbian Latin',
+             'name_local': u'srpski (latinica)'},
+ 'sv': {'bidi': False,
+        'language_code': 'sv',
+        'name': 'Swedish',
+        'name_local': u'Svenska'},
+ 'ta': {'bidi': False,
+        'language_code': 'ta',
+        'name': 'Tamil',
+        'name_local': u'\u0ba4\u0bae\u0bbf\u0bb4\u0bcd'},
+ 'te': {'bidi': False,
+        'language_code': 'te',
+        'name': 'Telugu',
+        'name_local': u'\u0c24\u0c46\u0c32\u0c41\u0c17\u0c41'},
+ 'th': {'bidi': False,
+        'language_code': 'th',
+        'name': 'Thai',
+        'name_local': u'Thai'},
+ 'tr': {'bidi': False,
+        'language_code': 'tr',
+        'name': 'Turkish',
+        'name_local': u'T\xfcrk\xe7e'},
+ 'uk': {'bidi': False,
+        'language_code': 'uk',
+        'name': 'Ukrainian',
+        'name_local': u'\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430'},
+ 'vi': {'bidi': False,
+        'language_code': 'vi',
+        'name': 'Vietnamese',
+        'name_local': u'Vietnamese'},
+ 'zh-cn': {'bidi': False,
+           'language_code': 'zh-cn',
+           'name': 'Simplified Chinese',
+           'name_local': u'\u7b80\u4f53\u4e2d\u6587'},
+ 'zh-tw': {'bidi': False,
+           'language_code': 'zh-tw',
+           'name': 'Traditional Chinese',
+           'name_local': u'\u7e41\u9ad4\u4e2d\u6587'}
+}
diff -r f700b623b8db -r 07ed5e4869c0 django/core/management/commands/makelanginfo.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/django/core/management/commands/makelanginfo.py	Thu Dec 02 12:22:29 2010 -0300
@@ -0,0 +1,79 @@
+import os
+from pprint import pformat
+import sys
+
+from django.core.management.base import CommandError, NoArgsCommand
+from django.utils import translation
+
+LANGUAGE_INFO_PY_FILE = 'language_info.py'
+
+TEMPLATE = """\
+# This file was generated with the makelanginfo Django management command and
+# is not meant to be edited by hand. For new translations, add them following
+# the steps described in the internals/contributing.txt document ('Submitting
+# and maintaining translations' section) and finally re-run
+# 'django-admin.py makelanginfo' to generate this file.
+
+lang_info = {
+%s
+}
+"""
+
+def make_language_info(verbosity='1'):
+    # Need to ensure that the i18n framework is enabled
+    from django.conf import settings
+    if settings.configured:
+        settings.USE_I18N = True
+    else:
+        settings.configure(USE_I18N = True)
+
+    invoked_for_django = False
+    if os.path.isdir(os.path.join('conf', 'locale')):
+        localedir = os.path.abspath(os.path.join('conf', 'locale'))
+        invoked_for_django = True
+    elif os.path.isdir('locale'):
+        localedir = os.path.abspath('locale')
+    else:
+        raise CommandError("This command should be run from the django/ directory of the Django source code tree. If you did indeed run it from there, maybe you are just missing the conf/locale?")
+
+    target_path = os.path.join(localedir, LANGUAGE_INFO_PY_FILE)
+
+    write_ok = True
+    # On Jython there is no os.access()
+    if not sys.platform.startswith('java'):
+        if os.access(target_path, os.F_OK) and not os.access(target_path, os.W_OK):
+            write_ok = False
+    if write_ok:
+        try:
+            f = open(target_path, 'w')
+        except IOError:
+            write_ok = False
+
+    if not write_ok:
+        raise CommandError("Can't open the %s file for writing, make sure you have the permissions to create or replace that file. Aborting the operation." % target_path)
+
+    if verbosity >= 1:
+        print 'Writing language information to %s' % target_path
+
+    lang_info = {}
+    for code, name in settings.LANGUAGES:
+        translation.activate(code)
+        lang_info[code] = {
+            "language_code": code,
+            "name": name,
+            "name_local": translation.ugettext(name),
+            "bidi": code in settings.LANGUAGES_BIDI
+        }
+    f.write(TEMPLATE % pformat(lang_info)[1:-1])
+    f.close()
+
+
+class Command(NoArgsCommand):
+    help = "Generates the data structures containing metadata about the available languages needed by some Django I18N runtime tools. This is a Django internal maintenance command."
+
+    requires_model_validation = False
+    can_import_settings = False
+
+    def handle_noargs(self, **options):
+        verbosity = int(options.get('verbosity'))
+        make_language_info(verbosity)
diff -r f700b623b8db -r 07ed5e4869c0 django/templatetags/i18n.py
--- a/django/templatetags/i18n.py	Wed Dec 01 15:06:25 2010 +0000
+++ b/django/templatetags/i18n.py	Thu Dec 02 12:22:29 2010 -0300
@@ -18,6 +18,34 @@
         context[self.variable] = [(k, translation.ugettext(v)) for k, v in settings.LANGUAGES]
         return ''
 
+class GetLanguageInfoNode(Node):
+    def __init__(self, lang_code, variable):
+        self.lang_code = Variable(lang_code)
+        self.variable = variable
+
+    def render(self, context):
+        lang_code = self.lang_code.resolve(context)
+        context[self.variable] = translation.get_language_info(lang_code)
+        return ''
+
+class GetLanguageInfoListNode(Node):
+    def __init__(self, languages, variable):
+        self.languages = Variable(languages)
+        self.variable = variable
+
+    def get_language_info(self, language):
+        # ``language`` is either a language code string or a sequence with the
+        # language code as its first item
+        if len(language[0]) > 1:
+            return translation.get_language_info(language[0])
+        else:
+            return translation.get_language_info(str(language))
+
+    def render(self, context):
+        languages = self.languages.resolve(context)
+        context[self.variable] = [self.get_language_info(l) for l in languages]
+        return ''
+
 class GetCurrentLanguageNode(Node):
     def __init__(self, variable):
         self.variable = variable
@@ -109,6 +137,55 @@
         raise TemplateSyntaxError("'get_available_languages' requires 'as variable' (got %r)" % args)
     return GetAvailableLanguagesNode(args[2])
 
+def do_get_language_info(parser, token):
+    """
+    This will store the language information dictionary for the given language
+    code in a context variable.
+
+    Usage::
+
+        {% get_language_info for language_code as l %}
+        {{ l.language_code }}
+        {{ l.name }}
+        {{ l.name_local }}
+        {{ l.bidi|yesno:"bi-directional,uni-directional" }}
+    """
+    args = token.contents.split()
+    if len(args) != 5 or args[1] != 'for' or args[3] != 'as':
+        raise TemplateSyntaxError("'%s' requires 'for string as variable' (got %r)" % (args[0], args[1:]))
+    return GetLanguageInfoNode(args[2], args[4])
+
+def do_get_language_info_list(parser, token):
+    """
+    This will store a list of language information dictionaries for the given
+    language codes in a context variable.  The language codes can be specified
+    ither as a list of strings or a settings.LANGUAGES style tuple (or any
+    sequence of sequences whose first items are language codes).
+
+    Usage::
+
+        {% get_language_info_list for LANGUAGES as langs %}
+        {% for l in langs %}
+          {{ l.language_code }}
+          {{ l.name }}
+          {{ l.name_local }}
+          {{ l.bidi|yesno:"bi-directional,uni-directional" }}
+        {% endfor %}
+    """
+    args = token.contents.split()
+    if len(args) != 5 or args[1] != 'for' or args[3] != 'as':
+        raise TemplateSyntaxError("'%s' requires 'for sequence as variable' (got %r)" % (args[0], args[1:]))
+    return GetLanguageInfoListNode(args[2], args[4])
+
+def language_name(lang_code):
+    return translation.get_language_info(lang_code)['name']
+
+def language_name_local(lang_code):
+    return translation.get_language_info(lang_code)['name_local']
+
+def language_bidi(lang_code):
+    return translation.get_language_info(lang_code)['bidi']
+
 def do_get_current_language(parser, token):
     """
     This will store the current language in the context.
@@ -269,7 +346,13 @@
             counter)
 
 register.tag('get_available_languages', do_get_available_languages)
+register.tag('get_language_info', do_get_language_info)
+register.tag('get_language_info_list', do_get_language_info_list)
 register.tag('get_current_language', do_get_current_language)
 register.tag('get_current_language_bidi', do_get_current_language_bidi)
 register.tag('trans', do_translate)
 register.tag('blocktrans', do_block_translate)
+
+register.filter(language_name)
+register.filter(language_name_local)
+register.filter(language_bidi)
diff -r f700b623b8db -r 07ed5e4869c0 django/utils/translation/__init__.py
--- a/django/utils/translation/__init__.py	Wed Dec 01 15:06:25 2010 +0000
+++ b/django/utils/translation/__init__.py	Thu Dec 02 12:22:29 2010 -0300
@@ -11,7 +11,7 @@
         'get_partial_date_formats', 'check_for_language', 'to_locale',
         'get_language_from_request', 'templatize', 'ugettext', 'ugettext_lazy',
         'ungettext', 'ungettext_lazy', 'pgettext', 'pgettext_lazy',
-        'npgettext', 'npgettext_lazy', 'deactivate_all']
+        'npgettext', 'npgettext_lazy', 'deactivate_all', 'get_language_info']
 
 # Here be dragons, so a short explanation of the logic won't hurt:
 # We are trying to solve two problems: (1) access settings, in particular
@@ -117,3 +117,13 @@
     """
     return u''.join([force_unicode(s) for s in strings])
 string_concat = lazy(_string_concat, unicode)
+
+def get_language_info(lang_code):
+    try:
+        from django.conf.locale.language_info import lang_info
+    except ImportError:
+        raise ImportError("Module django.conf.locale.language_info not found. Run django-admin.py makelanginfo to generate it.")
+    try:
+        return lang_info[lang_code]
+    except KeyError:
+        raise KeyError("Unknown language code %r. If you have added a new translation, remember to add the language to the LANGUAGES setting and update the django/conf/locale/language_info.py file by running django-admin.py makelanginfo." % lang_code)
diff -r f700b623b8db -r 07ed5e4869c0 docs/man/django-admin.1
--- a/docs/man/django-admin.1	Wed Dec 01 15:06:25 2010 +0000
+++ b/docs/man/django-admin.1	Thu Dec 02 12:22:29 2010 -0300
@@ -60,6 +60,12 @@
 .B sqlall
 for the given app(s) in the current database.
 .TP
+.BI "makelanginfo
+Extracts metadata about the languages that have translations available in
+Django by reading their PO message files and stores that information on disk.
+This command is used by the Django developers to make sure this information is
+up to date before a release.
+.TP
 .BI "makemessages [" "\-\-locale=LOCALE" "] [" "\-\-domain=DOMAIN" "] [" "\-\-extension=EXTENSION" "] [" "\-\-all" "] [" "\-\-symlinks" "] [" "\-\-ignore=PATTERN" "] [" "\-\-no\-default\-ignore" "] [" "\-\-no\-wrap" "]"
 Runs over the entire source tree of the current directory and pulls out all
 strings marked for translation. It creates (or updates) a message file in the
@@ -99,7 +105,7 @@
 Prints the custom SQL statements for the given app name(s).
 .TP
 .BI "sqlflush [" "appname ..." "]"
-Prints the SQL statements that would be executed for the "flush" 
+Prints the SQL statements that would be executed for the "flush"
 command.
 .TP
 .BI "sqlindexes [" "appname ..." "]"
@@ -144,7 +150,7 @@
 Show program's version number and exit.
 .TP
 .I \-h, \-\-help
-Show this help message and exit.
+Show an usage help message and exit.
 .TP
 .I \-\-settings=SETTINGS
 Python path to settings module, e.g. "myproject.settings.main". If
@@ -210,8 +216,9 @@
 .TP
 .I \-a, \-\-all
 Process all available locales when using makemessages..SH "ENVIRONMENT"
-.TP
-.I DJANGO_SETTINGS_MODULE
+.SH "ENVIRONMENT VARIABLES"
+
+.IP DJANGO_SETTINGS_MODULE
 In the absence of the
 .BI \-\-settings
 option, this environment variable defines the settings module to be read.
@@ -219,7 +226,6 @@
 .I \-\-database=DB
 Used to specify the database on which a command will operate. If not
 specified, this option will default to an alias of "default".
-.TP
 
 .SH "SEE ALSO"
 Full descriptions of all these options, with examples, as well as documentation
diff -r f700b623b8db -r 07ed5e4869c0 docs/ref/django-admin.txt
--- a/docs/ref/django-admin.txt	Wed Dec 01 15:06:25 2010 +0000
+++ b/docs/ref/django-admin.txt	Thu Dec 02 12:22:29 2010 -0300
@@ -401,6 +401,24 @@
 have specified that you want to load data onto the ``master``
 database.
 
+makelanginfo
+------------
+
+.. django-admin:: makelanginfo
+
+.. versionadded:: 1.3
+
+Extracts metadata about the languages that have translations available in
+Django (like if they are written bidirectionally, their names as represented
+both in English and in themselves using the appropiate alphabet, etc.) i.e.
+those present in the :setting:`LANGUAGES` setting. To achieve that it reads
+their PO message files, and updates the ``django.conf.locale.language_info``
+module on disk (for performance reasons) with an appropiate structure
+containing that information.
+
+This command is used by the Django developers to make sure this information is
+up to date before a release.
+
 makemessages
 ------------
 
diff -r f700b623b8db -r 07ed5e4869c0 docs/topics/i18n/internationalization.txt
--- a/docs/topics/i18n/internationalization.txt	Wed Dec 01 15:06:25 2010 +0000
+++ b/docs/topics/i18n/internationalization.txt	Thu Dec 02 12:22:29 2010 -0300
@@ -379,6 +379,27 @@
 input is a proper string, then add support for lazy translation objects at the
 end.
 
+Localized names of languages
+============================
+
+The ``get_language_info()`` function provides detailed information about
+languages::
+
+    >>> from django.utils.translation import get_language_info
+    >>> li = get_language_info('de')
+    >>> print li['name'], li['name_local'], li['bidi']
+    German Deutsch False
+
+The ``name`` and ``name_local`` attributes of the dictionary contain the name of
+the language in English and in the language itself, respectively.  The ``bidi``
+attribute is True only for bi-directional languages.
+
+The source of the language information is the ``django.conf.locale.language_info``
+module.  If you add a new translation, you should run the :djadmin:`makelanginfo`
+Django management command to add the information for the new language to it.
+
+Similar access to this information is available for template code. See below.
+
 .. _specifying-translation-strings-in-template-code:
 
 Specifying translation strings: In template code
@@ -518,6 +539,46 @@
     translator might translate the string ``"yes,no"`` as ``"ja,nein"``
     (keeping the comma intact).
 
+You can also retrieve information about any of the available languages using
+provided template tags and filters. To get information about a single language,
+use the ``{% get_language_info %}`` tag::
+
+    {% get_language_info for LANGUAGE_CODE as lang %}
+    {% get_language_info for "pl" as lang %}
+
+You can then access the information::
+
+    Language code: {{ lang.language_code }}<br />
+    Name of language: {{ lang.name_local }}<br />
+    Name in English: {{ lang.name }}<br />
+    Bi-directional: {{ lang.bidi }}
+
+You can also use the ``{% get_language_info_list %}`` template tag to retrieve
+information for a list of languages (e.g. active languages as specified in
+:setting:`LANGUAGES`).  See `The set_language redirect view`_ for an example
+of how to display a language selector using ``{% get_language_info_list %}``.
+
+In addition to :setting:`LANGUAGES` -style nested tuples,
+``{% get_language_info_list %}`` supports simple lists of language codes.  If
+you do this in your view:
+
+.. code-block:: python
+
+    return render_to_response('mytemplate.html',
+               {'available_languages': ['en', 'es', 'fr']},
+               RequestContext(request))
+
+you can iterate over those languages in the template::
+
+  {% get_language_info_list available_languages %}
+  {% for l in available_languages %} ... {% endfor %}
+
+There are also simple filters available for convenience:
+
+    * ``{{ LANGUAGE_CODE|language_name }}``  ("German")
+    * ``{{ LANGUAGE_CODE|language_name_local }}`` ("Deutsch")
+    * ``{{ LANGUAGE_CODE|bidi }}`` (False)
+
 .. _Django templates: ../templates_python/
 
 Specifying translation strings: In JavaScript code
@@ -579,7 +640,7 @@
 signs in the URL. This is especially useful if your pages use code from
 different apps and this changes often and you don't want to pull in one big
 catalog file. As a security measure, these values can only be either
-``django.conf`` or any package from the ``INSTALLED_APPS`` setting.
+``django.conf`` or any package from the :setting:`INSTALLED_APPS` setting.
 
 Using the JavaScript translation catalog
 ----------------------------------------
@@ -654,7 +715,7 @@
 parameter set in request. If session support is enabled, the view
 saves the language choice in the user's session. Otherwise, it saves the
 language choice in a cookie that is by default named ``django_language``.
-(The name can be changed through the ``LANGUAGE_COOKIE_NAME`` setting.)
+(The name can be changed through the :setting:`LANGUAGE_COOKIE_NAME` setting.)
 
 After setting the language choice, Django redirects the user, following this
 algorithm:
diff -r f700b623b8db -r 07ed5e4869c0 tests/regressiontests/i18n/tests.py
--- a/tests/regressiontests/i18n/tests.py	Wed Dec 01 15:06:25 2010 +0000
+++ b/tests/regressiontests/i18n/tests.py	Thu Dec 02 12:22:29 2010 -0300
@@ -7,12 +7,14 @@
 
 from django.conf import settings
 from django.template import Template, Context
-from django.test import TestCase
 from django.utils.formats import get_format, date_format, time_format, localize, localize_input, iter_format_modules
+from django.utils.importlib import import_module
 from django.utils.numberformat import format as nformat
 from django.utils.safestring import mark_safe, SafeString, SafeUnicode
-from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy, pgettext, npgettext, to_locale
-from django.utils.importlib import import_module
+from django.utils.translation import (ugettext, ugettext_lazy, activate,
+        deactivate, gettext_lazy, pgettext, npgettext, to_locale,
+        get_language_info)
+from django.utils.unittest import TestCase
 
 
 from forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm
@@ -708,3 +710,12 @@
         c.save()
         c.name = SafeString(u'Iñtërnâtiônàlizætiøn1'.encode('utf-8'))
         c.save()
+
+
+class TestLanguageInfo(TestCase):
+    def test_localized_language_info(self):
+        li = get_language_info('de')
+        self.assertEqual(li['language_code'], 'de')
+        self.assertEqual(li['name_local'], u'Deutsch')
+        self.assertEqual(li['name'], 'German')
+        self.assertEqual(li['bidi'], False)
diff -r f700b623b8db -r 07ed5e4869c0 tests/regressiontests/templates/tests.py
--- a/tests/regressiontests/templates/tests.py	Wed Dec 01 15:06:25 2010 +0000
+++ b/tests/regressiontests/templates/tests.py	Thu Dec 02 12:22:29 2010 -0300
@@ -1149,6 +1149,14 @@
             # translation of singular form in russian (#14126)
             'i18n27': ('{% load i18n %}{% blocktrans count number as counter %}1 result{% plural %}{{ counter }} results{% endblocktrans %}', {'number': 1, 'LANGUAGE_CODE': 'ru'}, u'1 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442'),
 
+            # retrieving language information
+            'i18n28': ('{% 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'),
+            'i18n29': ('{% 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'),
+            'i18n30': ('{% 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']}, u'it: Italian/italiano bidi=False; no: Norwegian/Norsk bidi=False; '),
+            'i18n31': ('{% 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'))}, u'sl: Slovenian/Sloven\u0161\u010dina bidi=False; fa: Persian/\u0641\u0627\u0631\u0633\u06cc bidi=True; '),
+            'i18n32': ('{% load i18n %}{{ "hu"|language_name }} {{ "hu"|language_name_local }} {{ "hu"|language_bidi }}', {}, u'Hungarian Magyar False'),
+            'i18n33': ('{% load i18n %}{{ langcode|language_name }} {{ langcode|language_name_local }} {{ langcode|language_bidi }}', {'langcode': 'nl'}, u'Dutch Nederlands False'),
+
             ### HANDLING OF TEMPLATE_STRING_IF_INVALID ###################################
 
             'invalidstr01': ('{{ var|default:"Foo" }}', {}, ('Foo','INVALID')),
