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>
This commit is contained in:
feelshana 2025-10-21 15:53:12 +08:00 committed by GitHub
parent 9a9d6a4a2b
commit 2bcf96565a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 33 additions and 13 deletions

View File

@ -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]

View File

@ -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()

View File

@ -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

View File

@ -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,
)

View File

@ -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():

View File

@ -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)

View File

@ -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

View File

@ -35,7 +35,7 @@ type AccountFormValues = z.infer<typeof accountFormSchema>
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,
},
})

View File

@ -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()}`)

View File

@ -345,7 +345,7 @@ export const uploadRemoteFileInfo = (url: string, isPublic?: boolean, silent?: b
export const sendEMailLoginCode = (email: string, language = 'en-US') =>
post<CommonResponse & { data: string }>('/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<LoginResponse>('/email-code-login/validity', { body: data })
export const sendResetPasswordCode = (email: string, language = 'en-US') =>