Exemple #1
0
 def test_retry_fetch_translations(self):
     cds_host = 'https://some.host'
     cds_handler = CDSHandler(
         ['el', 'en'],
         'some_token',
         host=cds_host,
     )
     responses.add(responses.GET, cds_host + '/content/el', status=500)
     responses.add(responses.GET, cds_host + '/content/el', status=202)
     responses.add(responses.GET,
                   cds_host + '/content/el',
                   json={'data': {
                       'source': {
                           'string': "translation"
                       }
                   }},
                   status=200)
     translations = cds_handler.fetch_translations('el')
     assert (translations == {
         'el': (True, {
             'source': {
                 'string': "translation"
             }
         })
     })
Exemple #2
0
    def test_fetch_translations(self, patched_logger):
        cds_host = 'https://some.host'
        cds_handler = CDSHandler(['el', 'en', 'fr'],
                                 'some_token',
                                 host=cds_host)

        # add response for languages
        responses.add(responses.GET,
                      cds_host + '/languages',
                      json={
                          "data": [
                              {
                                  "code": "el",
                              },
                              {
                                  "code": "en",
                              },
                              {
                                  "code": "fr",
                              },
                          ],
                          "meta": {
                              "some_key": "some_value"
                          }
                      },
                      status=200)

        # add response for translations
        responses.add(responses.GET,
                      cds_host + '/content/el',
                      json={
                          'data': {
                              'key1': {
                                  'string': 'key1_el'
                              },
                              'key2': {
                                  'string': 'key2_el'
                              },
                          },
                          'meta': {
                              "some_key": "some_value"
                          }
                      },
                      status=200)

        responses.add(responses.GET,
                      cds_host + '/content/en',
                      json={
                          'data': {
                              'key1': {
                                  'string': 'key1_en'
                              },
                              'key2': {
                                  'string': 'key2_en'
                              },
                          },
                          'meta': {}
                      },
                      status=200)

        # add response bad status response for a language here
        responses.add(responses.GET, cds_host + '/content/fr', status=404)

        resp = cds_handler.fetch_translations()
        assert resp == {
            'el': (True, {
                'key1': {
                    'string': 'key1_el'
                },
                'key2': {
                    'string': 'key2_el'
                },
            }),
            'en': (True, {
                'key1': {
                    'string': 'key1_en'
                },
                'key2': {
                    'string': 'key2_en'
                },
            }),
            'fr': (False, {})  # that is due to the error status in response
        }

        responses.reset()

        # test fetch_languages fails with connection error
        responses.add(responses.GET, cds_host + '/languages', status=500)
        resp = cds_handler.fetch_translations()
        assert resp == {}

        patched_logger.error.assert_called_with(
            'Error retrieving languages from CDS: UnknownError '
            '(`500 Server Error: Internal Server Error for url: '
            'https://some.host/languages`)')
        responses.reset()
        patched_logger.reset_mock()

        # test language code
        responses.add(responses.GET,
                      cds_host + '/content/el',
                      json={
                          'data': {
                              'key1': {
                                  'string': 'key1_el'
                              },
                              'key2': {
                                  'string': 'key2_el'
                              },
                          },
                          'meta': {
                              "some_key": "some_value"
                          }
                      },
                      status=200)

        resp = cds_handler.fetch_translations(language_code='el')
        assert resp == {
            'el': (True, {
                'key1': {
                    'string': 'key1_el'
                },
                'key2': {
                    'string': 'key2_el'
                },
            })
        }
        responses.reset()
        assert patched_logger.error.call_count == 0

        # test connection_error
        resp = cds_handler.fetch_translations(language_code='el')
        patched_logger.error.assert_called_with(
            'Error retrieving translations from CDS: ConnectionError')
        assert resp == {'el': (False, {})}
Exemple #3
0
    def test_fetch_translations_etags_management(self, patched_logger):

        cds_host = 'https://some.host'
        cds_handler = CDSHandler(['el', 'en'], 'some_token', host=cds_host)

        # add response for languages
        responses.add(responses.GET,
                      cds_host + '/languages',
                      json={
                          "data": [
                              {
                                  "code": "el",
                              },
                              {
                                  "code": "en",
                              },
                          ],
                          "meta": {
                              "some_key": "some_value"
                          }
                      },
                      status=200)

        # add response for translations
        responses.add(responses.GET,
                      cds_host + '/content/el',
                      json={
                          'data': {
                              'key1': {
                                  'string': 'key1_el'
                              },
                              'key2': {
                                  'string': 'key2_el'
                              },
                          },
                          'meta': {
                              "some_key": "some_value"
                          }
                      },
                      status=200,
                      headers={'ETag': 'some_unique_tag_is_here'})

        responses.add(
            responses.GET,
            cds_host + '/content/en',
            # whatever, we don't care about the content of json repsone atm.
            json={},
            status=304)

        resp = cds_handler.fetch_translations()
        assert resp == {
            'el': (True, {
                'key1': {
                    'string': 'key1_el'
                },
                'key2': {
                    'string': 'key2_el'
                },
            }),
            'en': (False, {})
        }
        assert cds_handler.etags.get('el') == 'some_unique_tag_is_here'
Exemple #4
0
class TxNative(object):
    """The main class of the framework, responsible for orchestrating all
    behavior."""

    def __init__(self):
        # The class uses an untypical initialization scheme, defining
        # an init() method, instead of initializing inside the constructor
        # This is necessary for allowing it to be initialized by its clients
        # with proper arguments, while at the same time being very easy
        # to import and use a single "global" instance
        self._cache = None
        self._languages = []
        self._missing_policy = None
        self._cds_handler = None
        self.initialized = False

    def init(
        self, languages, token, secret=None, cds_host=None,
        missing_policy=None, error_policy=None
    ):
        """Create an instance of the core framework class.

        Also warms up the cache by fetching the translations from the CDS.

        :param list languages: a list of language codes for the languages
            configured in the application
        :param str token: the API token to use for connecting to the CDS
        :param str secret: the additional secret to use for pushing source
            content
        :param str cds_host: an optional host for the Content Delivery Service,
            defaults to the host provided by Transifex
        :param AbstractRenderingPolicy missing_policy: an optional policy
            to use for returning strings when a translation is missing
        :param AbstractErrorPolicy error_policy: an optional policy
            to determine how to handle rendering errors
        """
        self._languages = languages
        self._cache = MemoryCache()
        self._missing_policy = missing_policy or SourceStringPolicy()
        self._error_policy = error_policy or SourceStringErrorPolicy()
        self._cds_handler = CDSHandler(
            self._languages, token, secret=secret, host=cds_host
        )
        self.initialized = True

    def translate(
        self, source_string, language_code, is_source=False,
        _context=None, escape=True, params=None
    ):
        """Translate the given string to the provided language.

        :param unicode source_string: the source string to get the translation
            for e.g. 'Order: {num, plural, one {A table} other {{num} tables}}'
        :param str language_code: the language to translate to
        :param bool is_source: a boolean indicating whether `translate`
            is being used for the source language
        :param unicode _context: an optional context that accompanies
            the string
        :param bool escape: if True, the returned string will be HTML-escaped,
            otherwise it won't
        :param dict params: optional parameters to replace any placeholders
            found in the translation string
        :return: the rendered string
        :rtype: unicode
        """

        if params is None:
            params = {}

        self._check_initialization()

        translation_template = self.get_translation(source_string,
                                                    language_code,
                                                    _context,
                                                    is_source)

        return self.render_translation(translation_template,
                                       params,
                                       source_string,
                                       language_code,
                                       escape)

    def get_translation(self, source_string, language_code, _context,
                        is_source=False):
        """ Try to retrieve the translation.

            A translation is a serialized source_string with ICU format
            support, e.g.
            '{num, plural, one {Ένα τραπέζι} other {{num} τραπέζια}}'
        """

        if is_source:
            translation_template = source_string
        else:
            pluralized, plurals = parse_plurals(source_string)
            key = generate_key(string=source_string, context=_context)
            translation_template = self._cache.get(key, language_code)
            if (translation_template is not None and pluralized and
                    translation_template.startswith('{???')):
                variable_name = source_string[1:source_string.index(',')].\
                    strip()
                translation_template = ('{' +
                                        variable_name +
                                        translation_template[4:])
        return translation_template

    def render_translation(self, translation_template, params, source_string,
                           language_code, escape=False):
        """ Replace the variables in the ICU translation """

        try:
            return StringRenderer.render(
                source_string=source_string,
                string_to_render=translation_template,
                language_code=language_code,
                escape=escape,
                missing_policy=self._missing_policy,
                params=params,
            )
        except Exception:
            return self._error_policy.get(
                source_string=source_string,
                translation=translation_template,
                language_code=language_code,
                escape=escape, params=params,
            )

    def fetch_translations(self):
        """Fetch fresh content from the CDS."""
        self._check_initialization()
        self._cache.update(self._cds_handler.fetch_translations())

    def push_source_strings(self, strings, purge=False):
        """Push the given source strings to the CDS.

        :param list strings: a list of SourceString objects
        :param bool purge: True deletes destination source content not included
                           in pushed content.
                           False appends the pushed content to destination
                           source content.
        :return: a tuple containing the status code and the content of the
            response
        :rtype: tuple
        """
        self._check_initialization()
        response = self._cds_handler.push_source_strings(strings, purge)
        return response.status_code, json.loads(response.content)

    def _check_initialization(self):
        """Raise an exception if the class has not been initialized.

        :raise NotInitializedError: if the class hasn't been initialized
        """
        if not self.initialized:
            raise NotInitializedError(
                'TxNative is not initialized, make sure you call init() first.'
            )