From 8ce6bc4ce136fb4b860bf21d74cbfed0538e1cd0 Mon Sep 17 00:00:00 2001 From: xudaotutou <13435638964@163.com> Date: Thu, 28 Nov 2024 11:44:27 +0800 Subject: [PATCH 1/2] i18n-costcenter --- .../costcenter/src/components/CurrencySymbol.tsx | 8 ++++++-- .../costcenter/src/components/RechargeModal.tsx | 6 +++--- .../src/components/billing/AmountDisplay.tsx | 14 ++++++++------ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/frontend/providers/costcenter/src/components/CurrencySymbol.tsx b/frontend/providers/costcenter/src/components/CurrencySymbol.tsx index ef16339b11c..12095b37c78 100644 --- a/frontend/providers/costcenter/src/components/CurrencySymbol.tsx +++ b/frontend/providers/costcenter/src/components/CurrencySymbol.tsx @@ -10,8 +10,12 @@ export default function CurrencySymbol({ return type === 'shellCoin' ? ( ) : type === 'cny' ? ( - + + ¥ + ) : ( - $ + + $ + ); } diff --git a/frontend/providers/costcenter/src/components/RechargeModal.tsx b/frontend/providers/costcenter/src/components/RechargeModal.tsx index 24529ddc6b6..80382bc5fa1 100644 --- a/frontend/providers/costcenter/src/components/RechargeModal.tsx +++ b/frontend/providers/costcenter/src/components/RechargeModal.tsx @@ -190,7 +190,7 @@ const BonusBox = (props: { {t('Double')}! + - + {props.bouns} @@ -213,7 +213,7 @@ const BonusBox = (props: { fontSize="12px" > {t('Bonus')} - + {props.bouns} )} @@ -536,7 +536,7 @@ const RechargeModal = forwardRef( > {t('Bonus')} {getBonus(amount)} - + {list.map((item) => ( {t(item.title)}: - + {formatMoney(item.value).toFixed(2)} ))} From 98218fece5e72211a12494e8a69d83e8c46ce2da Mon Sep 17 00:00:00 2001 From: xudaotutou <13435638964@163.com> Date: Thu, 28 Nov 2024 15:46:25 +0800 Subject: [PATCH 2/2] fix(costcenter):i18n --- .../costcenter/next-i18next.config.js | 2 +- .../providers/costcenter/src/pages/_app.tsx | 50 +++++++++-------- .../src/pages/cost_overview/index.tsx | 54 +++++++++++-------- .../providers/costcenter/src/stores/env.ts | 2 + .../costcenter/src/stores/session.ts | 2 - 5 files changed, 64 insertions(+), 46 deletions(-) diff --git a/frontend/providers/costcenter/next-i18next.config.js b/frontend/providers/costcenter/next-i18next.config.js index 7a365a52cc5..177b6887711 100644 --- a/frontend/providers/costcenter/next-i18next.config.js +++ b/frontend/providers/costcenter/next-i18next.config.js @@ -5,7 +5,7 @@ module.exports = { i18n: { - defaultLocale: 'zh', + defaultLocale: 'en', locales: ['en', 'zh'], localeDetection: false }, diff --git a/frontend/providers/costcenter/src/pages/_app.tsx b/frontend/providers/costcenter/src/pages/_app.tsx index 184a002bb54..efe62822101 100644 --- a/frontend/providers/costcenter/src/pages/_app.tsx +++ b/frontend/providers/costcenter/src/pages/_app.tsx @@ -1,23 +1,23 @@ import Layout from '@/layout'; -import { sealosApp } from 'sealos-desktop-sdk/app'; -import { EVENT_NAME } from 'sealos-desktop-sdk'; -import '@/styles/globals.scss'; +import { Response as initDataRes } from '@/pages/api/platform/getAppConfig'; +import request from '@/service/request'; +import useAppTypeStore from '@/stores/appType'; +import useBillingStore from '@/stores/billing'; +import useEnvStore from '@/stores/env'; import { theme } from '@/styles/chakraTheme'; +import '@/styles/globals.scss'; +import { ApiResp } from '@/types/api'; import { ChakraProvider } from '@chakra-ui/react'; -import { Hydrate, QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query'; +import { Hydrate, QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { appWithTranslation } from 'next-i18next'; import type { AppProps } from 'next/app'; import Router, { useRouter } from 'next/router'; import NProgress from 'nprogress'; import 'nprogress/nprogress.css'; -import 'react-day-picker/dist/style.css'; -import { appWithTranslation, i18n } from 'next-i18next'; import { useEffect } from 'react'; -import request from '@/service/request'; -import { ApiResp } from '@/types/api'; -import { Response as initDataRes } from '@/pages/api/platform/getAppConfig'; -import useEnvStore from '@/stores/env'; -import useAppTypeStore from '@/stores/appType'; -import useBillingStore from '@/stores/billing'; +import 'react-day-picker/dist/style.css'; +import { EVENT_NAME } from 'sealos-desktop-sdk'; +import { sealosApp } from 'sealos-desktop-sdk/app'; // Make sure to call `loadStripe` outside a component’s render to avoid // recreating the `Stripe` object on every render. @@ -40,24 +40,34 @@ const App = ({ Component, pageProps }: AppProps) => { const router = useRouter(); const { setAppTypeMap, appTypeMap } = useAppTypeStore(); const { setAppTypeList } = useBillingStore(); + // init language + const changeI18n = (data: { currentLanguage: string }) => { + router.replace(router.basePath, router.asPath, { locale: data.currentLanguage }); + }; useEffect(() => { - const changeI18n = (data: { currentLanguage: string }) => { - router.replace(router.basePath, router.asPath, { locale: data.currentLanguage }); + sealosApp.addAppEventListen(EVENT_NAME.CHANGE_I18N, changeI18n); + return () => { + sealosApp.removeAppEventListen(EVENT_NAME.CHANGE_I18N); }; - + }, []); + useEffect(() => { + state.setEnv('i18nIsInitialized', false); (async () => { try { const lang = await sealosApp.getLanguage(); changeI18n({ currentLanguage: lang.lng }); + state.setEnv('i18nIsInitialized', true); } catch (error) { - changeI18n({ - currentLanguage: 'zh' - }); + console.error('get language error'); + state.setEnv('i18nIsInitialized', false); } })(); + }, [router.asPath]); + // init + useEffect(() => { (async () => { try { const { data } = await request>('/api/platform/getAppConfig'); @@ -75,10 +85,6 @@ const App = ({ Component, pageProps }: AppProps) => { console.error('get init config error'); } })(); - sealosApp.addAppEventListen(EVENT_NAME.CHANGE_I18N, changeI18n); - return () => { - sealosApp.removeAppEventListen(EVENT_NAME.CHANGE_I18N); - }; }, []); useEffect(() => { diff --git a/frontend/providers/costcenter/src/pages/cost_overview/index.tsx b/frontend/providers/costcenter/src/pages/cost_overview/index.tsx index 1c97e073046..9d5333ca121 100644 --- a/frontend/providers/costcenter/src/pages/cost_overview/index.tsx +++ b/frontend/providers/costcenter/src/pages/cost_overview/index.tsx @@ -5,6 +5,7 @@ import { Trend } from '@/components/cost_overview/trend'; import { TrendBar } from '@/components/cost_overview/trendBar'; import useNotEnough from '@/hooks/useNotEnough'; import request from '@/service/request'; +import useEnvStore from '@/stores/env'; import useOverviewStore from '@/stores/overview'; import { ApiResp } from '@/types'; import { Box, Flex, useToast } from '@chakra-ui/react'; @@ -13,6 +14,7 @@ import { useTranslation } from 'next-i18next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { useRouter } from 'next/router'; import { MutableRefObject, createContext, useEffect, useRef } from 'react'; +import { sealosApp } from 'sealos-desktop-sdk/app'; export const RechargeContext = createContext<{ rechargeRef: MutableRefObject | null }>({ rechargeRef: null }); @@ -21,27 +23,37 @@ function CostOverview() { const { t } = useTranslation(); const setRecharge = useOverviewStore((s) => s.setRecharge); const router = useRouter(); + const toast = useToast(); + const { i18nIsInitialized } = useEnvStore(); useEffect(() => { - const { stripeState } = router.query; - if (stripeState === 'success') { - totast({ - status: 'success', - duration: 3000, - title: t('Stripe Success'), - isClosable: true, - position: 'top' - }); - } else if (stripeState === 'error') { - totast({ - status: 'error', - duration: 3000, - title: t('Stripe Cancel'), - isClosable: true, - position: 'top' - }); - } - !!stripeState && router.replace(router.pathname); - }, []); + (async () => { + const lng = ((await sealosApp.getLanguage())?.lng || 'en') as 'en' | 'zh'; + const { stripeState } = router.query; + if (!i18nIsInitialized || !router.isReady || !stripeState) return; + if (stripeState === 'success') { + toast({ + status: 'success', + duration: 3000, + title: t('Stripe Success', { + lng + }), + isClosable: true, + position: 'top' + }); + } else if (stripeState === 'error') { + toast({ + status: 'error', + duration: 3000, + title: t('Stripe Cancel', lng), + isClosable: true, + position: 'top' + }); + } else { + return; + } + !!stripeState && router.replace(router.pathname); + })(); + }, [t, i18nIsInitialized, router.query, router.isReady]); useEffect(() => { const { openRecharge } = router.query; if (openRecharge === 'true') { @@ -50,7 +62,7 @@ function CostOverview() { } }, []); const { NotEnoughModal } = useNotEnough(); - const totast = useToast(); + const rechargeRef = useRef(); const { data: balance_raw } = useQuery({ queryKey: ['getAccount'], diff --git a/frontend/providers/costcenter/src/stores/env.ts b/frontend/providers/costcenter/src/stores/env.ts index 9f5d9c60512..01426d6815c 100644 --- a/frontend/providers/costcenter/src/stores/env.ts +++ b/frontend/providers/costcenter/src/stores/env.ts @@ -9,6 +9,7 @@ type EnvState = { wechatEnabled: boolean; stripeEnabled: boolean; openRecharge: boolean; + i18nIsInitialized: boolean; currency: 'shellCoin' | 'cny' | 'usd'; stripePromise: ReturnType; setStripe: (pub: string) => void; @@ -27,6 +28,7 @@ const useEnvStore = create((set, get) => ({ stripeEnabled: false, gpuEnabled: false, openRecharge: false, + i18nIsInitialized: false, currency: 'shellCoin', stripePromise: Promise.resolve(null), setStripe: (pub: string) => set({ stripePromise: loadStripe(pub) }), diff --git a/frontend/providers/costcenter/src/stores/session.ts b/frontend/providers/costcenter/src/stores/session.ts index 0e59adbc30d..c470d6a2217 100644 --- a/frontend/providers/costcenter/src/stores/session.ts +++ b/frontend/providers/costcenter/src/stores/session.ts @@ -7,7 +7,6 @@ import { immer } from 'zustand/middleware/immer'; type SessionState = { session: SessionV1; - locale: string; setSession: (ss: SessionV1) => void; setSessionProp: (key: keyof SessionV1, value: any) => void; getSession: () => SessionV1; @@ -21,7 +20,6 @@ const useSessionStore = create()( persist( immer((set, get) => ({ session: {} as SessionV1, - locale: 'zh', setSession: (ss: SessionV1) => set({ session: ss }), setSessionProp: (key: keyof SessionV1, value: any) => { set((state) => {