From 2bcf96565a94c48edad441eb0f685174edfd30ca Mon Sep 17 00:00:00 2001 From: feelshana <151412598@qq.com> Date: Tue, 21 Oct 2025 15:53:12 +0800 Subject: [PATCH] Feature:during account initialization, set the interface language to be consistent with the display language(#27029) (#27042) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> --- api/constants/languages.py | 6 ++++++ api/controllers/console/auth/login.py | 8 ++++++-- api/controllers/console/setup.py | 7 ++++++- api/services/account_service.py | 9 ++++----- api/tests/integration_tests/conftest.py | 1 + .../services/test_account_service.py | 2 ++ api/tests/unit_tests/services/test_account_service.py | 3 ++- web/app/install/installForm.tsx | 3 ++- web/app/signin/check-code/page.tsx | 5 +++-- web/service/common.ts | 2 +- 10 files changed, 33 insertions(+), 13 deletions(-) diff --git a/api/constants/languages.py b/api/constants/languages.py index a509ddcf5d..0312a558c9 100644 --- a/api/constants/languages.py +++ b/api/constants/languages.py @@ -31,3 +31,9 @@ def supported_language(lang): error = f"{lang} is not a valid language." raise ValueError(error) + + +def get_valid_language(lang: str | None) -> str: + if lang and lang in languages: + return lang + return languages[0] diff --git a/api/controllers/console/auth/login.py b/api/controllers/console/auth/login.py index f371613bee..c0a565b5da 100644 --- a/api/controllers/console/auth/login.py +++ b/api/controllers/console/auth/login.py @@ -4,7 +4,7 @@ from flask_restx import Resource, reqparse import services from configs import dify_config -from constants.languages import languages +from constants.languages import get_valid_language from controllers.console import console_ns from controllers.console.auth.error import ( AuthenticationFailedError, @@ -204,10 +204,12 @@ class EmailCodeLoginApi(Resource): .add_argument("email", type=str, required=True, location="json") .add_argument("code", type=str, required=True, location="json") .add_argument("token", type=str, required=True, location="json") + .add_argument("language", type=str, required=False, location="json") ) args = parser.parse_args() user_email = args["email"] + language = args["language"] token_data = AccountService.get_email_code_login_data(args["token"]) if token_data is None: @@ -241,7 +243,9 @@ class EmailCodeLoginApi(Resource): if account is None: try: account = AccountService.create_account_and_tenant( - email=user_email, name=user_email, interface_language=languages[0] + email=user_email, + name=user_email, + interface_language=get_valid_language(language), ) except WorkSpaceNotAllowedCreateError: raise NotAllowedCreateWorkspace() diff --git a/api/controllers/console/setup.py b/api/controllers/console/setup.py index 6d2b22bde3..1200349e2d 100644 --- a/api/controllers/console/setup.py +++ b/api/controllers/console/setup.py @@ -74,12 +74,17 @@ class SetupApi(Resource): .add_argument("email", type=email, required=True, location="json") .add_argument("name", type=StrLen(30), required=True, location="json") .add_argument("password", type=valid_password, required=True, location="json") + .add_argument("language", type=str, required=False, location="json") ) args = parser.parse_args() # setup RegisterService.setup( - email=args["email"], name=args["name"], password=args["password"], ip_address=extract_remote_ip(request) + email=args["email"], + name=args["name"], + password=args["password"], + ip_address=extract_remote_ip(request), + language=args["language"], ) return {"result": "success"}, 201 diff --git a/api/services/account_service.py b/api/services/account_service.py index cb0eb7a9dd..13c3993fb5 100644 --- a/api/services/account_service.py +++ b/api/services/account_service.py @@ -13,7 +13,7 @@ from sqlalchemy.orm import Session from werkzeug.exceptions import Unauthorized from configs import dify_config -from constants.languages import language_timezone_mapping, languages +from constants.languages import get_valid_language, language_timezone_mapping from events.tenant_event import tenant_was_created from extensions.ext_database import db from extensions.ext_redis import redis_client, redis_fallback @@ -1259,7 +1259,7 @@ class RegisterService: return f"member_invite:token:{token}" @classmethod - def setup(cls, email: str, name: str, password: str, ip_address: str): + def setup(cls, email: str, name: str, password: str, ip_address: str, language: str): """ Setup dify @@ -1269,11 +1269,10 @@ class RegisterService: :param ip_address: ip address """ try: - # Register account = AccountService.create_account( email=email, name=name, - interface_language=languages[0], + interface_language=get_valid_language(language), password=password, is_setup=True, ) @@ -1315,7 +1314,7 @@ class RegisterService: account = AccountService.create_account( email=email, name=name, - interface_language=language or languages[0], + interface_language=get_valid_language(language), password=password, is_setup=is_setup, ) diff --git a/api/tests/integration_tests/conftest.py b/api/tests/integration_tests/conftest.py index 9dc7b76e04..4395a9815a 100644 --- a/api/tests/integration_tests/conftest.py +++ b/api/tests/integration_tests/conftest.py @@ -58,6 +58,7 @@ def setup_account(request) -> Generator[Account, None, None]: name=name, password=secrets.token_hex(16), ip_address="localhost", + language="en-US", ) with _CACHED_APP.test_request_context(): diff --git a/api/tests/test_containers_integration_tests/services/test_account_service.py b/api/tests/test_containers_integration_tests/services/test_account_service.py index c59fc50f08..4d4e77a802 100644 --- a/api/tests/test_containers_integration_tests/services/test_account_service.py +++ b/api/tests/test_containers_integration_tests/services/test_account_service.py @@ -2299,6 +2299,7 @@ class TestRegisterService: name=admin_name, password=admin_password, ip_address=ip_address, + language="en-US", ) # Verify account was created @@ -2348,6 +2349,7 @@ class TestRegisterService: name=admin_name, password=admin_password, ip_address=ip_address, + language="en-US", ) # Verify no entities were created (rollback worked) diff --git a/api/tests/unit_tests/services/test_account_service.py b/api/tests/unit_tests/services/test_account_service.py index 737202f8de..627a04bcd0 100644 --- a/api/tests/unit_tests/services/test_account_service.py +++ b/api/tests/unit_tests/services/test_account_service.py @@ -893,7 +893,7 @@ class TestRegisterService: mock_dify_setup.return_value = mock_dify_setup_instance # Execute test - RegisterService.setup("admin@example.com", "Admin User", "password123", "192.168.1.1") + RegisterService.setup("admin@example.com", "Admin User", "password123", "192.168.1.1", "en-US") # Verify results mock_create_account.assert_called_once_with( @@ -925,6 +925,7 @@ class TestRegisterService: "Admin User", "password123", "192.168.1.1", + "en-US", ) # Verify rollback operations were called diff --git a/web/app/install/installForm.tsx b/web/app/install/installForm.tsx index 0a534b72fe..01bfd59b6d 100644 --- a/web/app/install/installForm.tsx +++ b/web/app/install/installForm.tsx @@ -35,7 +35,7 @@ type AccountFormValues = z.infer const InstallForm = () => { useDocumentTitle('') - const { t } = useTranslation() + const { t, i18n } = useTranslation() const docLink = useDocLink() const router = useRouter() const [showPassword, setShowPassword] = React.useState(false) @@ -58,6 +58,7 @@ const InstallForm = () => { await setup({ body: { ...data, + language: i18n.language, }, }) diff --git a/web/app/signin/check-code/page.tsx b/web/app/signin/check-code/page.tsx index da6bd426af..adbde377a1 100644 --- a/web/app/signin/check-code/page.tsx +++ b/web/app/signin/check-code/page.tsx @@ -13,12 +13,13 @@ import I18NContext from '@/context/i18n' import { resolvePostLoginRedirect } from '../utils/post-login-redirect' export default function CheckCode() { - const { t } = useTranslation() + const { t, i18n } = useTranslation() const router = useRouter() const searchParams = useSearchParams() const email = decodeURIComponent(searchParams.get('email') as string) const token = decodeURIComponent(searchParams.get('token') as string) const invite_token = decodeURIComponent(searchParams.get('invite_token') || '') + const language = i18n.language const [code, setVerifyCode] = useState('') const [loading, setIsLoading] = useState(false) const { locale } = useContext(I18NContext) @@ -40,7 +41,7 @@ export default function CheckCode() { return } setIsLoading(true) - const ret = await emailLoginWithCode({ email, code, token }) + const ret = await emailLoginWithCode({ email, code, token, language }) if (ret.result === 'success') { if (invite_token) { router.replace(`/signin/invite-settings?${searchParams.toString()}`) diff --git a/web/service/common.ts b/web/service/common.ts index 8f2adc329e..55dec33cb5 100644 --- a/web/service/common.ts +++ b/web/service/common.ts @@ -345,7 +345,7 @@ export const uploadRemoteFileInfo = (url: string, isPublic?: boolean, silent?: b export const sendEMailLoginCode = (email: string, language = 'en-US') => post('/email-code-login', { body: { email, language } }) -export const emailLoginWithCode = (data: { email: string; code: string; token: string }) => +export const emailLoginWithCode = (data: { email: string; code: string; token: string; language: string }) => post('/email-code-login/validity', { body: data }) export const sendResetPasswordCode = (email: string, language = 'en-US') =>