We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
LanguageError: IOError: [Errno 2] No translation file found for domain: 'ckan'
Hi, 

We are noticing some 500 errors are being generated when  /e or /n are being passed instead of /en

Passing other letters such as /a /b /c will trigger a 404
### CKAN Version if known (or site URL)

Ckan 2.3.X -  2.5.2+
### Please describe the expected behaviour

http://demo.ckan.org/en = 200
http://demo.ckan.org/e = 404
http://demo.ckan.org/n = 404
### Please describe the actual behaviour

http://demo.ckan.org/en = 200
http://demo.ckan.org/e = 500
http://demo.ckan.org/n = 500
### What steps can be taken to reproduce the issue?

pass /e or /n in any ckan instance url and you can trigger a 500 internal server error.

http://demo.ckan.org/en/dataset = 200

http://demo.ckan.org/e/dataset = 500


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ckan/lib/i18n.py`
Content:
```
1 # encoding: utf-8
2 
3 import os
4 
5 from babel import Locale
6 from babel.core import (LOCALE_ALIASES,
7                         get_locale_identifier,
8                         UnknownLocaleError)
9 from babel.support import Translations
10 from paste.deploy.converters import aslist
11 from pylons import i18n
12 import pylons
13 
14 
15 from ckan.common import config
16 import ckan.i18n
17 from ckan.plugins import PluginImplementations
18 from ckan.plugins.interfaces import ITranslation
19 
20 # Default Portuguese language to Brazilian territory, since
21 # we don't have a Portuguese territory translation currently.
22 LOCALE_ALIASES['pt'] = 'pt_BR'
23 
24 
25 def get_locales_from_config():
26     ''' despite the name of this function it gets the locales defined by
27     the config AND also the locals available subject to the config. '''
28     locales_offered = config.get('ckan.locales_offered', '').split()
29     filtered_out = config.get('ckan.locales_filtered_out', '').split()
30     locale_default = config.get('ckan.locale_default', 'en')
31     locale_order = config.get('ckan.locale_order', '').split()
32     known_locales = get_locales()
33     all_locales = (set(known_locales) |
34                    set(locales_offered) |
35                    set(locale_order) |
36                    set(locale_default))
37     all_locales -= set(filtered_out)
38     return all_locales
39 
40 
41 def _get_locales():
42     # FIXME this wants cleaning up and merging with get_locales_from_config()
43     assert not config.get('lang'), \
44         ('"lang" config option not supported - please use ckan.locale_default '
45          'instead.')
46     locales_offered = config.get('ckan.locales_offered', '').split()
47     filtered_out = config.get('ckan.locales_filtered_out', '').split()
48     locale_default = config.get('ckan.locale_default', 'en')
49     locale_order = config.get('ckan.locale_order', '').split()
50 
51     locales = ['en']
52     if config.get('ckan.i18n_directory'):
53         i18n_path = os.path.join(config.get('ckan.i18n_directory'), 'i18n')
54     else:
55         i18n_path = os.path.dirname(ckan.i18n.__file__)
56 
57     # For every file in the ckan i18n directory see if babel can understand
58     # the locale. If yes, add it to the available locales
59     for locale in os.listdir(i18n_path):
60         try:
61             Locale.parse(locale)
62             locales.append(locale)
63         except (ValueError, UnknownLocaleError):
64             # Babel does not know how to make a locale out of this.
65             # This is fine since we are passing all files in the
66             # ckan.i18n_directory here which e.g. includes the __init__.py
67             pass
68 
69     assert locale_default in locales, \
70         'default language "%s" not available' % locale_default
71 
72     locale_list = []
73     for locale in locales:
74         # no duplicates
75         if locale in locale_list:
76             continue
77         # if offered locales then check locale is offered
78         if locales_offered and locale not in locales_offered:
79             continue
80         # remove if filtered out
81         if locale in filtered_out:
82             continue
83         # ignore the default as it will be added first
84         if locale == locale_default:
85             continue
86         locale_list.append(locale)
87     # order the list if specified
88     ordered_list = [locale_default]
89     for locale in locale_order:
90         if locale in locale_list:
91             ordered_list.append(locale)
92             # added so remove from our list
93             locale_list.remove(locale)
94     # add any remaining locales not ordered
95     ordered_list += locale_list
96 
97     return ordered_list
98 
99 available_locales = None
100 locales = None
101 locales_dict = None
102 _non_translated_locals = None
103 
104 
105 def get_locales():
106     ''' Get list of available locales
107     e.g. [ 'en', 'de', ... ]
108     '''
109     global locales
110     if not locales:
111         locales = _get_locales()
112     return locales
113 
114 
115 def non_translated_locals():
116     ''' These are the locales that are available but for which there are
117     no translations. returns a list like ['en', 'de', ...] '''
118     global _non_translated_locals
119     if not _non_translated_locals:
120         locales = config.get('ckan.locale_order', '').split()
121         _non_translated_locals = [x for x in locales if x not in get_locales()]
122     return _non_translated_locals
123 
124 
125 def get_locales_dict():
126     ''' Get a dict of the available locales
127     e.g.  { 'en' : Locale('en'), 'de' : Locale('de'), ... } '''
128     global locales_dict
129     if not locales_dict:
130         locales = _get_locales()
131         locales_dict = {}
132         for locale in locales:
133             locales_dict[str(locale)] = Locale.parse(locale)
134     return locales_dict
135 
136 
137 def get_available_locales():
138     ''' Get a list of the available locales
139     e.g.  [ Locale('en'), Locale('de'), ... ] '''
140     global available_locales
141     if not available_locales:
142         available_locales = []
143         for locale in get_locales():
144             # Add the short names for the locales. This equals the filename
145             # of the ckan translation files as opposed to the long name
146             # that includes the script which is generated by babel
147             # so e.g. `zn_CH` instead of `zn_Hans_CH` this is needed
148             # to properly construct urls with url_for
149             parsed_locale = Locale.parse(locale)
150             parsed_locale.short_name = locale
151 
152             # Add the full identifier (eg `pt_BR`) to the locale classes,
153             # as it does not offer a way of accessing it directly
154             parsed_locale.identifier = \
155                 get_identifier_from_locale_class(parsed_locale)
156             available_locales.append(parsed_locale)
157     return available_locales
158 
159 
160 def get_identifier_from_locale_class(locale):
161     return get_locale_identifier(
162         (locale.language,
163          locale.territory,
164          locale.script,
165          locale.variant))
166 
167 
168 def _set_lang(lang):
169     ''' Allows a custom i18n directory to be specified.
170     Creates a fake config file to pass to pylons.i18n.set_lang, which
171     sets the Pylons root path to desired i18n_directory.
172     This is needed as Pylons will only look for an i18n directory in
173     the application root.'''
174     if config.get('ckan.i18n_directory'):
175         fake_config = {'pylons.paths': {'root': config['ckan.i18n_directory']},
176                        'pylons.package': config['pylons.package']}
177         i18n.set_lang(lang, config=fake_config, class_=Translations)
178     else:
179         i18n.set_lang(lang, class_=Translations)
180 
181 
182 def handle_request(request, tmpl_context):
183     ''' Set the language for the request '''
184     lang = request.environ.get('CKAN_LANG') or \
185         config.get('ckan.locale_default', 'en')
186     if lang != 'en':
187         set_lang(lang)
188 
189     for plugin in PluginImplementations(ITranslation):
190         if lang in plugin.i18n_locales():
191             _add_extra_translations(plugin.i18n_directory(), lang,
192                                     plugin.i18n_domain())
193 
194     extra_directory = config.get('ckan.i18n.extra_directory')
195     extra_domain = config.get('ckan.i18n.extra_gettext_domain')
196     extra_locales = aslist(config.get('ckan.i18n.extra_locales'))
197     if extra_directory and extra_domain and extra_locales:
198         if lang in extra_locales:
199             _add_extra_translations(extra_directory, lang, extra_domain)
200 
201     tmpl_context.language = lang
202     return lang
203 
204 
205 def _add_extra_translations(dirname, locales, domain):
206     translator = Translations.load(dirname=dirname, locales=locales,
207                                    domain=domain)
208     try:
209         pylons.translator.merge(translator)
210     except AttributeError:
211         # this occurs when an extension has 'en' translations that
212         # replace the default strings. As set_lang has not been run,
213         # pylons.translation is the NullTranslation, so we have to
214         # replace the StackedObjectProxy ourselves manually.
215         environ = pylons.request.environ
216         environ['pylons.pylons'].translator = translator
217         if 'paste.registry' in environ:
218             environ['paste.registry'].replace(pylons.translator,
219                                               translator)
220 
221 
222 def get_lang():
223     ''' Returns the current language. Based on babel.i18n.get_lang but
224     works when set_lang has not been run (i.e. still in English). '''
225     langs = i18n.get_lang()
226     if langs:
227         return langs[0]
228     else:
229         return 'en'
230 
231 
232 def set_lang(language_code):
233     ''' Wrapper to pylons call '''
234     if language_code in non_translated_locals():
235         language_code = config.get('ckan.locale_default', 'en')
236     if language_code != 'en':
237         _set_lang(language_code)
238
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/ckan/lib/i18n.py b/ckan/lib/i18n.py
--- a/ckan/lib/i18n.py
+++ b/ckan/lib/i18n.py
@@ -27,8 +27,9 @@
     the config AND also the locals available subject to the config. '''
     locales_offered = config.get('ckan.locales_offered', '').split()
     filtered_out = config.get('ckan.locales_filtered_out', '').split()
-    locale_default = config.get('ckan.locale_default', 'en')
+    locale_default = [config.get('ckan.locale_default', 'en')]
     locale_order = config.get('ckan.locale_order', '').split()
+
     known_locales = get_locales()
     all_locales = (set(known_locales) |
                    set(locales_offered) |

{"golden_diff": "diff --git a/ckan/lib/i18n.py b/ckan/lib/i18n.py\n--- a/ckan/lib/i18n.py\n+++ b/ckan/lib/i18n.py\n@@ -27,8 +27,9 @@\n     the config AND also the locals available subject to the config. '''\n     locales_offered = config.get('ckan.locales_offered', '').split()\n     filtered_out = config.get('ckan.locales_filtered_out', '').split()\n-    locale_default = config.get('ckan.locale_default', 'en')\n+    locale_default = [config.get('ckan.locale_default', 'en')]\n     locale_order = config.get('ckan.locale_order', '').split()\n+\n     known_locales = get_locales()\n     all_locales = (set(known_locales) |\n                    set(locales_offered) |\n", "issue": "LanguageError: IOError: [Errno 2] No translation file found for domain: 'ckan'\nHi, \n\nWe are noticing some 500 errors are being generated when  /e or /n are being passed instead of /en\n\nPassing other letters such as /a /b /c will trigger a 404\n### CKAN Version if known (or site URL)\n\nCkan 2.3.X -  2.5.2+\n### Please describe the expected behaviour\n\nhttp://demo.ckan.org/en = 200\nhttp://demo.ckan.org/e = 404\nhttp://demo.ckan.org/n = 404\n### Please describe the actual behaviour\n\nhttp://demo.ckan.org/en = 200\nhttp://demo.ckan.org/e = 500\nhttp://demo.ckan.org/n = 500\n### What steps can be taken to reproduce the issue?\n\npass /e or /n in any ckan instance url and you can trigger a 500 internal server error.\n\nhttp://demo.ckan.org/en/dataset = 200\n\nhttp://demo.ckan.org/e/dataset = 500\n\n", "before_files": [{"content": "# encoding: utf-8\n\nimport os\n\nfrom babel import Locale\nfrom babel.core import (LOCALE_ALIASES,\n                        get_locale_identifier,\n                        UnknownLocaleError)\nfrom babel.support import Translations\nfrom paste.deploy.converters import aslist\nfrom pylons import i18n\nimport pylons\n\n\nfrom ckan.common import config\nimport ckan.i18n\nfrom ckan.plugins import PluginImplementations\nfrom ckan.plugins.interfaces import ITranslation\n\n# Default Portuguese language to Brazilian territory, since\n# we don't have a Portuguese territory translation currently.\nLOCALE_ALIASES['pt'] = 'pt_BR'\n\n\ndef get_locales_from_config():\n    ''' despite the name of this function it gets the locales defined by\n    the config AND also the locals available subject to the config. '''\n    locales_offered = config.get('ckan.locales_offered', '').split()\n    filtered_out = config.get('ckan.locales_filtered_out', '').split()\n    locale_default = config.get('ckan.locale_default', 'en')\n    locale_order = config.get('ckan.locale_order', '').split()\n    known_locales = get_locales()\n    all_locales = (set(known_locales) |\n                   set(locales_offered) |\n                   set(locale_order) |\n                   set(locale_default))\n    all_locales -= set(filtered_out)\n    return all_locales\n\n\ndef _get_locales():\n    # FIXME this wants cleaning up and merging with get_locales_from_config()\n    assert not config.get('lang'), \\\n        ('\"lang\" config option not supported - please use ckan.locale_default '\n         'instead.')\n    locales_offered = config.get('ckan.locales_offered', '').split()\n    filtered_out = config.get('ckan.locales_filtered_out', '').split()\n    locale_default = config.get('ckan.locale_default', 'en')\n    locale_order = config.get('ckan.locale_order', '').split()\n\n    locales = ['en']\n    if config.get('ckan.i18n_directory'):\n        i18n_path = os.path.join(config.get('ckan.i18n_directory'), 'i18n')\n    else:\n        i18n_path = os.path.dirname(ckan.i18n.__file__)\n\n    # For every file in the ckan i18n directory see if babel can understand\n    # the locale. If yes, add it to the available locales\n    for locale in os.listdir(i18n_path):\n        try:\n            Locale.parse(locale)\n            locales.append(locale)\n        except (ValueError, UnknownLocaleError):\n            # Babel does not know how to make a locale out of this.\n            # This is fine since we are passing all files in the\n            # ckan.i18n_directory here which e.g. includes the __init__.py\n            pass\n\n    assert locale_default in locales, \\\n        'default language \"%s\" not available' % locale_default\n\n    locale_list = []\n    for locale in locales:\n        # no duplicates\n        if locale in locale_list:\n            continue\n        # if offered locales then check locale is offered\n        if locales_offered and locale not in locales_offered:\n            continue\n        # remove if filtered out\n        if locale in filtered_out:\n            continue\n        # ignore the default as it will be added first\n        if locale == locale_default:\n            continue\n        locale_list.append(locale)\n    # order the list if specified\n    ordered_list = [locale_default]\n    for locale in locale_order:\n        if locale in locale_list:\n            ordered_list.append(locale)\n            # added so remove from our list\n            locale_list.remove(locale)\n    # add any remaining locales not ordered\n    ordered_list += locale_list\n\n    return ordered_list\n\navailable_locales = None\nlocales = None\nlocales_dict = None\n_non_translated_locals = None\n\n\ndef get_locales():\n    ''' Get list of available locales\n    e.g. [ 'en', 'de', ... ]\n    '''\n    global locales\n    if not locales:\n        locales = _get_locales()\n    return locales\n\n\ndef non_translated_locals():\n    ''' These are the locales that are available but for which there are\n    no translations. returns a list like ['en', 'de', ...] '''\n    global _non_translated_locals\n    if not _non_translated_locals:\n        locales = config.get('ckan.locale_order', '').split()\n        _non_translated_locals = [x for x in locales if x not in get_locales()]\n    return _non_translated_locals\n\n\ndef get_locales_dict():\n    ''' Get a dict of the available locales\n    e.g.  { 'en' : Locale('en'), 'de' : Locale('de'), ... } '''\n    global locales_dict\n    if not locales_dict:\n        locales = _get_locales()\n        locales_dict = {}\n        for locale in locales:\n            locales_dict[str(locale)] = Locale.parse(locale)\n    return locales_dict\n\n\ndef get_available_locales():\n    ''' Get a list of the available locales\n    e.g.  [ Locale('en'), Locale('de'), ... ] '''\n    global available_locales\n    if not available_locales:\n        available_locales = []\n        for locale in get_locales():\n            # Add the short names for the locales. This equals the filename\n            # of the ckan translation files as opposed to the long name\n            # that includes the script which is generated by babel\n            # so e.g. `zn_CH` instead of `zn_Hans_CH` this is needed\n            # to properly construct urls with url_for\n            parsed_locale = Locale.parse(locale)\n            parsed_locale.short_name = locale\n\n            # Add the full identifier (eg `pt_BR`) to the locale classes,\n            # as it does not offer a way of accessing it directly\n            parsed_locale.identifier = \\\n                get_identifier_from_locale_class(parsed_locale)\n            available_locales.append(parsed_locale)\n    return available_locales\n\n\ndef get_identifier_from_locale_class(locale):\n    return get_locale_identifier(\n        (locale.language,\n         locale.territory,\n         locale.script,\n         locale.variant))\n\n\ndef _set_lang(lang):\n    ''' Allows a custom i18n directory to be specified.\n    Creates a fake config file to pass to pylons.i18n.set_lang, which\n    sets the Pylons root path to desired i18n_directory.\n    This is needed as Pylons will only look for an i18n directory in\n    the application root.'''\n    if config.get('ckan.i18n_directory'):\n        fake_config = {'pylons.paths': {'root': config['ckan.i18n_directory']},\n                       'pylons.package': config['pylons.package']}\n        i18n.set_lang(lang, config=fake_config, class_=Translations)\n    else:\n        i18n.set_lang(lang, class_=Translations)\n\n\ndef handle_request(request, tmpl_context):\n    ''' Set the language for the request '''\n    lang = request.environ.get('CKAN_LANG') or \\\n        config.get('ckan.locale_default', 'en')\n    if lang != 'en':\n        set_lang(lang)\n\n    for plugin in PluginImplementations(ITranslation):\n        if lang in plugin.i18n_locales():\n            _add_extra_translations(plugin.i18n_directory(), lang,\n                                    plugin.i18n_domain())\n\n    extra_directory = config.get('ckan.i18n.extra_directory')\n    extra_domain = config.get('ckan.i18n.extra_gettext_domain')\n    extra_locales = aslist(config.get('ckan.i18n.extra_locales'))\n    if extra_directory and extra_domain and extra_locales:\n        if lang in extra_locales:\n            _add_extra_translations(extra_directory, lang, extra_domain)\n\n    tmpl_context.language = lang\n    return lang\n\n\ndef _add_extra_translations(dirname, locales, domain):\n    translator = Translations.load(dirname=dirname, locales=locales,\n                                   domain=domain)\n    try:\n        pylons.translator.merge(translator)\n    except AttributeError:\n        # this occurs when an extension has 'en' translations that\n        # replace the default strings. As set_lang has not been run,\n        # pylons.translation is the NullTranslation, so we have to\n        # replace the StackedObjectProxy ourselves manually.\n        environ = pylons.request.environ\n        environ['pylons.pylons'].translator = translator\n        if 'paste.registry' in environ:\n            environ['paste.registry'].replace(pylons.translator,\n                                              translator)\n\n\ndef get_lang():\n    ''' Returns the current language. Based on babel.i18n.get_lang but\n    works when set_lang has not been run (i.e. still in English). '''\n    langs = i18n.get_lang()\n    if langs:\n        return langs[0]\n    else:\n        return 'en'\n\n\ndef set_lang(language_code):\n    ''' Wrapper to pylons call '''\n    if language_code in non_translated_locals():\n        language_code = config.get('ckan.locale_default', 'en')\n    if language_code != 'en':\n        _set_lang(language_code)\n", "path": "ckan/lib/i18n.py"}], "after_files": [{"content": "# encoding: utf-8\n\nimport os\n\nfrom babel import Locale\nfrom babel.core import (LOCALE_ALIASES,\n                        get_locale_identifier,\n                        UnknownLocaleError)\nfrom babel.support import Translations\nfrom paste.deploy.converters import aslist\nfrom pylons import i18n\nimport pylons\n\n\nfrom ckan.common import config\nimport ckan.i18n\nfrom ckan.plugins import PluginImplementations\nfrom ckan.plugins.interfaces import ITranslation\n\n# Default Portuguese language to Brazilian territory, since\n# we don't have a Portuguese territory translation currently.\nLOCALE_ALIASES['pt'] = 'pt_BR'\n\n\ndef get_locales_from_config():\n    ''' despite the name of this function it gets the locales defined by\n    the config AND also the locals available subject to the config. '''\n    locales_offered = config.get('ckan.locales_offered', '').split()\n    filtered_out = config.get('ckan.locales_filtered_out', '').split()\n    locale_default = [config.get('ckan.locale_default', 'en')]\n    locale_order = config.get('ckan.locale_order', '').split()\n\n    known_locales = get_locales()\n    all_locales = (set(known_locales) |\n                   set(locales_offered) |\n                   set(locale_order) |\n                   set(locale_default))\n    all_locales -= set(filtered_out)\n    return all_locales\n\n\ndef _get_locales():\n    # FIXME this wants cleaning up and merging with get_locales_from_config()\n    assert not config.get('lang'), \\\n        ('\"lang\" config option not supported - please use ckan.locale_default '\n         'instead.')\n    locales_offered = config.get('ckan.locales_offered', '').split()\n    filtered_out = config.get('ckan.locales_filtered_out', '').split()\n    locale_default = config.get('ckan.locale_default', 'en')\n    locale_order = config.get('ckan.locale_order', '').split()\n\n    locales = ['en']\n    if config.get('ckan.i18n_directory'):\n        i18n_path = os.path.join(config.get('ckan.i18n_directory'), 'i18n')\n    else:\n        i18n_path = os.path.dirname(ckan.i18n.__file__)\n\n    # For every file in the ckan i18n directory see if babel can understand\n    # the locale. If yes, add it to the available locales\n    for locale in os.listdir(i18n_path):\n        try:\n            Locale.parse(locale)\n            locales.append(locale)\n        except (ValueError, UnknownLocaleError):\n            # Babel does not know how to make a locale out of this.\n            # This is fine since we are passing all files in the\n            # ckan.i18n_directory here which e.g. includes the __init__.py\n            pass\n\n    assert locale_default in locales, \\\n        'default language \"%s\" not available' % locale_default\n\n    locale_list = []\n    for locale in locales:\n        # no duplicates\n        if locale in locale_list:\n            continue\n        # if offered locales then check locale is offered\n        if locales_offered and locale not in locales_offered:\n            continue\n        # remove if filtered out\n        if locale in filtered_out:\n            continue\n        # ignore the default as it will be added first\n        if locale == locale_default:\n            continue\n        locale_list.append(locale)\n    # order the list if specified\n    ordered_list = [locale_default]\n    for locale in locale_order:\n        if locale in locale_list:\n            ordered_list.append(locale)\n            # added so remove from our list\n            locale_list.remove(locale)\n    # add any remaining locales not ordered\n    ordered_list += locale_list\n\n    return ordered_list\n\navailable_locales = None\nlocales = None\nlocales_dict = None\n_non_translated_locals = None\n\n\ndef get_locales():\n    ''' Get list of available locales\n    e.g. [ 'en', 'de', ... ]\n    '''\n    global locales\n    if not locales:\n        locales = _get_locales()\n    return locales\n\n\ndef non_translated_locals():\n    ''' These are the locales that are available but for which there are\n    no translations. returns a list like ['en', 'de', ...] '''\n    global _non_translated_locals\n    if not _non_translated_locals:\n        locales = config.get('ckan.locale_order', '').split()\n        _non_translated_locals = [x for x in locales if x not in get_locales()]\n    return _non_translated_locals\n\n\ndef get_locales_dict():\n    ''' Get a dict of the available locales\n    e.g.  { 'en' : Locale('en'), 'de' : Locale('de'), ... } '''\n    global locales_dict\n    if not locales_dict:\n        locales = _get_locales()\n        locales_dict = {}\n        for locale in locales:\n            locales_dict[str(locale)] = Locale.parse(locale)\n    return locales_dict\n\n\ndef get_available_locales():\n    ''' Get a list of the available locales\n    e.g.  [ Locale('en'), Locale('de'), ... ] '''\n    global available_locales\n    if not available_locales:\n        available_locales = []\n        for locale in get_locales():\n            # Add the short names for the locales. This equals the filename\n            # of the ckan translation files as opposed to the long name\n            # that includes the script which is generated by babel\n            # so e.g. `zn_CH` instead of `zn_Hans_CH` this is needed\n            # to properly construct urls with url_for\n            parsed_locale = Locale.parse(locale)\n            parsed_locale.short_name = locale\n\n            # Add the full identifier (eg `pt_BR`) to the locale classes,\n            # as it does not offer a way of accessing it directly\n            parsed_locale.identifier = \\\n                get_identifier_from_locale_class(parsed_locale)\n            available_locales.append(parsed_locale)\n    return available_locales\n\n\ndef get_identifier_from_locale_class(locale):\n    return get_locale_identifier(\n        (locale.language,\n         locale.territory,\n         locale.script,\n         locale.variant))\n\n\ndef _set_lang(lang):\n    ''' Allows a custom i18n directory to be specified.\n    Creates a fake config file to pass to pylons.i18n.set_lang, which\n    sets the Pylons root path to desired i18n_directory.\n    This is needed as Pylons will only look for an i18n directory in\n    the application root.'''\n    if config.get('ckan.i18n_directory'):\n        fake_config = {'pylons.paths': {'root': config['ckan.i18n_directory']},\n                       'pylons.package': config['pylons.package']}\n        i18n.set_lang(lang, config=fake_config, class_=Translations)\n    else:\n        i18n.set_lang(lang, class_=Translations)\n\n\ndef handle_request(request, tmpl_context):\n    ''' Set the language for the request '''\n    lang = request.environ.get('CKAN_LANG') or \\\n        config.get('ckan.locale_default', 'en')\n    if lang != 'en':\n        set_lang(lang)\n\n    for plugin in PluginImplementations(ITranslation):\n        if lang in plugin.i18n_locales():\n            _add_extra_translations(plugin.i18n_directory(), lang,\n                                    plugin.i18n_domain())\n\n    extra_directory = config.get('ckan.i18n.extra_directory')\n    extra_domain = config.get('ckan.i18n.extra_gettext_domain')\n    extra_locales = aslist(config.get('ckan.i18n.extra_locales'))\n    if extra_directory and extra_domain and extra_locales:\n        if lang in extra_locales:\n            _add_extra_translations(extra_directory, lang, extra_domain)\n\n    tmpl_context.language = lang\n    return lang\n\n\ndef _add_extra_translations(dirname, locales, domain):\n    translator = Translations.load(dirname=dirname, locales=locales,\n                                   domain=domain)\n    try:\n        pylons.translator.merge(translator)\n    except AttributeError:\n        # this occurs when an extension has 'en' translations that\n        # replace the default strings. As set_lang has not been run,\n        # pylons.translation is the NullTranslation, so we have to\n        # replace the StackedObjectProxy ourselves manually.\n        environ = pylons.request.environ\n        environ['pylons.pylons'].translator = translator\n        if 'paste.registry' in environ:\n            environ['paste.registry'].replace(pylons.translator,\n                                              translator)\n\n\ndef get_lang():\n    ''' Returns the current language. Based on babel.i18n.get_lang but\n    works when set_lang has not been run (i.e. still in English). '''\n    langs = i18n.get_lang()\n    if langs:\n        return langs[0]\n    else:\n        return 'en'\n\n\ndef set_lang(language_code):\n    ''' Wrapper to pylons call '''\n    if language_code in non_translated_locals():\n        language_code = config.get('ckan.locale_default', 'en')\n    if language_code != 'en':\n        _set_lang(language_code)\n", "path": "ckan/lib/i18n.py"}]}