diff --git a/CHANGES b/CHANGES index d7d8af8..8557ae8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,13 @@ Version History =============== + +Version 0.9.3 (unreleased) +-------------------------- +* Added a tooltip to explain fuzzy entries (#206) +* New ROSETTA_LANGUAGES setting allows for translating languages which are not yet in LANGUAGES (#219) + + Version 0.9.2 ------------- * Cleanup of imports, and use relative imports. Hopefully fixes #209. diff --git a/docs/settings.rst b/docs/settings.rst index bdef1a2..fa17235 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -22,6 +22,7 @@ Rosetta can be configured via the following parameters, to be defined in your pr * ``ROSETTA_ENABLE_REFLANG``: Enables a selector for picking a reference language other than English. Defaults to ``False``. * ``ROSETTA_SHOW_AT_ADMIN_PANEL``: Adds a handy link to Rosetta at the bottom of the Django admin apps index. Defaults to ``False``. * ``ROSETTA_LOGIN_URL``: Use this if you want to override the login URL for rosetta. Defaults to ``settings.LOGIN_URL``. +* ``ROSETTA_LANGUAGES``: List of languages that Rosetta will offer to translate. This is useful when you wish to translate a language that is not yet defined in ``settings.LANGUAGES``. Defaults to ``settings.LANGUAGES``. Storages -------- diff --git a/rosetta/__init__.py b/rosetta/__init__.py index b9a2cb4..8726f2a 100644 --- a/rosetta/__init__.py +++ b/rosetta/__init__.py @@ -1,4 +1,4 @@ -VERSION = (0, 9, 2) +VERSION = (0, 9, 3) default_app_config = "rosetta.apps.RosettaAppConfig" diff --git a/rosetta/conf/__init__.py b/rosetta/conf/__init__.py index eb31ac5..483def0 100644 --- a/rosetta/conf/__init__.py +++ b/rosetta/conf/__init__.py @@ -51,6 +51,8 @@ class RosettaSettings(object): 'ROSETTA_AUTO_COMPILE': ('AUTO_COMPILE', True), 'ROSETTA_SHOW_AT_ADMIN_PANEL': ('SHOW_AT_ADMIN_PANEL', False), 'ROSETTA_LOGIN_URL': ('LOGIN_URL', dj_settings.LOGIN_URL), + + 'ROSETTA_LANGUAGES': ('ROSETTA_LANGUAGES', dj_settings.LANGUAGES) } def __init__(self): diff --git a/rosetta/templates/rosetta/css/rosetta.css b/rosetta/templates/rosetta/css/rosetta.css index 9472812..0cc6aba 100644 --- a/rosetta/templates/rosetta/css/rosetta.css +++ b/rosetta/templates/rosetta/css/rosetta.css @@ -35,3 +35,14 @@ div.module {margin-bottom: 20px;} #toolbar #translate-all { float:right} #toolbar form { float:left; } #changelist table thead th { padding: 2px 5px; } + + +.info-tip::after { + content: '?'; + color: white; + background-color: #bbb; + margin-left: 5px; + padding: 1px 5px; + border-radius: 10px; + font-size: 11px; +} diff --git a/rosetta/templates/rosetta/file-list.html b/rosetta/templates/rosetta/file-list.html index 82f6bef..5d91d65 100644 --- a/rosetta/templates/rosetta/file-list.html +++ b/rosetta/templates/rosetta/file-list.html @@ -1,5 +1,5 @@ {% extends "rosetta/base.html" %} -{% load i18n %} +{% load i18n static %} {% block pagetitle %}{{block.super}} - {% trans "Language selection" %}{% endblock %} @@ -32,7 +32,7 @@ {% trans "Progress"%} {% trans "Messages" %} {% trans "Translated" %} - {% trans "Fuzzy"%} + {% trans "Fuzzy"%} {% trans "Obsolete"%} {% trans "File" %} diff --git a/rosetta/templates/rosetta/form.html b/rosetta/templates/rosetta/form.html index 2e69ea7..32c9a3a 100644 --- a/rosetta/templates/rosetta/form.html +++ b/rosetta/templates/rosetta/form.html @@ -75,7 +75,7 @@
{% trans "Original" %}
{% if main_language %}{{ main_language }}{% endif %} {{ rosetta_i18n_lang_name }} - {% trans "Fuzzy" %} + {% trans "Fuzzy" %} {% trans "Occurrences(s)" %} diff --git a/rosetta/tests/tests.py b/rosetta/tests/tests.py index 78cc1ae..2888802 100644 --- a/rosetta/tests/tests.py +++ b/rosetta/tests/tests.py @@ -87,7 +87,7 @@ class RosettaTestCase(TestCase): os.path.normpath('rosetta/locale/xx/LC_MESSAGES/django.po') in str(r.content) ) - @override_settings(LANGUAGES=( + @override_settings(ROSETTA_LANGUAGES=( ('xx', 'dummy language'), )) def test_2_PickFile(self): @@ -101,7 +101,7 @@ class RosettaTestCase(TestCase): self.assertTrue('content-type' in r._headers.keys()) self.assertTrue('application/x-zip' in r._headers.get('content-type')) - @override_settings(LANGUAGES=( + @override_settings(ROSETTA_LANGUAGES=( ('xx', 'dummy language'), )) def test_4_DoChanges(self): @@ -137,7 +137,7 @@ class RosettaTestCase(TestCase): self.assertTrue('String 2' in str(r.content)) self.assertTrue('Hello, world' in str(r.content)) - @override_settings(LANGUAGES=( + @override_settings(ROSETTA_LANGUAGES=( ('xx', 'dummy language'), )) def test_5_TestIssue67(self): @@ -171,7 +171,7 @@ class RosettaTestCase(TestCase): self.assertTrue('or n%100>=20) ? 1 : 2)' not in str(content)) del(content) - @override_settings(LANGUAGES=( + @override_settings(ROSETTA_LANGUAGES=( ('xx', 'dummy language'), )) def test_6_ExcludedApps(self): @@ -190,7 +190,7 @@ class RosettaTestCase(TestCase): r = self.client.get(self.project_file_list_url) self.assertNotContains(r, 'rosetta/locale/xx/LC_MESSAGES/django.po') - @override_settings(LANGUAGES=( + @override_settings(ROSETTA_LANGUAGES=( ('xx', 'dummy language'), )) def test_8_hideObsoletes(self): @@ -297,7 +297,7 @@ class RosettaTestCase(TestCase): self.assertTrue(r.content) self.assertEqual(r.status_code, 200) - @override_settings(LANGUAGES=(('fr', 'French'), ('xx', 'Dummy Language'),)) + @override_settings(ROSETTA_LANGUAGES=(('fr', 'French'), ('xx', 'Dummy Language'),)) def test_13_catalog_filters(self): r = self.client.get(self.third_party_file_list_url) self.assertTrue( @@ -488,7 +488,7 @@ class RosettaTestCase(TestCase): self.assertTrue('m_4765f7de94996d3de5975fa797c3451f' in str(r.content)) self.assertTrue('m_08e4e11e2243d764fc45a5a4fba5d0f2' in str(r.content)) - @override_settings(LANGUAGES=( + @override_settings(ROSETTA_LANGUAGES=( ('xx', 'dummy language'), )) def test_23_save_header_data(self): @@ -590,7 +590,7 @@ class RosettaTestCase(TestCase): @override_settings( ROSETTA_POFILENAMES=('pr44.po', ), - LANGUAGES=(('xx', 'dummy language'),) + ROSETTA_LANGUAGES=(('xx', 'dummy language'),) ) def test_30_pofile_names(self): os.unlink(self.dest_file) @@ -648,7 +648,7 @@ class RosettaTestCase(TestCase): @override_settings( ROSETTA_ENABLE_REFLANG=True, - LANGUAGES=(('xx', 'dummy language'),) + ROSETTA_LANGUAGES=(('xx', 'dummy language'),) ) def test_33_reflang(self): self.copy_po_file_from_template('./django.po.issue60.template') @@ -705,7 +705,7 @@ class RosettaTestCase(TestCase): r = self.client.get(self.all_file_list_url) self.assertContains(r, 'locale/bs-Cyrl-BA/LC_MESSAGES/django.po') - @override_settings(LANGUAGES=(('yy-Anot', u'Yet Another dummy language'),)) + @override_settings(ROSETTA_LANGUAGES=(('yy-Anot', u'Yet Another dummy language'),)) def test_37_issue_133_complex_locales(self): r = self.client.get(self.all_file_list_url) self.assertContains(r, 'locale/yy_Anot/LC_MESSAGES/django.po') @@ -864,7 +864,7 @@ class RosettaTestCase(TestCase): self.assertEqual(view.po_file_path, self.dest_file) # But if the language isn't an option, we get a 404 - with self.settings(LANGUAGES=[l for l in settings.LANGUAGES if l[0] != 'xx']): + with self.settings(ROSETTA_LANGUAGES=[l for l in settings.LANGUAGES if l[0] != 'xx']): view = self._setup_view( view=views.TranslationFormView(), request=request, @@ -983,6 +983,20 @@ class RosettaTestCase(TestCase): self.assertRedirects(r, '/custom-url/?next=/rosetta/files/all/', fetch_redirect_response=False) self.assertEqual(302, r.status_code) + def test_51_rosetta_languages(self): + self.assertTrue('xx' in dict(settings.LANGUAGES)) + self.assertFalse('yy' in dict(settings.LANGUAGES)) + + with self.settings(ROSETTA_LANGUAGES=(('xx', 'foo language'), )): + r = self.client.get(self.project_file_list_url) + self.assertTrue('foo language' in str(r.content)) + self.assertFalse('bar language' in str(r.content)) + + with self.settings(ROSETTA_LANGUAGES=(('xx', 'foo language'), ('yy', 'bar language'), )): + r = self.client.get(self.project_file_list_url) + self.assertTrue('foo language' in str(r.content)) + self.assertTrue('bar language' in str(r.content)) + # Stubbed access control function def no_access(user): diff --git a/rosetta/views.py b/rosetta/views.py index 2c30bfa..3db3112 100644 --- a/rosetta/views.py +++ b/rosetta/views.py @@ -102,14 +102,14 @@ class RosettaFileLevelMixin(RosettaBaseMixin): def language_id(self): """Determine/return the language id from the url kwargs, after validating that: - 1. the language is in settings.LANGUAGES, and + 1. the language is in rosetta_settings.ROSETTA_LANGUAGES, and 2. the current user is permitted to translate that language (If either of the above fail, throw a 404.) """ # (Formerly known as "rosetta_i18n_lang_code") lang_id = self.kwargs['lang_id'] - if lang_id not in {l[0] for l in settings.LANGUAGES}: + if lang_id not in {l[0] for l in rosetta_settings.ROSETTA_LANGUAGES}: raise Http404 if not can_translate_language(self.request.user, lang_id): raise Http404 @@ -218,7 +218,7 @@ class TranslationFileListView(RosettaBaseMixin, TemplateView): languages = [] has_pos = False - for language in settings.LANGUAGES: + for language in rosetta_settings.ROSETTA_LANGUAGES: if not can_translate_language(self.request.user, language[0]): continue @@ -445,7 +445,7 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): # Handle REF_LANG setting; mark up our entries with the reg lang's # corresponding translations - LANGUAGES = list(settings.LANGUAGES) + LANGUAGES = list(rosetta_settings.ROSETTA_LANGUAGES) if rosetta_settings.ENABLE_REFLANG: if self.ref_lang_po_file: for o in paginator.object_list: @@ -485,7 +485,7 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): main_language = None if main_language_id and main_language_id != self.language_id: # Translate from id to language name - for language in settings.LANGUAGES: + for language in rosetta_settings.ROSETTA_LANGUAGES: if language[0] == main_language_id: main_language = _(language[1]) break @@ -502,7 +502,7 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): # Collect some constants for the template rosetta_i18n_lang_name = six.text_type( - dict(settings.LANGUAGES).get(self.language_id) + dict(rosetta_settings.ROSETTA_LANGUAGES).get(self.language_id) ) # "bidi" as in "bi-directional" rosetta_i18n_lang_bidi = self.language_id.split('-')[0] in settings.LANGUAGES_BIDI @@ -552,11 +552,11 @@ class TranslationFormView(RosettaFileLevelMixin, TemplateView): """Return the language id for the "reference language" (the language to be translated *from*, if not English). - Throw a 404 if it's not in settings.LANGUAGES. + Throw a 404 if it's not in rosetta_settings.ROSETTA_LANGUAGES. """ ref_lang = self._request_request('ref_lang', 'msgid') if ref_lang != 'msgid': - allowed_languages = {l[0] for l in settings.LANGUAGES} + allowed_languages = {l[0] for l in rosetta_settings.ROSETTA_LANGUAGES} if ref_lang not in allowed_languages: raise Http404 return ref_lang diff --git a/testproject/locale/yy/LC_MESSAGES/django.po b/testproject/locale/yy/LC_MESSAGES/django.po new file mode 100644 index 0000000..8938f73 --- /dev/null +++ b/testproject/locale/yy/LC_MESSAGES/django.po @@ -0,0 +1,35 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-04-07 04:49-0500\n" +"PO-Revision-Date: 2019-04-07 05:09-0551\n" +"Last-Translator: b' '\n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" +"X-Translated-Using: django-rosetta 0.9.3\n" + +#: templates/test.html:3 +msgid "Some text to translate" +msgstr "asdasd" + +#: templates/test.html:5 +#, python-format +msgid "" +"\n" +"one bottle of beer on the wall\n" +msgid_plural "" +"\n" +"%(num_bottles)s bottles of beer on the wall\n" +msgstr[0] "" +msgstr[1] ""