コード例 #1
0
ファイル: test_entity.py プロジェクト: Pike/pontoon
def test_entity_project_locale_subpages(entity_test_models):
    """
    If paths specified as subpages, return project entities from paths
    assigned to these subpages only along with their translations for
    locale.
    """
    tr0 = entity_test_models[0]
    subpageX = entity_test_models[3]
    locale_a = tr0.locale
    entity_a = tr0.entity
    resource0 = tr0.entity.resource
    project_a = tr0.entity.resource.project
    subpages = [subpageX.name]
    entities = Entity.map_entities(
        locale_a,
        Entity.for_project_locale(
            project_a,
            locale_a,
            subpages,
        ),
    )
    assert len(entities) == 1
    assert entities[0]['path'] == resource0.path
    assert entities[0]['original'] == entity_a.string
    assert entities[0]['translation'][0]['string'] == tr0.string
コード例 #2
0
ファイル: test_entity.py プロジェクト: Pike/pontoon
def test_entity_project_locale_order(entity_test_models):
    """
    Return entities in correct order.
    """
    resource0 = entity_test_models[0].entity.resource
    locale_a = entity_test_models[0].locale
    project_a = resource0.project
    EntityFactory.create(
        order=2,
        resource=resource0,
        string='Second String',
    )
    EntityFactory.create(
        order=1,
        resource=resource0,
        string='First String',
    )
    entities = Entity.map_entities(
        locale_a,
        Entity.for_project_locale(
            project_a,
            locale_a,
        ),
    )
    assert entities[1]['original'] == 'First String'
    assert entities[2]['original'] == 'Second String'
コード例 #3
0
ファイル: test_entity.py プロジェクト: Pike/pontoon
def test_entity_project_locale_cleaned_key(entity_test_models):
    """
    If key contanis source string and Translate Toolkit separator,
    remove them.
    """
    resource0 = entity_test_models[0].entity.resource
    locale_a = entity_test_models[0].locale
    project_a = resource0.project
    entities = Entity.map_entities(
        locale_a,
        Entity.for_project_locale(
            project_a,
            locale_a,
        ),
    )
    assert entities[0]['key'] == ''
    assert entities[1]['key'] == 'Key'
コード例 #4
0
ファイル: views.py プロジェクト: Pike/pontoon
def _get_entities_list(locale, project, form):
    """Return a specific list of entities, as defined by the `entity_ids` field of the form.

    This is used for batch editing.
    """
    entities = (
        Entity.objects.filter(pk__in=form.cleaned_data['entity_ids'])
        .distinct()
        .order_by('order')
    )

    return JsonResponse({
        'entities': Entity.map_entities(locale, entities),
        'stats': TranslatedResource.objects.stats(
            project, form.cleaned_data['paths'], locale
        ),
    }, safe=False)
コード例 #5
0
ファイル: test_entity.py プロジェクト: Pike/pontoon
def test_entity_project_locale_paths(entity_test_models):
    """
    If paths specified, return project entities from these paths only along
    with their translations for locale.
    """
    tr0, tr0pl, trX, subpageX = entity_test_models
    locale_a = tr0.locale
    project_a = tr0.entity.resource.project
    paths = ['resourceX.po']
    entities = Entity.map_entities(
        locale_a,
        Entity.for_project_locale(
            project_a,
            locale_a,
            paths,
        ),
    )
    assert len(entities) == 1
    assert entities[0]['path'] == trX.entity.resource.path
    assert entities[0]['original'] == trX.entity.string
    assert entities[0]['translation'][0]['string'] == trX.string
コード例 #6
0
ファイル: views.py プロジェクト: Pike/pontoon
def _get_all_entities(locale, project, form, entities):
    """Return entities without pagination.

    This is used by the in-context mode of the Translate page.
    """
    has_next = False
    entities_to_map = Entity.for_project_locale(
        project,
        locale,
        paths=form.cleaned_data['paths'],
        exclude_entities=form.cleaned_data['exclude_entities']
    )
    visible_entities = entities.values_list('pk', flat=True)

    return JsonResponse({
        'entities': Entity.map_entities(locale, entities_to_map, visible_entities),
        'has_next': has_next,
        'stats': TranslatedResource.objects.stats(
            project, form.cleaned_data['paths'], locale
        ),
    }, safe=False)
コード例 #7
0
def _get_paginated_entities(locale, project, form, entities):
    """Return a paginated list of entities.

    This is used by the regular mode of the Translate page.
    """
    paginator = Paginator(entities, form.cleaned_data['limit'])

    try:
        entities_page = paginator.page(1)
    except EmptyPage:
        return JsonResponse({
            'has_next': False,
            'stats': {},
        })

    has_next = entities_page.has_next()
    entities_to_map = entities_page.object_list

    # If requested entity not on the first page
    if form.cleaned_data['entity']:
        entity_pk = form.cleaned_data['entity']
        entities_to_map_pks = [e.pk for e in entities_to_map]

        # TODO: entities_to_map.values_list() doesn't return entities from selected page
        if entity_pk not in entities_to_map_pks:
            if entity_pk in entities.values_list('pk', flat=True):
                entities_to_map_pks.append(entity_pk)
                entities_to_map = entities.filter(pk__in=entities_to_map_pks)

    return JsonResponse(
        {
            'entities':
            Entity.map_entities(locale, entities_to_map, []),
            'has_next':
            has_next,
            'stats':
            TranslatedResource.objects.stats(
                project, form.cleaned_data['paths'], locale),
        },
        safe=False)
コード例 #8
0
def test_entity_project_locale_order(entity_test_models):
    """
    Return entities in correct order.
    """
    resource0 = entity_test_models[0].entity.resource
    locale0 = entity_test_models[0].locale
    project0 = resource0.project
    Entity.objects.create(
        order=1,
        resource=resource0,
        string='Second String')
    Entity.objects.create(
        order=0,
        resource=resource0,
        string='First String')
    entities = Entity.map_entities(
        locale0,
        Entity.for_project_locale(
            project0,
            locale0))
    assert entities[1]['original'] == 'First String'
    assert entities[2]['original'] == 'Second String'
コード例 #9
0
def test_entity_project_locale_subpages(entity_test_models):
    """
    If paths specified as subpages, return project entities from paths
    assigned to these subpages only along with their translations for
    locale.
    """
    tr0 = entity_test_models[0]
    subpageX = entity_test_models[3]
    locale0 = tr0.locale
    entity0 = tr0.entity
    resource0 = tr0.entity.resource
    project0 = tr0.entity.resource.project
    subpages = [subpageX.name]
    entities = Entity.map_entities(
        locale0,
        Entity.for_project_locale(
            project0,
            locale0,
            subpages))
    assert len(entities) == 1
    assert entities[0]['path'] == resource0.path
    assert entities[0]['original'] == entity0.string
    assert entities[0]['translation'][0]['string'] == tr0.string
コード例 #10
0
ファイル: test_entity.py プロジェクト: Pike/pontoon
def test_entity_project_locale_plurals(
    entity_test_models,
    locale_b,
    project_b,
):
    """
    For pluralized strings, return all available plural forms.
    """
    tr0, tr0pl, trX, subpageX = entity_test_models
    locale_a = tr0.locale
    entity_a = tr0.entity
    project_a = tr0.entity.resource.project
    entities = Entity.map_entities(
        locale_a,
        Entity.for_project_locale(
            project_a,
            locale_a,
        ),
    )
    assert entities[0]['original'] == entity_a.string
    assert entities[0]['original_plural'] == entity_a.string_plural
    assert entities[0]['translation'][0]['string'] == tr0.string
    assert entities[0]['translation'][1]['string'] == tr0pl.string
コード例 #11
0
ファイル: test_entity.py プロジェクト: systemik/pontoon
def test_entity_project_locale_paths(entity_test_models):
    """
    If paths specified, return project entities from these paths only along
    with their translations for locale.
    """
    tr0, tr0pl, trX, subpageX = entity_test_models
    locale_a = tr0.locale
    preferred_source_locale = ''
    project_a = tr0.entity.resource.project
    paths = ['resourceX.po']
    entities = Entity.map_entities(
        locale_a,
        preferred_source_locale,
        Entity.for_project_locale(
            project_a,
            locale_a,
            paths,
        ),
    )
    assert len(entities) == 1
    assert entities[0]['path'] == trX.entity.resource.path
    assert entities[0]['original'] == trX.entity.string
    assert entities[0]['translation'][0]['string'] == trX.string
コード例 #12
0
def test_entity_project_locale_plurals(
    entity_test_models,
    locale_b,
    project_b,
):
    """
    For pluralized strings, return all available plural forms.
    """
    tr0, tr0pl, trX, subpageX = entity_test_models
    locale_a = tr0.locale
    entity_a = tr0.entity
    project_a = tr0.entity.resource.project
    entities = Entity.map_entities(
        locale_a,
        Entity.for_project_locale(
            project_a,
            locale_a,
        ),
    )
    assert entities[0]['original'] == entity_a.string
    assert entities[0]['original_plural'] == entity_a.string_plural
    assert entities[0]['translation'][0]['string'] == tr0.string
    assert entities[0]['translation'][1]['string'] == tr0pl.string
コード例 #13
0
def _get_all_entities(locale, project, form, entities):
    """Return entities without pagination.

    This is used by the in-context mode of the Translate page.
    """
    has_next = False
    entities_to_map = Entity.for_project_locale(
        project,
        locale,
        paths=form.cleaned_data['paths'],
        exclude_entities=form.cleaned_data['exclude_entities'])
    visible_entities = entities.values_list('pk', flat=True)

    return JsonResponse(
        {
            'entities':
            Entity.map_entities(locale, entities_to_map, visible_entities),
            'has_next':
            has_next,
            'stats':
            TranslatedResource.objects.stats(
                project, form.cleaned_data['paths'], locale),
        },
        safe=False)
コード例 #14
0
ファイル: test_entity.py プロジェクト: NeatNerdPrime/pontoon
def test_entity_project_locale_paths(admin, entity_test_models):
    """
    If paths specified, return project entities from these paths only along
    with their translations for locale.
    """
    tr0, tr0pl, trX, subpageX = entity_test_models
    locale_a = tr0.locale
    preferred_source_locale = ""
    project_a = tr0.entity.resource.project
    paths = ["resourceX.po"]
    entities = Entity.map_entities(
        locale_a,
        preferred_source_locale,
        Entity.for_project_locale(
            admin,
            project_a,
            locale_a,
            paths,
        ),
    )
    assert len(entities) == 1
    assert entities[0]["path"] == trX.entity.resource.path
    assert entities[0]["original"] == trX.entity.string
    assert entities[0]["translation"][0]["string"] == trX.string
コード例 #15
0
ファイル: views.py プロジェクト: Pike/pontoon
def _get_paginated_entities(locale, project, form, entities):
    """Return a paginated list of entities.

    This is used by the regular mode of the Translate page.
    """
    paginator = Paginator(entities, form.cleaned_data['limit'])

    try:
        entities_page = paginator.page(1)
    except EmptyPage:
        return JsonResponse({
            'has_next': False,
            'stats': {},
        })

    has_next = entities_page.has_next()
    entities_to_map = entities_page.object_list

    # If requested entity not on the first page
    if form.cleaned_data['entity']:
        entity_pk = form.cleaned_data['entity']
        entities_to_map_pks = [e.pk for e in entities_to_map]

        # TODO: entities_to_map.values_list() doesn't return entities from selected page
        if entity_pk not in entities_to_map_pks:
            if entity_pk in entities.values_list('pk', flat=True):
                entities_to_map_pks.append(entity_pk)
                entities_to_map = entities.filter(pk__in=entities_to_map_pks)

    return JsonResponse({
        'entities': Entity.map_entities(locale, entities_to_map, []),
        'has_next': has_next,
        'stats': TranslatedResource.objects.stats(
            project, form.cleaned_data['paths'], locale
        ),
    }, safe=False)
コード例 #16
0
ファイル: views.py プロジェクト: rahuldecoded/pontoon
def entities(request):
    """Get entities for the specified project, locale and paths."""
    try:
        project = request.POST['project']
        locale = request.POST['locale']
        paths = request.POST.getlist('paths[]')
        limit = int(request.POST.get('limit', 50))
    except (MultiValueDictKeyError, ValueError) as err:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=err))

    project = get_object_or_404(Project, slug=project)
    locale = get_object_or_404(Locale, code__iexact=locale)

    filter_type = request.POST.get('filter', '')
    search = request.POST.get('search', '')
    exclude_entities = request.POST.getlist('excludeEntities[]', [])

    # Only return entities with provided IDs (batch editing)
    entity_ids = request.POST.getlist('entityIds[]', [])
    if entity_ids:
        entities = (
            Entity.objects.filter(pk__in=entity_ids)
            .prefetch_resources_translations(locale)
            .distinct()
            .order_by('order')
        )

        return JsonResponse({
            'entities': Entity.map_entities(locale, entities),
            'stats': TranslatedResource.objects.stats(project, paths, locale),
            'authors': Translation.authors(locale, project, paths).serialize(),
        }, safe=False)

    entities = Entity.for_project_locale(
        project, locale, paths, filter_type, search, exclude_entities
    )

    # Only return a list of entity PKs (batch editing: select all)
    if request.POST.get('pkOnly', None):
        return JsonResponse({
            'entity_pks': list(entities.values_list('pk', flat=True)),
        })

    visible_entities = []

    # In-place view: load all entities
    if request.POST.get('inplaceEditor', None):
        has_next = False
        entities_to_map = Entity.for_project_locale(
            project, locale, paths, None, None, exclude_entities
        )
        visible_entities = entities.values_list('pk', flat=True)

    # Out-of-context view: paginate entities
    else:
        paginator = Paginator(entities, limit)

        try:
            entities_page = paginator.page(1)
        except EmptyPage:
            return JsonResponse({
                'has_next': False,
                'stats': {},
                'authors': []
            })

        has_next = entities_page.has_next()
        entities_to_map = entities_page.object_list

        # If requested entity not on the first page
        entity = request.POST.get('entity', None)
        if entity:
            try:
                entity_pk = int(entity)
            except ValueError as err:
                return HttpResponseBadRequest('Bad Request: {error}'.format(error=err))

            # TODO: entities_to_map.values_list() doesn't return entities from selected page
            if entity_pk not in [e.pk for e in entities_to_map]:
                if entity_pk in entities.values_list('pk', flat=True):
                    entities_to_map = list(entities_to_map) + list(entities.filter(pk=entity_pk))

    return JsonResponse({
        'entities': Entity.map_entities(locale, entities_to_map, visible_entities),
        'has_next': has_next,
        'stats': TranslatedResource.objects.stats(project, paths, locale),
        'authors': Translation.authors(locale, project, paths).serialize(),
    }, safe=False)
コード例 #17
0
def test_entity_project_locale_no_paths(
    entity_test_models,
    locale_b,
    project_b,
):
    """
    If paths not specified, return all project entities along with their
    translations for locale.
    """
    tr0, tr0pl, trX, subpageX = entity_test_models
    locale_a = tr0.locale
    entity_a = tr0.entity
    resource0 = tr0.entity.resource
    project_a = tr0.entity.resource.project
    entities = Entity.map_entities(
        locale_a,
        Entity.for_project_locale(project_a, locale_a),
    )
    assert len(entities) == 2
    assert entities[0]['path'] == resource0.path
    assert entities[0]['original'] == entity_a.string
    assert entities[0]['translation'][0]['string'] == tr0.string
    assert entities[1]['path'] == trX.entity.resource.path
    assert entities[1]['original'] == trX.entity.string
    assert entities[1]['translation'][0]['string'] == trX.string

    # Ensure all attributes are assigned correctly
    expected = {
        'comment':
        '',
        'format':
        'po',
        'obsolete':
        False,
        'marked':
        unicode(entity_a.string),
        'key':
        '',
        'path':
        unicode(resource0.path),
        'project':
        project_a.serialize(),
        'translation': [
            {
                'pk': tr0.pk,
                'fuzzy': False,
                'string': unicode(tr0.string),
                'approved': False,
                'rejected': False,
            },
            {
                'pk': tr0pl.pk,
                'fuzzy': False,
                'string': unicode(tr0pl.string),
                'approved': False,
                'rejected': False,
            },
        ],
        'order':
        0,
        'source': [],
        'original_plural':
        unicode(entity_a.string_plural),
        'marked_plural':
        unicode(entity_a.string_plural),
        'pk':
        entity_a.pk,
        'original':
        unicode(entity_a.string),
        'readonly':
        False,
        'visible':
        False,
    }
    assert entities[0] == expected
コード例 #18
0
ファイル: test_entity.py プロジェクト: NeatNerdPrime/pontoon
def test_entity_project_locale_no_paths(
    admin,
    entity_test_models,
    locale_b,
    project_b,
):
    """
    If paths not specified, return all project entities along with their
    translations for locale.
    """
    tr0, tr0pl, trX, subpageX = entity_test_models
    locale_a = tr0.locale
    preferred_source_locale = ""
    entity_a = tr0.entity
    resource0 = tr0.entity.resource
    project_a = tr0.entity.resource.project
    entities = Entity.map_entities(
        locale_a,
        preferred_source_locale,
        Entity.for_project_locale(admin, project_a, locale_a),
    )
    assert len(entities) == 2
    assert entities[0]["path"] == resource0.path
    assert entities[0]["original"] == entity_a.string
    assert entities[0]["translation"][0]["string"] == tr0.string
    assert entities[1]["path"] == trX.entity.resource.path
    assert entities[1]["original"] == trX.entity.string
    assert entities[1]["translation"][0]["string"] == trX.string

    # Ensure all attributes are assigned correctly
    expected = {
        "comment":
        "",
        "group_comment":
        "",
        "resource_comment":
        "",
        "format":
        "po",
        "obsolete":
        False,
        "key":
        "",
        "context":
        "",
        "path":
        str(resource0.path),
        "project":
        project_a.serialize(),
        "translation": [
            {
                "pk": tr0.pk,
                "pretranslated": False,
                "fuzzy": False,
                "string": str(tr0.string),
                "approved": False,
                "rejected": False,
                "warnings": [],
                "errors": [],
            },
            {
                "pk": tr0pl.pk,
                "pretranslated": False,
                "fuzzy": False,
                "string": str(tr0pl.string),
                "approved": False,
                "rejected": False,
                "warnings": [],
                "errors": [],
            },
        ],
        "order":
        0,
        "source": [],
        "original_plural":
        str(entity_a.string_plural),
        "pk":
        entity_a.pk,
        "original":
        str(entity_a.string),
        "machinery_original":
        str(entity_a.string),
        "readonly":
        False,
        "visible":
        False,
        "is_sibling":
        False,
    }
    assert entities[0] == expected
コード例 #19
0
ファイル: views.py プロジェクト: CE-OP/pontoon
def entities(request):
    """Get entities for the specified project, locale and paths."""
    try:
        project = request.POST['project']
        locale = request.POST['locale']
        paths = request.POST.getlist('paths[]')
        limit = int(request.POST.get('limit', 50))
    except (MultiValueDictKeyError, ValueError) as err:
        return HttpResponseBadRequest('Bad Request: {error}'.format(error=err))

    project = get_object_or_404(Project, slug=project)
    locale = get_object_or_404(Locale, code__iexact=locale)

    filter_type = request.POST.get('filter', '')
    search = request.POST.get('search', '')
    exclude_entities = request.POST.getlist('excludeEntities[]', [])

    # Only return entities with provided IDs
    entity_ids = request.POST.getlist('entityIds[]', [])
    if entity_ids:
        entities = (
            Entity.objects.filter(pk__in=entity_ids)
            .prefetch_resources_translations(locale)
            .distinct()
            .order_by('order')
        )

        return JsonResponse({
            'entities': Entity.map_entities(locale, entities),
            'stats': TranslatedResource.objects.stats(project, paths, locale),
        }, safe=False)

    entities = Entity.for_project_locale(
        project, locale, paths, filter_type, search, exclude_entities
    )

    # Only return a list of entity PKs
    if request.POST.get('pkOnly', None):
        return JsonResponse({
            'entity_pks': list(entities.values_list('pk', flat=True)),
        })

    visible_entities = []

    # In-place view: load all entities
    if request.POST.get('inplaceEditor', None):
        has_next = False
        entities_to_map = Entity.for_project_locale(
            project, locale, paths, None, None, exclude_entities
        )
        visible_entities = entities.values_list('pk', flat=True)

    # Out-of-context view: paginate entities
    else:
        paginator = Paginator(entities, limit)

        try:
            entities_page = paginator.page(1)
        except EmptyPage:
            return JsonResponse({
                'has_next': False,
                'stats': {},
            })

        has_next = entities_page.has_next()
        entities_to_map = entities_page.object_list

        # If requested entity not on the first page
        entity = request.POST.get('entity', None)
        if entity:
            entity_pk = int(entity)
            # TODO: entities_to_map.values_list() doesn't return entities from selected page
            if entity_pk not in [e.pk for e in entities_to_map]:
                if entity_pk in entities.values_list('pk', flat=True):
                    entities_to_map = list(entities_to_map) + list(entities.filter(pk=entity_pk))

    return JsonResponse({
        'entities': Entity.map_entities(locale, entities_to_map, visible_entities),
        'has_next': has_next,
        'stats': TranslatedResource.objects.stats(project, paths, locale),
    }, safe=False)
コード例 #20
0
ファイル: test_entity.py プロジェクト: Pike/pontoon
def test_entity_project_locale_no_paths(
    entity_test_models,
    locale_b,
    project_b,
):
    """
    If paths not specified, return all project entities along with their
    translations for locale.
    """
    tr0, tr0pl, trX, subpageX = entity_test_models
    locale_a = tr0.locale
    entity_a = tr0.entity
    resource0 = tr0.entity.resource
    project_a = tr0.entity.resource.project
    entities = Entity.map_entities(
        locale_a,
        Entity.for_project_locale(project_a, locale_a),
    )
    assert len(entities) == 2
    assert entities[0]['path'] == resource0.path
    assert entities[0]['original'] == entity_a.string
    assert entities[0]['translation'][0]['string'] == tr0.string
    assert entities[1]['path'] == trX.entity.resource.path
    assert entities[1]['original'] == trX.entity.string
    assert entities[1]['translation'][0]['string'] == trX.string

    # Ensure all attributes are assigned correctly
    expected = {
        'comment': '',
        'format': 'po',
        'obsolete': False,
        'marked': unicode(entity_a.string),
        'key': '',
        'path': unicode(resource0.path),
        'project': project_a.serialize(),
        'translation': [
            {
                'pk': tr0.pk,
                'fuzzy': False,
                'string': unicode(tr0.string),
                'approved': False,
                'rejected': False,
                'warnings': [],
                'errors': [],
            },
            {
                'pk': tr0pl.pk,
                'fuzzy': False,
                'string': unicode(tr0pl.string),
                'approved': False,
                'rejected': False,
                'warnings': [],
                'errors': [],
            },
        ],
        'order': 0,
        'source': [],
        'original_plural': unicode(entity_a.string_plural),
        'marked_plural': unicode(entity_a.string_plural),
        'pk': entity_a.pk,
        'original': unicode(entity_a.string),
        'readonly': False,
        'visible': False,
    }
    assert entities[0] == expected
コード例 #21
0
ファイル: views.py プロジェクト: mastizada/pontoon
def entities(request):
    """Get entities for the specified project, locale and paths."""
    try:
        project = request.POST["project"]
        locale = request.POST["locale"]
        paths = request.POST.getlist("paths[]")
        limit = int(request.POST.get("limit", 50))
    except (MultiValueDictKeyError, ValueError) as e:
        return HttpResponseBadRequest("Bad Request: {error}".format(error=e))

    project = get_object_or_404(Project, slug=project)
    locale = get_object_or_404(Locale, code__iexact=locale)

    filter_type = request.POST.get("filterType", "")
    search = request.POST.get("search", "")
    exclude_entities = request.POST.getlist("excludeEntities[]", [])

    # Only return entities with provided IDs
    entity_ids = request.POST.getlist("entityIds[]", [])
    if entity_ids:
        entities = (
            Entity.objects.filter(pk__in=entity_ids)
            .prefetch_resources_translations(locale)
            .distinct()
            .order_by("order")
        )

        return JsonResponse(
            {
                "entities": Entity.map_entities(locale, entities),
                "stats": TranslatedResource.objects.stats(project, paths, locale),
            },
            safe=False,
        )

    entities = Entity.for_project_locale(project, locale, paths, filter_type, search, exclude_entities)

    # Only return a list of entity PKs
    if request.POST.get("pkOnly", None):
        return JsonResponse({"entity_pks": list(entities.values_list("pk", flat=True))})

    # In-place view: load all entities
    if request.POST.get("inplaceEditor", None):
        has_next = False
        entities_to_map = entities

    # Out-of-context view: paginate entities
    else:
        paginator = Paginator(entities, limit)

        try:
            entities_page = paginator.page(1)
        except EmptyPage:
            return JsonResponse({"has_next": False, "stats": {}})

        has_next = entities_page.has_next()
        entities_to_map = entities_page.object_list

    return JsonResponse(
        {
            "entities": Entity.map_entities(locale, entities_to_map),
            "has_next": has_next,
            "stats": TranslatedResource.objects.stats(project, paths, locale),
        },
        safe=False,
    )