Search
Write a publication
Pull to refresh

Comments 4

Ну с текстом понятно.. А что вы будете делать когда, внезапно, узнаете, что цифры тоже надо переводить?

Помнится, я экспрессивно выразил легкое недоумение, когда у меня заказчик, неожиданно, попросил показывать цифры на арабском в уже полузаконченном проекте. А все страницы состояли из таблиц с числами, плюс экспорт в pdf.

У меня нет задачи доказать, что все неправильно, но пару моментов вам точно стоит обдумать уже сейчас, что бы не мозолить пятую точку позже:

Back-end:

  1. ModelTranslation базируется на статических данных в settings.py - LANGUAGES, и в файле регистрации переводов в ModelTranslation. В итоге данные по переводным полям каждой модели и языкам существует еще до старта проекта. Это значит, что объявить Meta.fields можно уже на этапе объявления класса, а не выполнять бессмысленно одну и ту же работу на каждой инициализации сериализатора: get_fields() вызывается на __init__. Ну или хоть закешируйте результат.

  2. У ModelTranslation есть скрытый косяк, при работе с языками - если язык удален, то после миграции будет снесена колонка переводов на этот язык. Т.е. Один из языков нельзя временно поставить на паузу. Вы поймете, как это важно, когда у вас языков будет много, а переводчики работают не так быстро как хочется.

  3. Посмотрите исполнительность кода на 10 переводных полях модели, на 10 языках. Вероятно, сразу после этого захотите что-то поменять. Передположу, что могут появиться несколько сериализаторов для одной и той же модели.

  4. Поскольку я совсем разлюбил ModelTranslation за 6 лет работы с ним, я создал DAJNGO-TOF, уже есть полностью переписанная V.2. Вам, вероятно, пригодится. Там статика переводится, json языковой для Vue-i18n генерится, статика с фронта тоже собирается на перевод.

Front-End:

  1. Вместо лютого хардкода в c моделями используйте Proxy. Почему это хорошо? Потому, что всего несколько строк кода:

    let handler = {

    get(target, name) {

    let lang = 'en' // define somehow your lang

    return target[`${n}_{lang}`] || target[n]

    }}

    def Shape = Proxy(shape_object, handler)

    Что я выиграл - нет зависимости от списка переводных полей, мне не нужна эта функция. Нет отдельных геттеров, правда, есть один глобальный.

  2. Cписок переводных полей отдельная тема. У вас на бэк поля на перевод в своем списке добавляются, а на фронт в своем списке. Если проект большой и растет и работают несколько команд - ой полыхнет когда нить. Оно всегда когда-нибудь полыхает, но с переводными полями этого точно можно избежать назначив one_source_of_truth.

  3. Мой совет. не передавайте все переводы. ЭТО НЕ НАДО! У нормального клиента происходит максимум один раз переключение в языках, это если вы плохо определили язык клиента до старта. Или переключение между двумя в очень редких случаях, когда он сознательно что-то сравнивает (название ингридиентов рецепта, побочные действия медикамента и т.п.). Если кеширование настроено нормально и руки норм, то все работает без доп запросов. Для проверки, может я шучу, опять же поработайте с объектами 10 полей, 10 переводов - и залогируйте сколько процентов информации было использовано.

  4. Не понял прикола с this.name = this.getField(data) , у вас это уже дескриптор, зачем еще обертка?

В общем все очень странно, и выглядит, что вы, или еще это не тестировали, то ли вам все равно, потому как 2 языка и одно переводное поле и вы еще ничего не заметили.

Успеха в любом случае.

Добрый день! В первую очередь хотелось бы сказать спасибо за такой развернутый отзыв ?

Теперь попробую ответить/прокомментировать по пунктам.

Back-end:

  1. Было несколько вариантов того, как реализовать работу с переводами. Тот, на котором мы остановились, предполагал изменение всех сериализаторов (как раз добавление полей с переводами в Meta.fields), и это было решено сделать с помощью миксина, т.к. требовало только добавление нового родителя.

    Предложение закешировать хорошее, уже добавил 3 строки в миксин и время второй и следующих сериализаций уменьшилось с 0.1 до 0.01 сек. ??

  2. Если язык удалён - скорее всего это обдуманный шаг. Про медленных переводчиков: и в modeltranslation и в django-tof есть fallback языки, которые как раз и нужны для того, чтобы определить список запасных языков, если на выбранный язык перевода нет. Во-вторых, есть вариант программно ограничить список языков с которыми могут работать пользователи, до тех пор пока переводы не будут добавлены.

  3. У ModelTranslation есть косяки, но есть и плюсы - отсутствие дополнительных запросов в БД. Насколько я понял вы имеете ввиду слишком большую таблицу (10 полей х 10 переводов - уже минимум 110 полей в таблице), но тут уже вопрос не к ModelTranslation, а к выбору технологий - зачем начинать использовать ModelTranslation на проекте с таким большим количеством языков.

  4. У вашего решения тоже есть минусы, как автор вы это знаете. К тому же репозиторий не обновляется уже около 4-х лет. Возможно в какой-то момент вы столкнулись с проблемой, которую сложно решить (возможно плотная интеграция в ORM)? В любом случае - спасибо, это ещё один вариант, который стоит рассмотреть.

Front-End:

  1. Крайне не понял про "лютый хардкод" в моделях. Всё что происходит - наследование и указание списка полей. Во-вторых - класс TranslatableModel делает чуть больше, чем ваш пример. Ваш пример очень упрощен, в нём описан только getter свойств (причем для каждого свойства будет сначала выполнена проверка наличия атрибута с названием <название_свойства>_<язык>), и если расширить этот пример до класса, который будет +- с таким же функционалом - я не думаю, что он будет намного меньше.

  2. Хорошее предложение, к тому же и реализуется очень просто. Спасибо ??

  3. Я с вами полностью согласен, была бы моя воля - я бы вообще запретил переводы для динамических данных. С другой стороны есть ТЗ со своими требованиями. Отправка данных на всех языках обусловлена к тому же тем, как мы работаем с данными - после того как сервере произошло событие (например создание объекта в БД) нам необходимо отправить этот объект всем подключенным пользователям по WS, и этот новый объект должен появится у пользователя на том языке, который у него активирован.

  4. Эта обертка нужна только для того, чтобы можно было перейти в метод getField и посмотреть на комментарий, который у него написан. Это лучший вариант, чем писать в каждой конкретной модели что значит this.name = data; this.desc = data и почему каждому свойству присваивается data, когда data - это все данные объекта.

На самом деле у нас несколько проектов, которые используют описаный подход (в проектах > 10 моделей с переводами и > 20 переводимых полей) и нет никаких проблем в работе с переводами. После реализации этого кода добавление новых сущностей с переводами не доставляет проблем - нужно просто добавить миксин в сериализатор и в класс модели на фронте, а всё остальное уже работает. Именно из-за простоты реализации и использования захотелось поделиться тем, что получилось.

В любом случае - нет единого подхода для решения похожей задачи в разных проектах, и нужно понимать куда движется проект и как он будет развиваться чтобы принимать решение об использовании нашего подхода или какого-либо другого.

Спасибо за ответ.

  1. Meta.fields решено было сделать с помощью миксина, т.к. требовало только добавление нового родителя.

    Минимум это создание миксина, добавление строки импорта, правка в обявлении каждого сериалайзера тестирование и вызов кода на каждый реквест.

    Есть второй вариант - вы пишете функцию сбора полей, по модели и языкам.

    from django.conf import settings

    from modeltranslation.manager import get_translatable_fields_for_model

    def collect_translatable_fields(model_cls):

        for field in get_translatable_fields_for_model(model_cls):

            yield from (f'{field.name}_{lang}' for lang in settings.LANGUAGES)

    А потом импортируете функцию и прописываете в каждом сериализаторе

    class Meta:

    model = MySuperTranslatedModel

    fields = not, translatable, fields, *collect_translatable_fields(model)

    столько же линий правок, но код выполняется только на объявлении сериализатора. Но не на каждый запрос, пусть результат вычислений и закеширован. При сравнении -

    вы поменяете 0.1 сек не на 0.01 сек, а 0.1 сек на 0. Но конечно всегда есть контекст которого я в ващем случае не знаю.

  1. Если язык удалён - скорее всего это обдуманный шаг.

    Удален да. А еще бывает поставлен на паузу. В ModelTranslation - это невозможно, потому, что оно базируется на settings languages. Отключение языка необходимо, например, для изменения работы переводных URL через i18n_patterns. В этом случае modeltranslation добавляет/удаляет колонки с переводами в таблице. Это дичь. Нет ни одного другого settings, изменение которого меняет содержимое таблиц. Не добавляет или удаляет таблицы. Меняет колонки!!! Это последнее, что я бы ожидал от строчки в settings.

  2. У ModelTranslation есть косяки, но есть и плюсы - отсутствие дополнительных запросов в БД.

    Буквально вчера было обсуждение, что плюс - это когда хороший план исполнения запросов, а не когда один запрос или несколько. Я вот, например, всегда думал, что Join быстрее subquery. Пока не нашел статью IBM от 2004, что нет, не быстрее. И потом еще и сам проверил. Я к тому что считать кодичество запросов без плана так себе затея (https://habr.com/ru/companies/axenix/articles/787944/)

  3. Для status qwo - Django-TOF V2 updated 6 months ago. А так - написано независимо от версии питона, и от версии Django. Мне кажется, что качество репозитория характеризуется не тем, насколько регулярно происходят правки, а насколько много еще открытых issues. Косяк пока знаю один, сортировку по значению переводного поля не сделал, поскольку у разных переводов свой fallback у каталонского - fallback испанский и только потом английский. в Modeltranslation про многослойный fallback даже не слышали.

  4. "Proxy". Ваш пример очень упрощен. Если расширить этот пример до класса.

    Мой пример позволяет получать как текущий перевод shape.transfield, так и любой языковой shape.transfield_en например. А еще, class Proxy работает, как очень любопытный миксин, всячески рекомендую изучить. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy

  5. Этот объект отправить всем подключенным пользователям по WS, на том языке, который у него активирован.

    WS соединение всегда можно открывать со статусом языка. Слать сообщение если язык поменялся на клиенте, и передавать минимум информации. А то, какой язык на клиенте, вы скорее всего и так знаете, поскольку сообщения от пользователя, вероятно, приходят на каком одном языке и не переведенные на все. Если нет, то это какое то странное мультиязычное бизнес решение. Поделитесь ссылочкой посмотреть и поучиться.

  6. this.name = data; this.desc = data.

    Ээ. Не надо так. Может лучше this.data = data. а get/set дескрипторов просто обращаются к this.data ?? Ну иначе overhead получается.

В общем, чего я тут спорю? У вас есть решение, которое пока работает и решает задачу. Так и прекрасно! Я заметил сложности, которые вам еще предстоит решить... или нет. Всякое бывает. Удачи проекту и поделитесь ссылочкой на посмотреть.

Sign up to leave a comment.