vue-localeflow
v0.2.0
Published
A lightweight, type-safe Vue 3 internationalization (i18n) solution with reactive language switching and localStorage support
Maintainers
Readme
vue-localeflow
一个轻量级、类型安全的 Vue 3 国际化(i18n)解决方案,提供响应式的语言切换和本地存储支持。支持任意数据类型,包括字符串、对象、组件和函数。
English | 中文
✨ 特性
- 🚀 轻量级 - 压缩后<1KB,不到 100 行代码
- 💪 类型安全 - 完整的 TypeScript 支持,编译时类型检查
- ⚡ 高性能 - 无额外的函数调用开销,直接访问响应式对象
- 💾 持久化 - 自动持久化多语言设置,刷新后保持语言选择
- 🎯 简单易用 - 极简的 API 设计,三分钟上手
- 🌍 多类型支持 - 支持字符串、对象、Vue 组件、函数等任意类型
📦 安装
# npm
npm install vue-localeflow
# yarn
yarn add vue-localeflow
# pnpm
pnpm add vue-localeflow🚀 快速开始
基础用法
import { createLang } from 'vue-localeflow'
// 定义语言数据(支持任意类型)
const langData = {
chs: {
hello: '你好',
country: {
cn: '中国',
us: '美国',
},
},
en: {
hello: 'Hello',
country: {
cn: 'China',
us: 'United States',
},
},
}
// 创建语言管理器
const lang = createLang(langData, { default: 'chs' })
// 使用语言数据
console.log(lang.hello) // '你好'
console.log(lang.country.cn) // '中国'
console.log(lang.$.lang) // 'chs' (当前语言键)
// 切换语言
lang.$.set('en')
console.log(lang.hello) // 'Hello'
console.log(lang.country.cn) // 'China'在 Vue 组件中使用
<template>
<div>
<!-- 语言切换按钮 -->
<div class="lang-switcher">
<button
v-for="l in langs"
:key="l.key"
@click="lang.$.set(l.key)"
:class="{ active: lang.$.lang === l.key }"
>
{{ l.label }}
</button>
</div>
<!-- 直接访问语言数据 -->
<h1>{{ lang.hello }}</h1>
<p>{{ lang.country.cn }}</p>
<!-- 当前语言显示 -->
<p>当前语言:{{ lang.$.lang }}</p>
</div>
</template>
<script setup lang="ts">
import { createLang } from 'vue-localeflow'
const langData = {
chs: {
hello: '你好,世界!',
country: { cn: '中国', us: '美国' },
},
en: {
hello: 'Hello, World!',
country: { cn: 'China', us: 'United States' },
},
cht: {
hello: '你好,世界!',
country: { cn: '中國', us: '美國' },
},
}
const lang = createLang(langData, { default: 'chs' })
const langs = [
{ key: 'chs', label: '简体中文' },
{ key: 'en', label: 'English' },
{ key: 'cht', label: '繁體中文' },
]
</script>支持多种数据类型
import { createLang } from 'vue-localeflow'
import HelloComponent from './components/Hello.vue'
const lang = createLang({
chs: {
// 字符串
title: '欢迎',
// 嵌套对象
country: {
cn: '中国',
us: '美国',
},
// Vue 组件
helloUI: <HelloComponent country="cn" />,
// 函数
helloTo: (name: string) => `你好,${name}!`,
},
en: {
title: 'Welcome',
country: {
cn: 'China',
us: 'United States',
},
helloUI: <HelloComponent country="en" />,
helloTo: (name: string) => `Hello, ${name}!`,
},
})
// 使用不同类型的数据
console.log(lang.title) // 字符串
console.log(lang.country.cn) // 嵌套对象
console.log(lang.helloTo('Vue')) // 函数调用
// 在模板中使用组件:<component :is="lang.helloUI" />使用 t 函数进行临时多语言映射
// t 函数用于一次性的多语言映射,无需预定义
const message = lang.$.t({
chs: '中午好,你吃了吗?',
en: 'Good afternoon, have you eaten?',
cht: '中午好,你吃了嗎?',
})
// 根据当前语言返回对应的值
console.log(message) // 根据 lang.$.lang 返回对应语言的文本📚 API 参考
createLang(langs, options?)
创建一个语言管理器实例。
参数
langs- 语言数据对象,键为语言标识,值为该语言的数据对象(支持任意类型)options- 可选配置项default- 默认语言键,如果未指定则使用langs中声明的第一个语言;如果传入了无效值,也会回退到第一个语言storageKey- localStorage 存储键名,默认为'vue-localeflow'storage- 自定义持久化适配器,适合接入 Cookie 或其他存储介质resolveInitialLang- 当没有持久化值时,用于决定初始语言的自定义函数
返回值
返回一个只读的响应式代理对象,包含以下属性和方法:
$- 语言管理器对象,包含以下属性和方法:lang- 当前激活的语言键(只读)set(key, persist?)- 设置当前语言key- 要设置的语言键persist- 是否写入当前持久化适配器,默认为true
t(options)- 根据当前语言动态选择值options- 包含语言键对应值的对象,允许只传部分语言- 返回值 - 优先返回当前语言对应的值;如果缺失,则回退到
default对应的值;如果仍然缺失,则按langs的声明顺序返回第一个可用值
...langData- 当前语言的所有数据属性(通过 Proxy 动态代理访问)
🔧 高级用法
Nuxt SSR 接入
在 SPA 中无需额外配置,直接使用 createLang() 即可。
如果在 Nuxt 中使用,为了让服务端请求内的语言状态隔离生效,建议在 Nuxt 插件中接入 vue-localeflow/nuxt:
// plugins/lang.ts
import { installNuxtLang } from 'vue-localeflow/nuxt'
import { lang } from '~/lang'
export default defineNuxtPlugin((nuxtApp) => {
const cookie = useCookie<'chs' | 'en'>('lang')
installNuxtLang(lang, nuxtApp.vueApp, {
useState,
storage: {
get: () => cookie.value,
set: (value) => {
cookie.value = value
},
},
resolveInitialLang: () => {
if (import.meta.server) {
const headers = useRequestHeaders(['accept-language'])
const acceptLanguage = headers['accept-language'] || ''
if (acceptLanguage.includes('zh')) return 'chs'
if (acceptLanguage.includes('en')) return 'en'
return undefined
}
return navigator.language.startsWith('zh') ? 'chs' : 'en'
},
})
})如果你只是在 SPA 中根据浏览器语言初始化,可以直接在 createLang() 中传入 resolveInitialLang:
const lang = createLang(langData, {
default: 'en',
resolveInitialLang: () => {
if (navigator.language.startsWith('zh-CN')) return 'chs'
if (navigator.language.startsWith('en')) return 'en'
return undefined
},
})接入完成后,业务代码仍然可以继续全局导出并直接使用 lang:
// 任意运行时调用链里都可以直接读取
export function getPageTitle() {
return lang.title
}不建议在模块顶层缓存请求态语言值,例如:
// 不推荐
export const pageTitle = lang.title这类顶层求值会在 SSR 中丢失当前请求上下文。
TypeScript 类型安全
// 定义语言数据类型
const chs = {
nav: {
home: '首页',
about: '关于我们',
contact: '联系我们',
},
message: {
welcome: '欢迎访问我们的网站!',
error: '发生了错误',
},
format: {
date: 'YYYY年MM月DD日',
currency: '¥',
},
} as const
export type ILang = typeof chs
// 其他语言必须符合同样的类型
const en: ILang = {
nav: {
home: 'Home',
about: 'About Us',
contact: 'Contact Us',
},
message: {
welcome: 'Welcome to our website!',
error: 'An error occurred',
},
format: {
date: 'MM/DD/YYYY',
currency: '$',
},
}
export const lang = createLang({ chs, en }, { default: 'chs' })
// 现在享受完整的类型提示和检查
console.log(lang.nav.home) // 类型安全的属性访问
console.log(lang.format.date) // 编译时类型检查函数和组件支持
import MyComponent from './MyComponent.vue'
const lang = createLang({
chs: {
// 支持函数
greet: (name: string) => `你好,${name}!`,
formatPrice: (price: number) => `¥${price.toFixed(2)}`,
// 支持 Vue 组件
headerComponent: <MyComponent theme="dark" />,
footerComponent: <MyComponent theme="light" />,
},
en: {
greet: (name: string) => `Hello, ${name}!`,
formatPrice: (price: number) => `$${price.toFixed(2)}`,
headerComponent: <MyComponent theme="dark" />,
footerComponent: <MyComponent theme="light" />,
},
})
// 使用函数
console.log(lang.greet('Vue')) // "你好,Vue!"
console.log(lang.formatPrice(99.99)) // "¥99.99"
// 在模板中使用组件
// <component :is="lang.headerComponent" />临时语言切换(不持久化)
// 临时切换语言,不保存到 localStorage
lang.$.set('en', false)
// 刷新页面后会恢复之前保存的语言自定义存储配置
const lang = createLang(langData, {
storageKey: 'my-app-language', // 自定义存储键
default: 'chs', // 默认语言
})🌟 最佳实践
1. 分离语言文件并保证类型安全
// lang/chs.ts
export const chs = {
common: {
save: '保存',
cancel: '取消',
confirm: '确认',
loading: '加载中...',
},
nav: {
home: '首页',
about: '关于',
contact: '联系我们',
},
pages: {
home: {
title: '欢迎使用 vue-localeflow',
subtitle: '轻量级 Vue 3 国际化解决方案',
},
},
} as const
export type ILang = typeof chs// lang/en.ts
import type { ILang } from './chs'
export const en: ILang = {
common: {
save: 'Save',
cancel: 'Cancel',
confirm: 'Confirm',
loading: 'Loading...',
},
nav: {
home: 'Home',
about: 'About',
contact: 'Contact Us',
},
pages: {
home: {
title: 'Welcome to vue-localeflow',
subtitle: 'Lightweight Vue 3 i18n Solution',
},
},
}// lang/index.ts
import { createLang } from 'vue-localeflow'
import { chs } from './chs'
import { en } from './en'
export const lang = createLang(
{ chs, en },
{
default: 'chs',
storageKey: 'app-language',
}
)
// 导出类型以供其他地方使用
export type { ILang } from './chs'2. 任意地方使用
由于 lang 对象是响应式的,您可以在项目的任意地方直接导入和使用,无需额外的 Hook 或 Provider。
- 组件外使用
import { lang } from '@/lang'
export async function fetchUserData() {
const response = await fetch('/api/user', {
headers: {
'Accept-Language': lang.$.lang === 'chs' ? 'zh-CN' : 'en-US',
},
})
if (!response.ok) {
throw new Error(lang.message.error) // 直接使用当前语言的错误信息
}
return response.json()
}- 组件内使用
<template>
<div>{{ lang.greeting }}</div>
</template>