diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index ee726c45d..93be5c639 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -375,6 +375,7 @@ class ConnectionTokenViewSet(AuthFaceMixin, ExtraActionApiMixin, RootOrgViewMixi for name in default_name_opts.keys(): value = preferences.get(name, default_name_opts[name]) connect_options[name] = value + connect_options['lang'] = getattr(user, 'lang', settings.LANGUAGE_CODE) data['connect_options'] = connect_options @staticmethod diff --git a/apps/authentication/serializers/connection_token.py b/apps/authentication/serializers/connection_token.py index cb8759fef..3c176f57c 100644 --- a/apps/authentication/serializers/connection_token.py +++ b/apps/authentication/serializers/connection_token.py @@ -75,6 +75,7 @@ class SuperConnectionTokenSerializer(ConnectionTokenSerializer): def get_user(self, attrs): return attrs.get('user') + class AdminConnectionTokenSerializer(ConnectionTokenSerializer): class Meta(ConnectionTokenSerializer.Meta): model = AdminConnectionToken diff --git a/apps/authentication/signal_handlers.py b/apps/authentication/signal_handlers.py index 9ba927075..4dd2caa2f 100644 --- a/apps/authentication/signal_handlers.py +++ b/apps/authentication/signal_handlers.py @@ -37,7 +37,7 @@ def on_user_auth_login_success(sender, user, request, **kwargs): cache.set(lock_key, request.session.session_key, None) lang = request.COOKIES.get('django_language') - if lang: + if not user.lang and lang: user.lang = lang diff --git a/apps/jumpserver/middleware.py b/apps/jumpserver/middleware.py index ad6bb496d..b9fff7b35 100644 --- a/apps/jumpserver/middleware.py +++ b/apps/jumpserver/middleware.py @@ -10,7 +10,7 @@ from django.conf import settings from django.core.exceptions import MiddlewareNotUsed from django.http.response import HttpResponseForbidden from django.shortcuts import HttpResponse -from django.utils import timezone +from django.utils import timezone, translation from .utils import set_current_request @@ -137,3 +137,30 @@ class EndMiddleware: response = self.get_response(request) request._e_time_end = time.time() return response + + +class LocaleMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + response = self.get_response(request) + lang = None + if request.user.is_authenticated: + lang = getattr(request.user, 'lang', None) + + if not lang: + lang = translation.get_language_from_request(request, check_path=False) + + lang = lang or settings.LANGUAGE_CODE + try: + translation.activate(lang) + request.LANGUAGE_CODE = lang + response.set_cookie( + settings.LANGUAGE_COOKIE_NAME, + lang, + expires=timezone.now() + timezone.timedelta(days=365) + ) + except Exception: + pass + return response diff --git a/apps/jumpserver/settings/base.py b/apps/jumpserver/settings/base.py index 00bb729c4..11169db06 100644 --- a/apps/jumpserver/settings/base.py +++ b/apps/jumpserver/settings/base.py @@ -165,6 +165,7 @@ MIDDLEWARE = [ 'jumpserver.middleware.RequestMiddleware', 'jumpserver.middleware.RefererCheckMiddleware', 'jumpserver.middleware.SQLCountMiddleware', + 'jumpserver.middleware.LocaleMiddleware', 'orgs.middleware.OrgMiddleware', 'authentication.backends.oidc.middleware.OIDCRefreshIDTokenMiddleware', 'authentication.backends.cas.middleware.CASMiddleware', diff --git a/apps/notifications/notifications.py b/apps/notifications/notifications.py index d200cc41b..b61ecc9b3 100644 --- a/apps/notifications/notifications.py +++ b/apps/notifications/notifications.py @@ -11,6 +11,7 @@ from common.utils.timezone import local_now from notifications.backends import BACKEND from settings.utils import get_login_title from users.models import User +from users.utils import activate_user_language from .models import SystemMsgSubscription, UserMsgSubscription __all__ = ('SystemMessage', 'UserMessage', 'system_msgs', 'Message') @@ -267,13 +268,13 @@ class SystemMessage(Message): *subscription.users.all(), *chain(*[g.users.all() for g in subscription.groups.all()]) ] - - receive_user_ids = [u.id for u in users] - backends_msg_mapper = self.get_backend_msg_mapper(receive_backends) - if is_async: - publish_task.delay(receive_user_ids, backends_msg_mapper) - else: - self.send_msg(receive_user_ids, backends_msg_mapper) + for user in users: + with activate_user_language(user): + backends_msg_mapper = self.get_backend_msg_mapper(receive_backends) + if is_async: + publish_task.delay([user.id], backends_msg_mapper) + else: + self.send_msg([user.id], backends_msg_mapper) @classmethod def post_insert_to_db(cls, subscription: SystemMsgSubscription): @@ -295,12 +296,13 @@ class UserMessage(Message): 发送消息到每个用户配置的接收方式上 """ sub = UserMsgSubscription.objects.get(user=self.user) - backends_msg_mapper = self.get_backend_msg_mapper(sub.receive_backends) - receive_user_ids = [self.user.id] - if is_async: - publish_task.delay(receive_user_ids, backends_msg_mapper) - else: - self.send_msg(receive_user_ids, backends_msg_mapper) + with activate_user_language(self.user): + backends_msg_mapper = self.get_backend_msg_mapper(sub.receive_backends) + receive_user_ids = [self.user.id] + if is_async: + publish_task.delay(receive_user_ids, backends_msg_mapper) + else: + self.send_msg(receive_user_ids, backends_msg_mapper) @classmethod def get_test_user(cls): diff --git a/apps/users/serializers/preference/lina.py b/apps/users/serializers/preference/lina.py index 8c2b906df..38a39fb44 100644 --- a/apps/users/serializers/preference/lina.py +++ b/apps/users/serializers/preference/lina.py @@ -48,5 +48,5 @@ class BasicSerializer(serializers.Serializer): class LinaSerializer(serializers.Serializer): - # basic = BasicSerializer(required=False, label=_('Basic')) + basic = BasicSerializer(required=False, label=_('Basic')) file = FileEncryptSerializer(required=False, label=_('File Encryption')) diff --git a/apps/users/serializers/profile.py b/apps/users/serializers/profile.py index c61edc1c7..45df2a718 100644 --- a/apps/users/serializers/profile.py +++ b/apps/users/serializers/profile.py @@ -63,6 +63,7 @@ class UserProfileSerializer(UserSerializer): mfa_level = LabeledChoiceField(choices=MFAMixin.MFA_LEVEL_CHOICES, label=_("MFA"), required=False) guide_url = serializers.SerializerMethodField() receive_backends = serializers.ListField(child=serializers.CharField(), read_only=True) + lang = serializers.SerializerMethodField(label=_("Language")) class Meta(UserSerializer.Meta): read_only_fields = [ @@ -74,7 +75,7 @@ class UserProfileSerializer(UserSerializer): ] fields = UserSerializer.Meta.fields + [ 'public_key_comment', 'public_key_hash_md5', 'guide_url', - "wecom_id", "dingtalk_id", "feishu_id", "slack_id", + "wecom_id", "dingtalk_id", "feishu_id", "slack_id", 'lang' ] + read_only_fields extra_kwargs = dict(UserSerializer.Meta.extra_kwargs) @@ -144,6 +145,9 @@ class UserProfileSerializer(UserSerializer): raise serializers.ValidationError(msg) return password + def get_lang(self, obj): + return getattr(self.instance, 'lang', settings.LANGUAGE_CODE) + class UserPKUpdateSerializer(serializers.ModelSerializer): class Meta: diff --git a/apps/users/utils.py b/apps/users/utils.py index 173f99847..3bcc9b1ee 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -6,10 +6,12 @@ import logging import os import re import time +from contextlib import contextmanager import pyotp from django.conf import settings from django.core.cache import cache +from django.utils import translation from django.utils.translation import gettext as _ from common.tasks import send_mail_async @@ -336,3 +338,10 @@ def is_confirm_time_valid(session, key): def is_auth_confirm_time_valid(session): return is_confirm_time_valid(session, 'MFA_VERIFY_TIME') + + +@contextmanager +def activate_user_language(user): + language = getattr(user, 'lang', settings.LANGUAGE_CODE) + with translation.override(language): + yield