feat:新增多语言
This commit is contained in:
34
src/lib/i18n.ts
Normal file
34
src/lib/i18n.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import i18n from 'i18next'
|
||||
import {initReactI18next} from 'react-i18next'
|
||||
|
||||
import en from '@/message/en'
|
||||
import zh from '@/message/zh'
|
||||
import {useUserStore} from '@/store/user.ts'
|
||||
|
||||
export function normalizeLanguage(language?: string) {
|
||||
const value = language?.trim().toLowerCase()
|
||||
|
||||
if (!value) {
|
||||
return 'zh'
|
||||
}
|
||||
|
||||
return value.startsWith('zh') ? 'zh' : 'en'
|
||||
}
|
||||
|
||||
const initialLanguage = normalizeLanguage(useUserStore.getState().language)
|
||||
|
||||
void i18n
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources: {
|
||||
zh: {translation: zh},
|
||||
en: {translation: en},
|
||||
},
|
||||
lng: initialLanguage,
|
||||
fallbackLng: 'zh',
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
})
|
||||
|
||||
export default i18n
|
||||
@@ -1,7 +1,7 @@
|
||||
import type {goodsType} from '@/types/business.type.ts'
|
||||
|
||||
export const queryKeys = {
|
||||
authBootstrap: ['auth-bootstrap'] as const,
|
||||
authBootstrap: (username: string) => ['auth-bootstrap', username] as const,
|
||||
goodsCatalog: (types?: readonly goodsType[]) => ['goods-catalog', ...(types ?? ['all'])] as const,
|
||||
assets: (sessionId: string) => ['assets', sessionId] as const,
|
||||
addressList: (sessionId: string) => ['address-list', sessionId] as const,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import ky, {HTTPError, type AfterResponseHook, type Input, type KyInstance, type Options} from 'ky'
|
||||
|
||||
import {notifyError, resolveToastMessage} from '@/features/notifications'
|
||||
import i18n from '@/lib/i18n'
|
||||
import {useUserStore} from '@/store/user.ts'
|
||||
import type {ValidateTokenData} from '@/types/auth.type.ts'
|
||||
import {objectToFormData} from './tool'
|
||||
@@ -44,7 +45,17 @@ const isApiEnvelope = (value: unknown): value is ApiEnvelope =>
|
||||
'code' in value &&
|
||||
typeof (value as {code?: unknown}).code === 'number'
|
||||
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? '/api/'
|
||||
const resolveApiBaseUrl = () => {
|
||||
const configuredBaseUrl = import.meta.env.VITE_API_BASE_URL?.trim()
|
||||
|
||||
if (configuredBaseUrl) {
|
||||
return `${configuredBaseUrl.replace(/\/+$/, '')}/api/`
|
||||
}
|
||||
|
||||
return import.meta.env.PROD ? 'https://playx-api.cjdhr.top/api/' : '/api/'
|
||||
}
|
||||
|
||||
const API_BASE_URL = resolveApiBaseUrl()
|
||||
const REQUEST_TIMEOUT = 10_000
|
||||
const AUTH_RETRY_HEADER = 'x-auth-retried'
|
||||
const VERIFY_TOKEN_PATH = '/v1/mall/verifyToken'
|
||||
@@ -56,12 +67,18 @@ export const setAccessTokenFormatter = (formatter?: TokenFormatter) => {
|
||||
accessTokenFormatter = formatter ?? ((token) => `Bearer ${token}`)
|
||||
}
|
||||
|
||||
const getRequestLanguage = () => useUserStore.getState().language?.trim() || 'zh'
|
||||
|
||||
const authRefreshClient = ky.create({
|
||||
baseUrl: API_BASE_URL,
|
||||
timeout: REQUEST_TIMEOUT,
|
||||
retry: 0,
|
||||
headers: {
|
||||
lang: 'zh',
|
||||
hooks: {
|
||||
beforeRequest: [
|
||||
({request}) => {
|
||||
request.headers.set('lang', getRequestLanguage())
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
@@ -88,7 +105,7 @@ async function refreshAuthInfo() {
|
||||
refreshAuthInfoPromise = (async () => {
|
||||
const token = useUserStore.getState().userInfo?.token?.trim()
|
||||
if (!token) {
|
||||
throw new RequestError('Unauthorized')
|
||||
throw new RequestError(i18n.t('errors.unauthorized'))
|
||||
}
|
||||
|
||||
const response = await authRefreshClient.post('v1/mall/verifyToken', {
|
||||
@@ -100,7 +117,7 @@ async function refreshAuthInfo() {
|
||||
}>()
|
||||
|
||||
if (!response || (response.code !== 1 && response.code !== 200) || !response.data?.session_id) {
|
||||
throw new RequestError(resolveToastMessage(response, 'Unauthorized'))
|
||||
throw new RequestError(resolveToastMessage(response, i18n.t('errors.unauthorized')))
|
||||
}
|
||||
|
||||
useUserStore.getState().setAuthInfo(response.data)
|
||||
@@ -141,12 +158,11 @@ const requestClient = ky.create({
|
||||
baseUrl: API_BASE_URL,
|
||||
timeout: REQUEST_TIMEOUT,
|
||||
retry: 0,
|
||||
headers: {
|
||||
lang: 'zh',
|
||||
},
|
||||
hooks: {
|
||||
beforeRequest: [
|
||||
({request}) => {
|
||||
request.headers.set('lang', getRequestLanguage())
|
||||
|
||||
const token = useUserStore.getState().userInfo?.token
|
||||
if (!token) {
|
||||
return
|
||||
@@ -165,7 +181,7 @@ const requestClient = ky.create({
|
||||
error.data,
|
||||
typeof error.data === 'string'
|
||||
? error.data
|
||||
: error.response.statusText || 'Request failed',
|
||||
: error.response.statusText || i18n.t('errors.requestFailed'),
|
||||
)
|
||||
|
||||
notifyError(message)
|
||||
@@ -177,7 +193,7 @@ const requestClient = ky.create({
|
||||
})
|
||||
}
|
||||
|
||||
const message = error.message || 'Network request failed'
|
||||
const message = error.message || i18n.t('errors.networkRequestFailed')
|
||||
notifyError(message)
|
||||
return new RequestError(message, {
|
||||
cause: error,
|
||||
@@ -219,7 +235,7 @@ async function parseResponse<T, R extends ResponseType>(
|
||||
const payload = await response.json()
|
||||
|
||||
if (isApiEnvelope(payload) && payload.code !== 1 && payload.code !== 200) {
|
||||
const message = payload.msg?.trim() || 'Request failed'
|
||||
const message = payload.msg?.trim() || i18n.t('errors.requestFailed')
|
||||
notifyError(message)
|
||||
throw new RequestError(message, {
|
||||
status: response.status,
|
||||
|
||||
Reference in New Issue
Block a user