Comments 4
Ну с текстом понятно.. А что вы будете делать когда, внезапно, узнаете, что цифры тоже надо переводить?
Помнится, я экспрессивно выразил легкое недоумение, когда у меня заказчик, неожиданно, попросил показывать цифры на арабском в уже полузаконченном проекте. А все страницы состояли из таблиц с числами, плюс экспорт в pdf.
У меня нет задачи доказать, что все неправильно, но пару моментов вам точно стоит обдумать уже сейчас, что бы не мозолить пятую точку позже:
Back-end:
ModelTranslation базируется на статических данных в settings.py - LANGUAGES, и в файле регистрации переводов в ModelTranslation. В итоге данные по переводным полям каждой модели и языкам существует еще до старта проекта. Это значит, что объявить Meta.fields можно уже на этапе объявления класса, а не выполнять бессмысленно одну и ту же работу на каждой инициализации сериализатора: get_fields() вызывается на __init__. Ну или хоть закешируйте результат.
У ModelTranslation есть скрытый косяк, при работе с языками - если язык удален, то после миграции будет снесена колонка переводов на этот язык. Т.е. Один из языков нельзя временно поставить на паузу. Вы поймете, как это важно, когда у вас языков будет много, а переводчики работают не так быстро как хочется.
Посмотрите исполнительность кода на 10 переводных полях модели, на 10 языках. Вероятно, сразу после этого захотите что-то поменять. Передположу, что могут появиться несколько сериализаторов для одной и той же модели.
Поскольку я совсем разлюбил ModelTranslation за 6 лет работы с ним, я создал DAJNGO-TOF, уже есть полностью переписанная V.2. Вам, вероятно, пригодится. Там статика переводится, json языковой для Vue-i18n генерится, статика с фронта тоже собирается на перевод.
Front-End:
Вместо лютого хардкода в 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)
Что я выиграл - нет зависимости от списка переводных полей, мне не нужна эта функция. Нет отдельных геттеров, правда, есть один глобальный.
Cписок переводных полей отдельная тема. У вас на бэк поля на перевод в своем списке добавляются, а на фронт в своем списке. Если проект большой и растет и работают несколько команд - ой полыхнет когда нить. Оно всегда когда-нибудь полыхает, но с переводными полями этого точно можно избежать назначив one_source_of_truth.
Мой совет. не передавайте все переводы. ЭТО НЕ НАДО! У нормального клиента происходит максимум один раз переключение в языках, это если вы плохо определили язык клиента до старта. Или переключение между двумя в очень редких случаях, когда он сознательно что-то сравнивает (название ингридиентов рецепта, побочные действия медикамента и т.п.). Если кеширование настроено нормально и руки норм, то все работает без доп запросов. Для проверки, может я шучу, опять же поработайте с объектами 10 полей, 10 переводов - и залогируйте сколько процентов информации было использовано.
Не понял прикола с
this.name = this.getField(data)
, у вас это уже дескриптор, зачем еще обертка?
В общем все очень странно, и выглядит, что вы, или еще это не тестировали, то ли вам все равно, потому как 2 языка и одно переводное поле и вы еще ничего не заметили.
Успеха в любом случае.
Добрый день! В первую очередь хотелось бы сказать спасибо за такой развернутый отзыв ?
Теперь попробую ответить/прокомментировать по пунктам.
Back-end:
Было несколько вариантов того, как реализовать работу с переводами. Тот, на котором мы остановились, предполагал изменение всех сериализаторов (как раз добавление полей с переводами в
Meta.fields
), и это было решено сделать с помощью миксина, т.к. требовало только добавление нового родителя.Предложение закешировать хорошее, уже добавил 3 строки в миксин и время второй и следующих сериализаций уменьшилось с 0.1 до 0.01 сек. ??
Если язык удалён - скорее всего это обдуманный шаг. Про медленных переводчиков: и в
modeltranslation
и вdjango-tof
есть fallback языки, которые как раз и нужны для того, чтобы определить список запасных языков, если на выбранный язык перевода нет. Во-вторых, есть вариант программно ограничить список языков с которыми могут работать пользователи, до тех пор пока переводы не будут добавлены.У
ModelTranslation
есть косяки, но есть и плюсы - отсутствие дополнительных запросов в БД. Насколько я понял вы имеете ввиду слишком большую таблицу (10 полей х 10 переводов - уже минимум 110 полей в таблице), но тут уже вопрос не кModelTranslation
, а к выбору технологий - зачем начинать использоватьModelTranslation
на проекте с таким большим количеством языков.У вашего решения тоже есть минусы, как автор вы это знаете. К тому же репозиторий не обновляется уже около 4-х лет. Возможно в какой-то момент вы столкнулись с проблемой, которую сложно решить (возможно плотная интеграция в ORM)? В любом случае - спасибо, это ещё один вариант, который стоит рассмотреть.
Front-End:
Крайне не понял про "лютый хардкод" в моделях. Всё что происходит - наследование и указание списка полей. Во-вторых - класс
TranslatableModel
делает чуть больше, чем ваш пример. Ваш пример очень упрощен, в нём описан только getter свойств (причем для каждого свойства будет сначала выполнена проверка наличия атрибута с названием <название_свойства>_<язык>), и если расширить этот пример до класса, который будет +- с таким же функционалом - я не думаю, что он будет намного меньше.Хорошее предложение, к тому же и реализуется очень просто. Спасибо ??
Я с вами полностью согласен, была бы моя воля - я бы вообще запретил переводы для динамических данных. С другой стороны есть ТЗ со своими требованиями. Отправка данных на всех языках обусловлена к тому же тем, как мы работаем с данными - после того как сервере произошло событие (например создание объекта в БД) нам необходимо отправить этот объект всем подключенным пользователям по WS, и этот новый объект должен появится у пользователя на том языке, который у него активирован.
Эта обертка нужна только для того, чтобы можно было перейти в метод
getField
и посмотреть на комментарий, который у него написан. Это лучший вариант, чем писать в каждой конкретной модели что значитthis.name = data; this.desc = data
и почему каждому свойству присваивается data, когда data - это все данные объекта.
На самом деле у нас несколько проектов, которые используют описаный подход (в проектах > 10 моделей с переводами и > 20 переводимых полей) и нет никаких проблем в работе с переводами. После реализации этого кода добавление новых сущностей с переводами не доставляет проблем - нужно просто добавить миксин в сериализатор и в класс модели на фронте, а всё остальное уже работает. Именно из-за простоты реализации и использования захотелось поделиться тем, что получилось.
В любом случае - нет единого подхода для решения похожей задачи в разных проектах, и нужно понимать куда движется проект и как он будет развиваться чтобы принимать решение об использовании нашего подхода или какого-либо другого.
Спасибо за ответ.
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. Но конечно всегда есть контекст которого я в ващем случае не знаю.
Если язык удалён - скорее всего это обдуманный шаг.
Удален да. А еще бывает поставлен на паузу. В ModelTranslation - это невозможно, потому, что оно базируется на settings languages. Отключение языка необходимо, например, для изменения работы переводных URL через i18n_patterns. В этом случае modeltranslation добавляет/удаляет колонки с переводами в таблице. Это дичь. Нет ни одного другого settings, изменение которого меняет содержимое таблиц. Не добавляет или удаляет таблицы. Меняет колонки!!! Это последнее, что я бы ожидал от строчки в settings.
У
ModelTranslation
есть косяки, но есть и плюсы - отсутствие дополнительных запросов в БД.Буквально вчера было обсуждение, что плюс - это когда хороший план исполнения запросов, а не когда один запрос или несколько. Я вот, например, всегда думал, что Join быстрее subquery. Пока не нашел статью IBM от 2004, что нет, не быстрее. И потом еще и сам проверил. Я к тому что считать кодичество запросов без плана так себе затея (https://habr.com/ru/companies/axenix/articles/787944/)
Для status qwo - Django-TOF V2 updated 6 months ago. А так - написано независимо от версии питона, и от версии Django. Мне кажется, что качество репозитория характеризуется не тем, насколько регулярно происходят правки, а насколько много еще открытых issues. Косяк пока знаю один, сортировку по значению переводного поля не сделал, поскольку у разных переводов свой fallback у каталонского - fallback испанский и только потом английский. в Modeltranslation про многослойный fallback даже не слышали.
"Proxy". Ваш пример очень упрощен. Если расширить этот пример до класса.
Мой пример позволяет получать как текущий перевод shape.transfield, так и любой языковой shape.transfield_en например. А еще, class Proxy работает, как очень любопытный миксин, всячески рекомендую изучить. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
Этот объект отправить всем подключенным пользователям по WS, на том языке, который у него активирован.
WS соединение всегда можно открывать со статусом языка. Слать сообщение если язык поменялся на клиенте, и передавать минимум информации. А то, какой язык на клиенте, вы скорее всего и так знаете, поскольку сообщения от пользователя, вероятно, приходят на каком одном языке и не переведенные на все. Если нет, то это какое то странное мультиязычное бизнес решение. Поделитесь ссылочкой посмотреть и поучиться.
this.name = data; this.desc = data.
Ээ. Не надо так. Может лучше this.data = data. а get/set дескрипторов просто обращаются к this.data ?? Ну иначе overhead получается.
В общем, чего я тут спорю? У вас есть решение, которое пока работает и решает задачу. Так и прекрасно! Я заметил сложности, которые вам еще предстоит решить... или нет. Всякое бывает. Удачи проекту и поделитесь ссылочкой на посмотреть.
Переводы полей моделей Django + Vue