Repository: django-cms/django-cms

Issue:
cms.utils.i18n.get_fallback_languages may fail if LANGUAGES has more languages than CMS_LANGUAGES
Reported via IRC.

Use case: Show the admin interface in English (for users that configured their browser to English), but CMS pages should only be in German or French. 

In the use case above, settings might be something like:

``` python
LANGUAGES = [
    ('en', 'English'),
    ('de', 'Deutsch'),
    ('fr', 'French'),
]

CMS_LANGUAGES = {
    1: [
        {
            'code': 'de',
            'name': gettext('Deutsch'),
            'public': True,
        },
        {
            'code': 'fr',
            'name': gettext('French'),
            'fallbacks': ['de',],
            'public': False,
        },
    ],
    'default': {
        'fallbacks': ['de',],
        'redirect_on_fallback':True,
        'public': False,
        'hide_untranslated': False,
    }
}
```

`'en'` is in `LANGUAGES` but not in `CMS_LANGUAGES`.

Now if `cms.utils.i18n.get_fallback_languages` is called with `'en'` as argument (that happens for example if you try to add a page in the admin with the admin UI in English, as the add view tries to log the page change in the current active language, not language of the page. This triggers a call to `get_fallback_languages` with English as the argument) it raises a `LanguageError` as `'en'` is not available to the CMS.

cms.utils.i18n.get_fallback_languages may fail if LANGUAGES has more languages than CMS_LANGUAGES
Reported via IRC.

Use case: Show the admin interface in English (for users that configured their browser to English), but CMS pages should only be in German or French. 

In the use case above, settings might be something like:

``` python
LANGUAGES = [
    ('en', 'English'),
    ('de', 'Deutsch'),
    ('fr', 'French'),
]

CMS_LANGUAGES = {
    1: [
        {
            'code': 'de',
            'name': gettext('Deutsch'),
            'public': True,
        },
        {
            'code': 'fr',
            'name': gettext('French'),
            'fallbacks': ['de',],
            'public': False,
        },
    ],
    'default': {
        'fallbacks': ['de',],
        'redirect_on_fallback':True,
        'public': False,
        'hide_untranslated': False,
    }
}
```

`'en'` is in `LANGUAGES` but not in `CMS_LANGUAGES`.

Now if `cms.utils.i18n.get_fallback_languages` is called with `'en'` as argument (that happens for example if you try to add a page in the admin with the admin UI in English, as the add view tries to log the page change in the current active language, not language of the page. This triggers a call to `get_fallback_languages` with English as the argument) it raises a `LanguageError` as `'en'` is not available to the CMS.



Discussion:

Resolving patch:
diff --git a/cms/utils/i18n.py b/cms/utils/i18n.py
--- a/cms/utils/i18n.py
+++ b/cms/utils/i18n.py
@@ -152,7 +152,10 @@ def get_fallback_languages(language, site_id=None):
     """
     returns a list of fallback languages for the given language
     """
-    language = get_language_object(language, site_id)
+    try:
+        language = get_language_object(language, site_id)
+    except LanguageError:
+        language = get_languages(site_id)[0]
     return language.get('fallbacks', [])
 
 


Test patch:
diff --git a/cms/tests/i18n.py b/cms/tests/i18n.py
--- a/cms/tests/i18n.py
+++ b/cms/tests/i18n.py
@@ -3,6 +3,7 @@
 from cms.test_utils.util.context_managers import SettingsOverride
 from cms.utils import i18n
 from django.utils.importlib import import_module
+from cms.utils.i18n import get_fallback_languages
 
 
 class TestLanguages(SettingsOverrideTestCase):
@@ -275,6 +276,39 @@ def test_get_languages_undefined_site(self):
             self.assertEqual(lang['hide_untranslated'], False)
 
 
+class TestLanguagesNotInCMSLanguages(SettingsOverrideTestCase):
+    settings_overrides = {
+        'LANGUAGE_CODE': 'en',
+        'LANGUAGES': [
+            ('en', 'English'),
+            ('de', 'German'),
+            ('fr', 'French')
+        ],
+        'CMS_LANGUAGES': {
+            1: [
+                {
+                    'code': 'de',
+                    'name': 'German',
+                    'public': True,
+                },
+                {
+                    'code': 'fr',
+                    'name': 'French',
+                    'public': True
+                }
+            ],
+            'default': {
+                'fallbacks': ['de', 'fr'],
+            }
+        },
+        'SITE_ID': 1,
+    }
+
+    def test_get_fallback_languages(self):
+        languages = get_fallback_languages('en', 1)
+        self.assertEqual(languages, ['de', 'fr'])
+
+
 class TestLanguageFallbacks(SettingsOverrideTestCase):
 
     settings_overrides = {