feat: 添加国际化支持,更新相关配置和组件,优化余额格式化

This commit is contained in:
2025-12-13 20:03:34 +07:00
parent 3866c85815
commit f5c7b1fb0a
14 changed files with 159 additions and 23 deletions

2
auto-imports.d.ts vendored
View File

@@ -183,6 +183,7 @@ declare global {
const useFullscreen: typeof import('@vueuse/core').useFullscreen const useFullscreen: typeof import('@vueuse/core').useFullscreen
const useGamepad: typeof import('@vueuse/core').useGamepad const useGamepad: typeof import('@vueuse/core').useGamepad
const useGeolocation: typeof import('@vueuse/core').useGeolocation const useGeolocation: typeof import('@vueuse/core').useGeolocation
const useI18n: typeof import('vue-i18n').useI18n
const useId: typeof import('vue').useId const useId: typeof import('vue').useId
const useIdle: typeof import('@vueuse/core').useIdle const useIdle: typeof import('@vueuse/core').useIdle
const useImage: typeof import('@vueuse/core').useImage const useImage: typeof import('@vueuse/core').useImage
@@ -492,6 +493,7 @@ declare module 'vue' {
readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']> readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']> readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']> readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
readonly useI18n: UnwrapRef<typeof import('vue-i18n')['useI18n']>
readonly useId: UnwrapRef<typeof import('vue')['useId']> readonly useId: UnwrapRef<typeof import('vue')['useId']>
readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']> readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
readonly useImage: UnwrapRef<typeof import('@vueuse/core')['useImage']> readonly useImage: UnwrapRef<typeof import('@vueuse/core')['useImage']>

View File

@@ -4,7 +4,7 @@ import antfu from "@antfu/eslint-config";
export default antfu({ export default antfu({
vue: true, vue: true,
typescript: true, typescript: true,
jsonc: false, jsonc: true,
gitignore: true, gitignore: true,
ignores: [ ignores: [
"ios", "ios",

View File

@@ -29,6 +29,7 @@
"ionicons": "^8.0.13", "ionicons": "^8.0.13",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"vue": "^3.5.25", "vue": "^3.5.25",
"vue-i18n": "^11.2.2",
"vue-router": "^4.6.3" "vue-router": "^4.6.3"
}, },
"devDependencies": { "devDependencies": {

40
pnpm-lock.yaml generated
View File

@@ -53,6 +53,9 @@ importers:
vue: vue:
specifier: ^3.5.25 specifier: ^3.5.25
version: 3.5.25(typescript@5.9.3) version: 3.5.25(typescript@5.9.3)
vue-i18n:
specifier: ^11.2.2
version: 11.2.2(vue@3.5.25(typescript@5.9.3))
vue-router: vue-router:
specifier: ^4.6.3 specifier: ^4.6.3
version: 4.6.3(vue@3.5.25(typescript@5.9.3)) version: 4.6.3(vue@3.5.25(typescript@5.9.3))
@@ -1114,6 +1117,18 @@ packages:
peerDependencies: peerDependencies:
vue: '>=3' vue: '>=3'
'@intlify/core-base@11.2.2':
resolution: {integrity: sha512-0mCTBOLKIqFUP3BzwuFW23hYEl9g/wby6uY//AC5hTgQfTsM2srCYF2/hYGp+a5DZ/HIFIgKkLJMzXTt30r0JQ==}
engines: {node: '>= 16'}
'@intlify/message-compiler@11.2.2':
resolution: {integrity: sha512-XS2p8Ff5JxWsKhgfld4/MRQzZRQ85drMMPhb7Co6Be4ZOgqJX1DzcZt0IFgGTycgqL8rkYNwgnD443Q+TapOoA==}
engines: {node: '>= 16'}
'@intlify/shared@11.2.2':
resolution: {integrity: sha512-OtCmyFpSXxNu/oET/aN6HtPCbZ01btXVd0f3w00YsHOb13Kverk1jzA2k47pAekM55qbUw421fvPF1yxZ+gicw==}
engines: {node: '>= 16'}
'@ionic/cli-framework-output@2.2.8': '@ionic/cli-framework-output@2.2.8':
resolution: {integrity: sha512-TshtaFQsovB4NWRBydbNFawql6yul7d5bMiW1WYYf17hd99V6xdDdk3vtF51bw6sLkxON3bDQpWsnUc9/hVo3g==} resolution: {integrity: sha512-TshtaFQsovB4NWRBydbNFawql6yul7d5bMiW1WYYf17hd99V6xdDdk3vtF51bw6sLkxON3bDQpWsnUc9/hVo3g==}
engines: {node: '>=16.0.0'} engines: {node: '>=16.0.0'}
@@ -4313,6 +4328,12 @@ packages:
vue-flow-layout@0.2.0: vue-flow-layout@0.2.0:
resolution: {integrity: sha512-zKgsWWkXq0xrus7H4Mc+uFs1ESrmdTXlO0YNbR6wMdPaFvosL3fMB8N7uTV308UhGy9UvTrGhIY7mVz9eN+L0Q==} resolution: {integrity: sha512-zKgsWWkXq0xrus7H4Mc+uFs1ESrmdTXlO0YNbR6wMdPaFvosL3fMB8N7uTV308UhGy9UvTrGhIY7mVz9eN+L0Q==}
vue-i18n@11.2.2:
resolution: {integrity: sha512-ULIKZyRluUPRCZmihVgUvpq8hJTtOqnbGZuv4Lz+byEKZq4mU0g92og414l6f/4ju+L5mORsiUuEPYrAuX2NJg==}
engines: {node: '>= 16'}
peerDependencies:
vue: ^3.0.0
vue-router@4.6.3: vue-router@4.6.3:
resolution: {integrity: sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==} resolution: {integrity: sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==}
peerDependencies: peerDependencies:
@@ -5557,6 +5578,18 @@ snapshots:
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
vue: 3.5.25(typescript@5.9.3) vue: 3.5.25(typescript@5.9.3)
'@intlify/core-base@11.2.2':
dependencies:
'@intlify/message-compiler': 11.2.2
'@intlify/shared': 11.2.2
'@intlify/message-compiler@11.2.2':
dependencies:
'@intlify/shared': 11.2.2
source-map-js: 1.2.1
'@intlify/shared@11.2.2': {}
'@ionic/cli-framework-output@2.2.8': '@ionic/cli-framework-output@2.2.8':
dependencies: dependencies:
'@ionic/utils-terminal': 2.3.5 '@ionic/utils-terminal': 2.3.5
@@ -9128,6 +9161,13 @@ snapshots:
vue-flow-layout@0.2.0: {} vue-flow-layout@0.2.0: {}
vue-i18n@11.2.2(vue@3.5.25(typescript@5.9.3)):
dependencies:
'@intlify/core-base': 11.2.2
'@intlify/shared': 11.2.2
'@vue/devtools-api': 6.6.4
vue: 3.5.25(typescript@5.9.3)
vue-router@4.6.3(vue@3.5.25(typescript@5.9.3)): vue-router@4.6.3(vue@3.5.25(typescript@5.9.3)):
dependencies: dependencies:
'@vue/devtools-api': 6.6.4 '@vue/devtools-api': 6.6.4

View File

@@ -1,7 +1,7 @@
import type { App } from "@riwa/api-types"; import type { App } from "@riwa/api-types";
import { treaty } from "@elysiajs/eden"; import { treaty } from "@elysiajs/eden";
const client = treaty<App>(`${window.location.origin}/api`, { const client = treaty<App>(window.location.origin, {
fetch: { fetch: {
credentials: "include", credentials: "include",
}, },

9
src/locales/en-US.json Normal file
View File

@@ -0,0 +1,9 @@
{
"wallet": {
"wallet": "Wallet",
"recharge": "Recharge",
"withdraw": "Withdraw",
"transfer": "Transfer",
"balance": "Balance"
}
}

16
src/locales/index.ts Normal file
View File

@@ -0,0 +1,16 @@
import { createI18n } from "vue-i18n";
import enUS from "./en-US.json";
import zhCN from "./zh-CN.json";
export type MessageSchema = typeof enUS;
const i18n = createI18n<MessageSchema, "en-US" | "zh-CN">({
locale: "en-US",
fallbackLocale: "en-US",
messages: {
"en-US": enUS,
"zh-CN": zhCN,
},
});
export { i18n };

9
src/locales/zh-CN.json Normal file
View File

@@ -0,0 +1,9 @@
{
"wallet": {
"wallet": "钱包",
"recharge": "充值",
"withdraw": "提现",
"transfer": "转账",
"balance": "余额"
}
}

View File

@@ -2,6 +2,7 @@ import { IonicVue } from "@ionic/vue";
import { createApp } from "vue"; import { createApp } from "vue";
import App from "./App.vue"; import App from "./App.vue";
import { i18n } from "./locales";
import router from "./router"; import router from "./router";
/* UnoCSS */ /* UnoCSS */
@@ -40,7 +41,8 @@ import "./theme/ionic.css";
const app = createApp(App) const app = createApp(App)
.use(IonicVue) .use(IonicVue)
.use(router); .use(router)
.use(i18n);
router.isReady().then(() => { router.isReady().then(() => {
app.mount("#app"); app.mount("#app");

View File

@@ -1,6 +1,6 @@
export function formatBalance(amount: MaybeRefOrGetter<number>, locale: Intl.LocalesArgument = "en-US"): ComputedRef<string> { export function formatBalance(amount: MaybeRefOrGetter<number>, locale: Intl.LocalesArgument = "en-US"): ComputedRef<string> {
return computed(() => { return computed(() => {
const balance = toValue(amount); const balance = toValue(amount);
return `$${balance.toLocaleString(locale, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; return `$${balance.toLocaleString(locale, { minimumFractionDigits: 0, maximumFractionDigits: 2 })}`;
}); });
} }

View File

@@ -1,30 +1,46 @@
<script lang='ts' setup> <script lang='ts' setup>
import { client } from "@/api"; import { client } from "@/api";
const balance = ref(9999999.00); const { t } = useI18n();
const balance = ref(9999999);
const formattedBalance = formatBalance(balance); const formattedBalance = formatBalance(balance);
const { data } = await client.asset.balances.get(); const { data } = await client.api.asset.balances.get();
</script> </script>
<template> <template>
<ion-card> <div class="mt-20px">
<ion-card-header> <div class="grid grid-cols-2 gap-20px p-20px bg-[var(--ion-card-background)] rounded-t-6px">
<ion-card-title>{{ formattedBalance }}</ion-card-title> <div class="flex flex-col gap-4px">
<ion-card-subtitle>Wallet</ion-card-subtitle> <div class="ion-text-uppercase text-xs color-text-400 font-500 tracking-0.4px">
</ion-card-header> {{ t('wallet.balance') }}
</div>
<div class="text-lg font-bold">
{{ formattedBalance }}
</div>
</div>
<div class="flex flex-col gap-4px">
<div class="ion-text-uppercase text-xs color-text-400 font-500 tracking-0.4px">
{{ t('wallet.balance') }}
</div>
<div class="text-lg font-bold">
{{ formattedBalance }}
</div>
</div>
</div>
<!-- <ion-card-content> <div class="px-10px pb-20px bg-[var(--ion-card-background)] rounded-b-6px">
Here's a small text description for the card content. Nothing more, nothing less. <ion-buttons class="gap-10px" expand="block">
</ion-card-content> --> <ion-button expand="block" fill="clear">
{{ t("wallet.recharge") }}
<ion-button fill="clear">
Recharge
</ion-button> </ion-button>
<ion-button fill="clear"> <ion-button expand="block" fill="clear">
Withdrawal {{ t("wallet.withdraw") }}
</ion-button> </ion-button>
</ion-card> </ion-buttons>
</div>
</div>
</template> </template>
<style scoped></style> <style scoped></style>

21
src/vite-env.d.ts vendored
View File

@@ -1,2 +1,23 @@
/// <reference types="vite/client" /> /// <reference types="vite/client" />
/// <reference types="unplugin-icons/types/vue" /> /// <reference types="unplugin-icons/types/vue" />
import type { MessageSchema } from "@/locales";
import {
DefineDateTimeFormat,
DefineLocaleMessage,
DefineNumberFormat,
} from "vue-i18n";
declare module "vue-i18n" {
// define the locale messages schema
export interface DefineLocaleMessage extends MessageSchema {
}
// define the datetime format schema
export interface DefineDateTimeFormat {
}
// define the number format schema
export interface DefineNumberFormat {
}
}

View File

@@ -33,6 +33,26 @@ export default defineConfig({
light: "var(--ion-color-light)", light: "var(--ion-color-light)",
medium: "var(--ion-color-medium)", medium: "var(--ion-color-medium)",
dark: "var(--ion-color-dark)", dark: "var(--ion-color-dark)",
text: {
50: "var(--ion-text-color-step-50)",
100: "var(--ion-text-color-step-100)",
150: "var(--ion-text-color-step-150)",
200: "var(--ion-text-color-step-200)",
250: "var(--ion-text-color-step-250)",
300: "var(--ion-text-color-step-300)",
350: "var(--ion-text-color-step-350)",
400: "var(--ion-text-color-step-400)",
450: "var(--ion-text-color-step-450)",
500: "var(--ion-text-color-step-500)",
550: "var(--ion-text-color-step-550)",
600: "var(--ion-text-color-step-600)",
650: "var(--ion-text-color-step-650)",
700: "var(--ion-text-color-step-700)",
750: "var(--ion-text-color-step-750)",
800: "var(--ion-text-color-step-800)",
850: "var(--ion-text-color-step-850)",
900: "var(--ion-text-color-step-900)",
},
}, },
}, },
}); });

View File

@@ -23,7 +23,7 @@ export default defineConfig({
legacy(), legacy(),
autoImport({ autoImport({
dirs: ["src/composables", "src/utils"], dirs: ["src/composables", "src/utils"],
imports: ["vue", "vue-router", "@vueuse/core"], imports: ["vue", "vue-router", "@vueuse/core", "vue-i18n"],
resolvers: [IonicResolver()], resolvers: [IonicResolver()],
vueTemplate: true, vueTemplate: true,
}), }),