diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 52bcdde15..e7f082a34 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,25 @@ These are notable changes in XBlock. Unreleased ---------- +1.9.0 - 2023-11-20 +------------------ + +* Support for `OEP-58 JavaScript translations `_: + + * Introduced abstract JavaScript translations support by adding the ``i18n_js_namespace`` property and + ``get_i18n_js_namespace`` method to the ``SharedBlockBase``. This allows XBlocks to define a JavaScript namespace + so the XBlock i18n runtime service can manage and load JavaScript translations for XBlocks. + + * Added the stub ``get_javascript_i18n_catalog_url`` method to the ``NullI18nService`` class to be implemented + by runtime services. + + * See the `edx-platform atlas translations proposal `_ + +1.8.1 - 2023-10-07 +------------------ + +* Python Requirements Update +* Update setup.py, adds required packages 1.8.0 - 2023-09-25 ------------------ diff --git a/xblock/__init__.py b/xblock/__init__.py index 221a539ce..e230e9dc9 100644 --- a/xblock/__init__.py +++ b/xblock/__init__.py @@ -27,4 +27,4 @@ def __init__(self, *args, **kwargs): # without causing a circular import xblock.fields.XBlockMixin = XBlockMixin -__version__ = '1.8.1' +__version__ = '1.9.0' diff --git a/xblock/core.py b/xblock/core.py index 753c3cf71..407b5ee68 100644 --- a/xblock/core.py +++ b/xblock/core.py @@ -55,6 +55,7 @@ class SharedBlockBase(Plugin): resources_dir = '' public_dir = 'public' + i18n_js_namespace = None @classmethod def get_resources_dir(cls): @@ -70,6 +71,17 @@ def get_public_dir(cls): """ return cls.public_dir + @classmethod + def get_i18n_js_namespace(cls): + """ + Gets the JavaScript translations namespace for this XBlock. + + Returns: + str: The JavaScript namespace for this XBlock. + None: If this doesn't have JavaScript translations configured. + """ + return cls.i18n_js_namespace + @classmethod def open_local_resource(cls, uri): """ diff --git a/xblock/runtime.py b/xblock/runtime.py index 209d95992..d9f9efd86 100644 --- a/xblock/runtime.py +++ b/xblock/runtime.py @@ -1357,6 +1357,16 @@ def strftime(self, dtime, format): # pylint: disable=redefined-builtin timestring = dtime.strftime(format) return timestring + def get_javascript_i18n_catalog_url(self): + """ + Return the URL to the JavaScript i18n catalog file. + + This method returns None in NullI18nService. When implemented in + a runtime, it should return the URL to the JavaScript i18n catalog so + it can be loaded in frontends. + """ + return None + @property def ugettext(self): """ diff --git a/xblock/test/test_core.py b/xblock/test/test_core.py index b1d919b51..9f469850b 100644 --- a/xblock/test/test_core.py +++ b/xblock/test/test_core.py @@ -196,6 +196,39 @@ class FieldTester(XBlock): assert {1} == field_tester._field_data.get(field_tester, 'field_d') +def test_shared_block_base_defaults(): + """ + Test default values and static methods provided by the SharedBlockBase class. + """ + class DefaultsTester(XBlock): + pass + + defaults_tester = DefaultsTester( + TestRuntime(services={'field-data': DictFieldData({})}), + scope_ids=Mock(spec=ScopeIds) + ) + + # Testing the default static method return values of SharedBlockBase + assert defaults_tester.get_resources_dir() == '' + assert defaults_tester.get_public_dir() == 'public' + assert defaults_tester.get_i18n_js_namespace() is None + + class CustomizedValuesTester(XBlock): + resources_dir = 'custom_resource_dir' + public_dir = 'another_public_dir' + i18n_js_namespace = 'CustomizedValuesTesterI18N' + + customized_values_tester = CustomizedValuesTester( + TestRuntime(services={'field-data': DictFieldData({})}), + scope_ids=Mock(spec=ScopeIds), + ) + + # Testing the customized static method return values of SharedBlockBase + assert customized_values_tester.get_resources_dir() == 'custom_resource_dir' + assert customized_values_tester.get_public_dir() == 'another_public_dir' + assert customized_values_tester.get_i18n_js_namespace() == 'CustomizedValuesTesterI18N' + + def test_mutable_none_values(): # Check that fields with values intentionally set to None # save properly. diff --git a/xblock/test/test_runtime.py b/xblock/test/test_runtime.py index c7eb67773..d02bf02e9 100644 --- a/xblock/test/test_runtime.py +++ b/xblock/test/test_runtime.py @@ -523,6 +523,9 @@ def assert_equals_unicode(str1, str2): assert_equals_unicode("Feb 14, 2013 at 22:30", i18n.strftime(when, "DATE_TIME")) assert_equals_unicode("10:30:17 PM", i18n.strftime(when, "TIME")) + # Runtimes are expected to implement this method though. + assert i18n.get_javascript_i18n_catalog_url() is None, 'NullI18nService does not implement this method.' + # secret_service is available. assert self.runtime.service(self, "secret_service") == 17