mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-19 04:28:50 +08:00
【初始化】前端工程项目
This commit is contained in:
16
frontend/packages/i18n/eslint.config.js
Normal file
16
frontend/packages/i18n/eslint.config.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import baseConfig from '@baota/eslint'
|
||||
|
||||
/** @type {import("eslint").Linter.Config[]} */
|
||||
const config = [
|
||||
// 基础配置,用于通用的 JavaScript/TypeScript 规则
|
||||
...baseConfig,
|
||||
// 项目特定的配置覆盖
|
||||
{
|
||||
files: ['**/*.{js,ts}'],
|
||||
rules: {
|
||||
// 在此处添加项目特定的规则覆盖
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export default config
|
||||
44
frontend/packages/i18n/package.json
Normal file
44
frontend/packages/i18n/package.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "@baota/i18n",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"files": [
|
||||
"dist/**",
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"development": {
|
||||
"types": "./src/index.ts",
|
||||
"import": "./src/index.ts"
|
||||
},
|
||||
"default": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.mjs",
|
||||
"require": "./dist/index.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "vite build",
|
||||
"dev": "vite build --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^11.1.2",
|
||||
"@vueuse/core": "^13.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@baota/eslint": "workspace:*",
|
||||
"@baota/prettier": "workspace:*",
|
||||
"@baota/typescript": "workspace:*",
|
||||
"@vitejs/plugin-vue": "^5.0.0",
|
||||
"vite": "^5.0.0",
|
||||
"vite-plugin-dts": "^3.0.0"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
||||
3
frontend/packages/i18n/prettier.config.js
Normal file
3
frontend/packages/i18n/prettier.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import prettierConfig from '@baota/prettier'
|
||||
|
||||
export default prettierConfig
|
||||
130
frontend/packages/i18n/src/index.ts
Normal file
130
frontend/packages/i18n/src/index.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { type Ref, watch, effectScope, onScopeDispose, type WritableComputedRef, getCurrentScope } from 'vue'
|
||||
import { createI18n, I18n, I18nOptions, useI18n } from 'vue-i18n'
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import translationList from './translation'
|
||||
|
||||
interface LocaleReturn {
|
||||
i18n: I18n
|
||||
locale: Ref<string>
|
||||
localeOptions: { label: string; value: string }[]
|
||||
$t: (key: string, params?: Record<string, any>) => string
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 国际化
|
||||
* @param i18nConfig 国际化配置
|
||||
* @param scanConfig 扫描配置
|
||||
* @returns {LocaleReturn} 国际化实例
|
||||
*/
|
||||
export const useLocale = (
|
||||
i18nConfig: I18nOptions & { fileExt?: string },
|
||||
scanFiles: Record<string, () => Promise<any>>,
|
||||
): LocaleReturn => {
|
||||
// 当前激活的语言
|
||||
const locale = useLocalStorage<string>('locales-active', 'zhCN')
|
||||
|
||||
const fileExt = i18nConfig?.fileExt || 'js'
|
||||
|
||||
// 初始化语言包映射
|
||||
Object.keys(scanFiles).forEach((file) => {
|
||||
// 提取语言代码 (例如: './zh_CN/index.js' -> 'zh_CN')
|
||||
const langCode = file.match(/\.\/model\/([^/]+)\.js$/)?.[1] as string
|
||||
if (i18nConfig?.messages?.['zhCN'] || i18nConfig?.messages?.['enUS']) return
|
||||
if (langCode && Array.isArray(i18nConfig?.messages)) {
|
||||
i18nConfig.messages[langCode] = scanFiles[file] as any
|
||||
}
|
||||
})
|
||||
// 如果没有设置语言包,则使用默认语言包
|
||||
|
||||
// 创建国际化实例
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: locale.value || 'zhCN', // 设置默认语言
|
||||
fallbackLocale: 'enUS',
|
||||
...i18nConfig,
|
||||
}) as I18n
|
||||
|
||||
const language = (lang: string) => `./model/${lang}.${fileExt}`
|
||||
|
||||
/**
|
||||
* 加载语言包
|
||||
* @param {string} lang - 语言代码
|
||||
* @returns {Promise<Object>} - 语言包内容
|
||||
*/
|
||||
const loadLocaleMessages = async (lang: string) => {
|
||||
try {
|
||||
if (!scanFiles[language(lang)]) {
|
||||
console.warn(`Language ${lang} not found`)
|
||||
return {}
|
||||
}
|
||||
// 加载语言包
|
||||
const loadedModule = await scanFiles[language(lang)]?.()
|
||||
return loadedModule?.default || loadedModule || {}
|
||||
} catch (error) {
|
||||
console.error(`Failed to load locale ${lang}:`, error)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取可用的语言列表,生成对应的选项,需要过滤掉不支持的语言
|
||||
* @returns {string[]} - 语言代码列表
|
||||
*/
|
||||
const localeOptions = Object.entries(translationList)
|
||||
.filter(([key]) => {
|
||||
return Object.keys(scanFiles).includes(language(key))
|
||||
})
|
||||
.map(([key, value]) => ({
|
||||
label: value,
|
||||
value: key,
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
const order = ['zhCN', 'zhTW', 'enUS'] // 优先级最高
|
||||
// 获取当前语言在优先级数组中的索引
|
||||
const indexA = order.indexOf(a.value)
|
||||
const indexB = order.indexOf(b.value)
|
||||
// 如果都在优先级数组中,则按索引排序
|
||||
if (indexA !== -1 && indexB !== -1) {
|
||||
return indexA - indexB
|
||||
}
|
||||
// 否则按字母顺序排序
|
||||
return a.label.localeCompare(b.label)
|
||||
})
|
||||
|
||||
const scope = effectScope()
|
||||
scope.run(() => {
|
||||
// 监听语言变化
|
||||
watch(
|
||||
locale,
|
||||
async (newVal) => {
|
||||
const messages = await loadLocaleMessages(newVal)
|
||||
// 设置语言包
|
||||
i18n.global.setLocaleMessage(newVal, messages)
|
||||
// 获取当前作用域
|
||||
const scope = getCurrentScope()
|
||||
|
||||
if (scope) {
|
||||
// 使用当前作用域
|
||||
const { locale } = useI18n()
|
||||
// 设置当前语言
|
||||
locale.value = newVal
|
||||
} else {
|
||||
;(i18n.global.locale as WritableComputedRef<string, string>).value = newVal
|
||||
}
|
||||
// 设置当前语言
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
onScopeDispose(() => {
|
||||
scope.stop()
|
||||
})
|
||||
})
|
||||
|
||||
// 设置默认语言
|
||||
return {
|
||||
i18n,
|
||||
locale,
|
||||
$t: i18n.global.t,
|
||||
localeOptions,
|
||||
}
|
||||
}
|
||||
12
frontend/packages/i18n/src/translation.ts
Normal file
12
frontend/packages/i18n/src/translation.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export default {
|
||||
zhCN: '简体中文',
|
||||
zhTW: '繁體中文',
|
||||
enUS: 'English',
|
||||
jaJP: '日本語',
|
||||
ruRU: 'Русский',
|
||||
koKR: '한국어',
|
||||
ptBR: 'Português',
|
||||
frFR: 'Français',
|
||||
esAR: 'Español',
|
||||
arDZ: 'العربية',
|
||||
}
|
||||
11
frontend/packages/i18n/tsconfig.json
Normal file
11
frontend/packages/i18n/tsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "@baota/typescript/base.json",
|
||||
"include": ["**/*.{js,ts,jsx,tsx}", "eslint.config.ts", "./types.d.ts"],
|
||||
"compilerOptions": {
|
||||
"outDir": "dist", // 输出目录
|
||||
"baseUrl": "./",
|
||||
"rootDir": "src",
|
||||
"sourceMap": true
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
38
frontend/packages/i18n/vite.config.ts
Normal file
38
frontend/packages/i18n/vite.config.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import { resolve } from 'path'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import dts from 'vite-plugin-dts'
|
||||
|
||||
export default defineConfig({
|
||||
root: resolve(__dirname, './src'),
|
||||
plugins: [
|
||||
vue(),
|
||||
dts({
|
||||
include: ['*.ts'],
|
||||
beforeWriteFile: (filePath, content) => ({
|
||||
filePath: filePath.replace(/src/, ''),
|
||||
content,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
build: {
|
||||
outDir: resolve(__dirname, 'dist'),
|
||||
emptyOutDir: true,
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'src/index.ts'),
|
||||
name: 'BaotaI18n',
|
||||
formats: ['es', 'cjs'],
|
||||
fileName: (format) => `index.${format === 'es' ? 'mjs' : 'cjs'}`,
|
||||
},
|
||||
rollupOptions: {
|
||||
external: ['vue', 'vue-i18n', '@vueuse/core'],
|
||||
output: {
|
||||
globals: {
|
||||
vue: 'Vue',
|
||||
'vue-i18n': 'VueI18n',
|
||||
'@vueuse/core': 'VueUse',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
// vite.config.ts
|
||||
import { defineConfig } from "file:///Users/chudong/project/tools-monorepo/node_modules/.pnpm/vite@5.4.17_@types+node@22.14.1_less@4.3.0_sass@1.86.3_stylus@0.62.0_terser@5.39.0/node_modules/vite/dist/node/index.js";
|
||||
import { resolve } from "path";
|
||||
import vue from "file:///Users/chudong/project/tools-monorepo/node_modules/.pnpm/@vitejs+plugin-vue@5.2.3_vite@5.4.17_@types+node@22.14.1_less@4.3.0_sass@1.86.3_stylus@0.62.0_go6z3tefsws3wdi7v2oslblfka/node_modules/@vitejs/plugin-vue/dist/index.mjs";
|
||||
import dts from "file:///Users/chudong/project/tools-monorepo/node_modules/.pnpm/vite-plugin-dts@3.9.1_@types+node@22.14.1_rollup@4.39.0_typescript@5.8.3_vite@5.4.17_@types+n_zmp27njhd5iaq3ydw5bs3364h4/node_modules/vite-plugin-dts/dist/index.mjs";
|
||||
var __vite_injected_original_dirname = "/Users/chudong/project/tools-monorepo/packages/i18n";
|
||||
var vite_config_default = defineConfig({
|
||||
root: resolve(__vite_injected_original_dirname, "./src"),
|
||||
plugins: [
|
||||
vue(),
|
||||
dts({
|
||||
include: ["*.ts"],
|
||||
beforeWriteFile: (filePath, content) => ({
|
||||
filePath: filePath.replace(/src/, ""),
|
||||
content
|
||||
})
|
||||
})
|
||||
],
|
||||
build: {
|
||||
outDir: resolve(__vite_injected_original_dirname, "dist"),
|
||||
emptyOutDir: true,
|
||||
lib: {
|
||||
entry: resolve(__vite_injected_original_dirname, "src/index.ts"),
|
||||
name: "BaotaI18n",
|
||||
formats: ["es", "cjs"],
|
||||
fileName: (format) => `index.${format === "es" ? "mjs" : "cjs"}`
|
||||
},
|
||||
rollupOptions: {
|
||||
external: ["vue", "vue-i18n", "@vueuse/core"],
|
||||
output: {
|
||||
globals: {
|
||||
vue: "Vue",
|
||||
"vue-i18n": "VueI18n",
|
||||
"@vueuse/core": "VueUse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
export {
|
||||
vite_config_default as default
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvY2h1ZG9uZy9wcm9qZWN0L3Rvb2xzLW1vbm9yZXBvL3BhY2thZ2VzL2kxOG5cIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZmlsZW5hbWUgPSBcIi9Vc2Vycy9jaHVkb25nL3Byb2plY3QvdG9vbHMtbW9ub3JlcG8vcGFja2FnZXMvaTE4bi92aXRlLmNvbmZpZy50c1wiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9pbXBvcnRfbWV0YV91cmwgPSBcImZpbGU6Ly8vVXNlcnMvY2h1ZG9uZy9wcm9qZWN0L3Rvb2xzLW1vbm9yZXBvL3BhY2thZ2VzL2kxOG4vdml0ZS5jb25maWcudHNcIjtpbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJ1xuaW1wb3J0IHsgcmVzb2x2ZSB9IGZyb20gJ3BhdGgnXG5pbXBvcnQgdnVlIGZyb20gJ0B2aXRlanMvcGx1Z2luLXZ1ZSdcbmltcG9ydCBkdHMgZnJvbSAndml0ZS1wbHVnaW4tZHRzJ1xuXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xuXHRyb290OiByZXNvbHZlKF9fZGlybmFtZSwgJy4vc3JjJyksXG5cdHBsdWdpbnM6IFtcblx0XHR2dWUoKSxcblx0XHRkdHMoe1xuXHRcdFx0aW5jbHVkZTogWycqLnRzJ10sXG5cdFx0XHRiZWZvcmVXcml0ZUZpbGU6IChmaWxlUGF0aCwgY29udGVudCkgPT4gKHtcblx0XHRcdFx0ZmlsZVBhdGg6IGZpbGVQYXRoLnJlcGxhY2UoL3NyYy8sICcnKSxcblx0XHRcdFx0Y29udGVudCxcblx0XHRcdH0pLFxuXHRcdH0pLFxuXHRdLFxuXHRidWlsZDoge1xuXHRcdG91dERpcjogcmVzb2x2ZShfX2Rpcm5hbWUsICdkaXN0JyksXG5cdFx0ZW1wdHlPdXREaXI6IHRydWUsXG5cdFx0bGliOiB7XG5cdFx0XHRlbnRyeTogcmVzb2x2ZShfX2Rpcm5hbWUsICdzcmMvaW5kZXgudHMnKSxcblx0XHRcdG5hbWU6ICdCYW90YUkxOG4nLFxuXHRcdFx0Zm9ybWF0czogWydlcycsICdjanMnXSxcblx0XHRcdGZpbGVOYW1lOiAoZm9ybWF0KSA9PiBgaW5kZXguJHtmb3JtYXQgPT09ICdlcycgPyAnbWpzJyA6ICdjanMnfWAsXG5cdFx0fSxcblx0XHRyb2xsdXBPcHRpb25zOiB7XG5cdFx0XHRleHRlcm5hbDogWyd2dWUnLCAndnVlLWkxOG4nLCAnQHZ1ZXVzZS9jb3JlJ10sXG5cdFx0XHRvdXRwdXQ6IHtcblx0XHRcdFx0Z2xvYmFsczoge1xuXHRcdFx0XHRcdHZ1ZTogJ1Z1ZScsXG5cdFx0XHRcdFx0J3Z1ZS1pMThuJzogJ1Z1ZUkxOG4nLFxuXHRcdFx0XHRcdCdAdnVldXNlL2NvcmUnOiAnVnVlVXNlJyxcblx0XHRcdFx0fSxcblx0XHRcdH0sXG5cdFx0fSxcblx0fSxcbn0pXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQTJVLFNBQVMsb0JBQW9CO0FBQ3hXLFNBQVMsZUFBZTtBQUN4QixPQUFPLFNBQVM7QUFDaEIsT0FBTyxTQUFTO0FBSGhCLElBQU0sbUNBQW1DO0FBS3pDLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzNCLE1BQU0sUUFBUSxrQ0FBVyxPQUFPO0FBQUEsRUFDaEMsU0FBUztBQUFBLElBQ1IsSUFBSTtBQUFBLElBQ0osSUFBSTtBQUFBLE1BQ0gsU0FBUyxDQUFDLE1BQU07QUFBQSxNQUNoQixpQkFBaUIsQ0FBQyxVQUFVLGFBQWE7QUFBQSxRQUN4QyxVQUFVLFNBQVMsUUFBUSxPQUFPLEVBQUU7QUFBQSxRQUNwQztBQUFBLE1BQ0Q7QUFBQSxJQUNELENBQUM7QUFBQSxFQUNGO0FBQUEsRUFDQSxPQUFPO0FBQUEsSUFDTixRQUFRLFFBQVEsa0NBQVcsTUFBTTtBQUFBLElBQ2pDLGFBQWE7QUFBQSxJQUNiLEtBQUs7QUFBQSxNQUNKLE9BQU8sUUFBUSxrQ0FBVyxjQUFjO0FBQUEsTUFDeEMsTUFBTTtBQUFBLE1BQ04sU0FBUyxDQUFDLE1BQU0sS0FBSztBQUFBLE1BQ3JCLFVBQVUsQ0FBQyxXQUFXLFNBQVMsV0FBVyxPQUFPLFFBQVEsS0FBSztBQUFBLElBQy9EO0FBQUEsSUFDQSxlQUFlO0FBQUEsTUFDZCxVQUFVLENBQUMsT0FBTyxZQUFZLGNBQWM7QUFBQSxNQUM1QyxRQUFRO0FBQUEsUUFDUCxTQUFTO0FBQUEsVUFDUixLQUFLO0FBQUEsVUFDTCxZQUFZO0FBQUEsVUFDWixnQkFBZ0I7QUFBQSxRQUNqQjtBQUFBLE1BQ0Q7QUFBQSxJQUNEO0FBQUEsRUFDRDtBQUNELENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==
|
||||
Reference in New Issue
Block a user