This commit is contained in:
2026-01-05 12:47:14 +08:00
commit 1fc846fae3
1614 changed files with 162035 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
# nutui 语言包
目前支持的语言:
| 语言 | 文件名 | 版本 |
|--------------|--------|-----------|
| 英语 | en-US | |
| 印度尼西亚语 | id-ID | |
| 泰语 | th-TH | 等待 PR |
| 简体中文 | zh-CN | |
| 繁體中文 | zh-TW | |
> 在 [这里](https://github.com/jdf2e/nutui/tree/v4/src/packages/locale/lang) 查看所有的语言包源文件。
## 常见问题
### 找不到所需的语言包?
如果上方列表中没有你需要的语言,欢迎给我们提 Pull Request 来增加新的语言包。改动内容可以参考 [语言包](https://github.com/jdf2e/nutui/tree/v4/src/packages/locale/lang) 语言包 的 PR
### 业务代码如何实现国际化?
可以使用 [vue-i18n](https://github.com/kazupon/vue-i18n) 来实现。

View File

@@ -0,0 +1,2 @@
export * from './locale'
export * from './useTranslate'

View File

@@ -0,0 +1,111 @@
export interface BaseLang {
save: string
confirm: string
cancel: string
done: string
noData: string
placeholder: string
select: string
video: {
errorTip: string
clickRetry: string
}
fixednav: {
activeText: string
unActiveText: string
}
pagination: {
prev: string
next: string
}
calendaritem: {
weekdays: Array<string>
end: string
start: string
title: string
monthTitle: any
today: string
}
shortpassword: {
title: string
desc: string
tips: string
}
uploader: {
ready: string
readyUpload: string
waitingUpload: string
uploading: string
success: string
error: string
}
countdown: {
day: string
hour: string
minute: string
second: string
}
address: {
selectRegion: string
deliveryTo: string
chooseAnotherAddress: string
}
signature: {
reSign: string
unSupportTpl: string
}
ecard: {
chooseText: string
otherValueText: string
placeholder: string
}
timeselect: {
pickupTime: string
}
sku: {
buyNow: string
buyNumber: string
addToCart: string
}
skuheader: {
skuId: string
}
addresslist: {
addAddress: string
default: string
}
comment: {
complaintsText: string
additionalReview: any
additionalImages: any
}
infiniteloading: {
loading: string
pullTxt: string
loadMoreTxt: string
}
datepicker: {
year: string
month: string
day: string
hour: string
min: string
seconds: string
}
audiooperate: {
back: string
start: string
pause: string
forward: string
mute: string
}
pullrefresh: {
pulling: string
loosing: string
loading: string
}
}
export function defineLocaleConfig(locale: BaseLang) {
return locale
}

View File

@@ -0,0 +1,111 @@
import { defineLocaleConfig } from './baseLang'
export function EnUSLang() {
return defineLocaleConfig({
save: 'Save',
confirm: 'Confirm',
cancel: 'Cancel',
done: 'Done',
noData: 'No Data',
placeholder: 'Placeholder',
select: 'Select',
video: {
errorTip: 'Error Tip',
clickRetry: 'Click Retry',
},
fixednav: {
activeText: 'Close Nav',
unActiveText: 'Open Nav',
},
pagination: {
prev: 'Previous',
next: 'Next',
},
calendaritem: {
weekdays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
end: 'End',
start: 'Start',
title: 'Calendar',
monthTitle: (year: number, month: number) => `${year}/${month}`,
today: 'Today',
},
shortpassword: {
title: 'Please input a password',
desc: 'Verify',
tips: 'Forget password',
},
uploader: {
ready: 'Ready',
readyUpload: 'Ready to upload',
waitingUpload: 'Waiting for upload',
uploading: 'Uploading',
success: 'Upload successful',
error: 'Upload failed',
},
countdown: {
day: ' Day ',
hour: ' Hour ',
minute: ' Minute ',
second: ' Second ',
},
address: {
selectRegion: 'Select Region',
deliveryTo: 'Delivery To',
chooseAnotherAddress: 'Choose Another Address',
},
signature: {
reSign: 'Re Sign',
unSupportTpl: 'Sorry, the current browser doesn\'t support canvas, so we can\'t use this control!',
},
ecard: {
chooseText: 'Select',
otherValueText: 'Other Value',
placeholder: 'Placeholder',
},
timeselect: {
pickupTime: 'Pickup Time',
},
sku: {
buyNow: 'Buy Now',
buyNumber: 'Buy Number',
addToCart: 'Add to Cart',
},
skuheader: {
skuId: 'Sku Number',
},
addresslist: {
addAddress: 'Add New Address',
default: 'default',
},
comment: {
complaintsText: 'I have a complaint',
additionalReview: (day: number) => `Review after ${day} days of purchase`,
additionalImages: (length: number) => `There are ${length} follow-up comments`,
},
infiniteloading: {
loading: 'Loading...',
pullTxt: 'Loose to refresh',
loadMoreTxt: 'Oops, this is the bottom',
},
datepicker: {
year: 'Year',
month: 'Month',
day: 'Day',
hour: 'Hour',
min: 'Minute',
seconds: 'Second',
},
audiooperate: {
back: 'Back',
start: 'Start',
pause: 'Pause',
forward: 'Forward',
mute: 'Mute',
},
pullrefresh: {
pulling: 'Pull to refresh...',
loosing: 'Loose to refresh...',
loading: 'Loading...',
},
})
}

View File

@@ -0,0 +1,111 @@
import { defineLocaleConfig } from './baseLang'
export function IdIDLang() {
return defineLocaleConfig({
save: 'Simpan',
confirm: 'Konfirmasi',
cancel: 'Batal',
done: 'Selesai',
noData: 'Tidak Ada Data',
placeholder: 'Kolom Input',
select: 'Pilih',
video: {
errorTip: 'Terjadi Kesalahan',
clickRetry: 'Coba Lagi',
},
fixednav: {
activeText: 'Tutup Navigasi',
unActiveText: 'Buka Navigasi',
},
pagination: {
prev: 'Sebelumnya',
next: 'Selanjutnya',
},
calendaritem: {
weekdays: ['Min', 'Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab'],
end: 'Selesai',
start: 'Mulai',
title: 'Kalender',
monthTitle: (year: number, month: number) => `${year}/${month}`,
today: 'Hari ini',
},
shortpassword: {
title: 'Silakan masukan kata sandi',
desc: 'Anda telah menggunakan aset virtual, mohon lakukan verifikasi.',
tips: 'Lupa Kata Sandi',
},
uploader: {
ready: 'File berhasil diunggah',
readyUpload: 'Siap untuk mengunggah',
waitingUpload: 'Menunggu untuk diunggah',
uploading: 'Mengunggah',
success: 'Berhasil Diunggah',
error: 'Gagal Mengunggah',
},
countdown: {
day: ' Hari ',
hour: ' Jam ',
minute: ' Menit ',
second: ' Detik ',
},
address: {
selectRegion: 'Pilih Daerah',
deliveryTo: 'Kirim Ke',
chooseAnotherAddress: 'Pilih alamat lain',
},
signature: {
reSign: 'Masuk Kembali',
unSupportTpl: 'Maaf, browser Anda saat ini tidak mendukung Canvas, sehingga kita tidak dapat menggunakan kontrol ini!',
},
ecard: {
chooseText: 'Pilih',
otherValueText: 'Jumlah Lain',
placeholder: 'Kolom Input',
},
timeselect: {
pickupTime: 'Waktu Penjemputan',
},
sku: {
buyNow: 'Beli Sekarang',
buyNumber: 'Jumlah Pembelian',
addToCart: 'Tambahkan ke Keranjang',
},
skuheader: {
skuId: 'Nomor SKU',
},
addresslist: {
addAddress: 'Tambah Alamat Baru',
default: 'Bawaan',
},
comment: {
complaintsText: 'Saya memiliki komplain',
additionalReview: (day: number) => `Ulas setelah ${day} hari dari pembelian`,
additionalImages: (length: number) => `Terdapat ${length} komentar lainnya`,
},
infiniteloading: {
loading: 'Memuat...',
pullTxt: 'Lepaskan untuk memperbarui',
loadMoreTxt: 'Oops, sudah sampai bawah',
},
datepicker: {
year: 'Tahun',
month: 'Bulan',
day: 'Hari',
hour: 'Jam',
min: 'Menit',
seconds: 'Detik',
},
audiooperate: {
back: 'kembali',
start: 'Mulailah',
pause: 'berhenti sebentar',
forward: 'Maju cepat',
mute: 'Bisu',
},
pullrefresh: {
pulling: 'Tarik ke bawah untuk menyegarkan',
loosing: 'Lepaskan Refresh',
loading: 'Memuat...',
},
})
}

View File

@@ -0,0 +1,111 @@
import { defineLocaleConfig } from './baseLang'
export function ZhCNLang() {
return defineLocaleConfig({
save: '保存',
confirm: '确认',
cancel: '取消',
done: '完成',
noData: '暂无数据',
placeholder: '请输入',
select: '请选择',
video: {
errorTip: '视频加载失败',
clickRetry: '点击重试',
},
fixednav: {
activeText: '收起导航',
unActiveText: '快速导航',
},
pagination: {
prev: '上一页',
next: '下一页',
},
calendaritem: {
weekdays: ['日', '一', '二', '三', '四', '五', '六'],
end: '结束',
start: '开始',
title: '日期选择',
monthTitle: (year: number, month: number) => `${year}${month}`,
today: '今天',
},
shortpassword: {
title: '请输入密码',
desc: '您使用了虚拟资产,请进行验证',
tips: '忘记密码',
},
uploader: {
ready: '准备完成',
readyUpload: '准备上传',
waitingUpload: '等待上传',
uploading: '上传中',
success: '上传成功',
error: '上传失败',
},
countdown: {
day: '天',
hour: '时',
minute: '分',
second: '秒',
},
address: {
selectRegion: '请选择所在地区',
deliveryTo: '配送至',
chooseAnotherAddress: '选择其他地址',
},
signature: {
reSign: '重签',
unSupportTpl: '对不起当前浏览器不支持Canvas无法使用本控件',
},
ecard: {
chooseText: '请选择电子卡面值',
otherValueText: '其他面值',
placeholder: '请输入1-5000整数',
},
timeselect: {
pickupTime: '取件时间',
},
sku: {
buyNow: '立即购买',
buyNumber: '购买数量',
addToCart: '加入购物车',
},
skuheader: {
skuId: '商品编号',
},
addresslist: {
addAddress: '新建地址',
default: '默认',
},
comment: {
complaintsText: '我要投诉',
additionalReview: (day: number) => `购买${day}天后追评`,
additionalImages: (length: number) => `${length}张追评图片`,
},
infiniteloading: {
loading: '加载中...',
pullTxt: '松开刷新',
loadMoreTxt: '哎呀,这里是底部了啦',
},
datepicker: {
year: '年',
month: '月',
day: '日',
hour: '时',
min: '分',
seconds: '秒',
},
audiooperate: {
back: '倒退',
start: '开始',
pause: '暂停',
forward: '快进',
mute: '静音',
},
pullrefresh: {
pulling: '下拉刷新',
loosing: '释放刷新',
loading: '加载中...',
},
})
}

View File

@@ -0,0 +1,111 @@
import { defineLocaleConfig } from './baseLang'
export function ZhTWLang() {
return defineLocaleConfig({
save: '保存',
confirm: '確認',
cancel: '取消',
done: '完成',
noData: '暫無數據',
placeholder: '請輸入',
select: '請選擇',
video: {
errorTip: '視頻加載失敗',
clickRetry: '點擊重試',
},
fixednav: {
activeText: '收起導航',
unActiveText: '快速導航',
},
pagination: {
prev: '上一頁',
next: '下一頁',
},
calendaritem: {
weekdays: ['日', '一', '二', '三', '四', '五', '六'],
end: '結束',
start: '開始',
title: '行事曆選擇',
monthTitle: (year: number, month: number) => `${year}${month}`,
today: '今天',
},
shortpassword: {
title: '請輸入密碼',
desc: '您使用了虛擬資產,請進行驗證',
tips: '忘記密碼',
},
uploader: {
ready: '準備完成',
readyUpload: '準備上傳',
waitingUpload: '等待上傳',
uploading: '上傳中',
success: '上傳成功',
error: '上傳失敗',
},
countdown: {
day: '天',
hour: '時',
minute: '分',
second: '秒',
},
address: {
selectRegion: '請選擇所在地區',
deliveryTo: '配送至',
chooseAnotherAddress: '選擇其他地址',
},
signature: {
reSign: '重簽',
unSupportTpl: '對不起,當前瀏覽器不支持Canvas,無法使用本控制項!',
},
ecard: {
chooseText: '請選擇電子卡面值',
otherValueText: '其他面值',
placeholder: '請輸入1-5000整數',
},
timeselect: {
pickupTime: '取件時間',
},
sku: {
buyNow: '立即購買',
buyNumber: '購買數量',
addToCart: '加入購物車',
},
skuheader: {
skuId: '商品編號',
},
addresslist: {
addAddress: '新建地址',
default: '默认',
},
comment: {
complaintsText: '我要投訴',
additionalReview: (day: number) => `購買${day}天後追評`,
additionalImages: (length: number) => `${length}張追評圖片`,
},
infiniteloading: {
loading: '加載中...',
pullTxt: '鬆開刷新',
loadMoreTxt: '哎呀,這裡是底部了啦',
},
datepicker: {
year: '年',
month: '月',
day: '日',
hour: '時',
min: '分',
seconds: '秒',
},
audiooperate: {
back: '倒退',
start: '開始',
pause: '暫停',
forward: '快進',
mute: '靜音',
},
pullrefresh: {
pulling: '下拉刷新',
loosing: '釋放刷新',
loading: '加載中...',
},
})
}

View File

@@ -0,0 +1,42 @@
import { reactive, ref } from 'vue'
import { deepAssign } from '../components/_utils'
import type { BaseLang } from './lang/baseLang'
import { EnUSLang } from './lang/en-US'
import { IdIDLang } from './lang/id-ID'
import { ZhCNLang } from './lang/zh-CN'
import { ZhTWLang } from './lang/zh-TW'
// 组件默认语言设置
export type langKeys = 'zh-CN' | 'en-US' | 'zh-tw' | 'id-id'
export type Lang = Partial<Record<langKeys, any>>
export type DeepPartial<T> = {
[K in keyof T]?: T[K] extends Record<any, any> ? DeepPartial<T[K]> : T[K]
}
export { EnUSLang, IdIDLang, ZhCNLang, ZhTWLang }
const currentLang = ref<keyof Lang>('zh-CN')
const langs = reactive<Lang>({
'zh-CN': ZhCNLang(),
'en-US': EnUSLang(),
})
export const useCurrentLang = () => currentLang
export const Locale = {
languages(): BaseLang {
return langs[currentLang.value]
},
use(lang: keyof Lang, Languages?: DeepPartial<BaseLang>) {
currentLang.value = lang
if (Languages)
langs[lang] = Languages
},
merge(Languages: DeepPartial<BaseLang>) {
deepAssign(this.languages() as any, Languages)
},
}

View File

@@ -0,0 +1,37 @@
import { getPropByPath, isFunction } from '../components/_utils'
import { Locale, useCurrentLang } from './locale'
// export function useTranslate(object: Record<keyof Lang, any>) {
// for (const [key, value] of Object.entries(object))
// Locale.merge(key as langKeys, value)
// }
export function useTranslate(compName: string) {
function translate(keyPath: string, ...args: unknown[]): string {
// 依赖响应能力
const { languages } = Locale
const text = getPropByPath(languages(), `${compName.split('-').slice(1).join('-').replace('-', '')}.${keyPath}`) || getPropByPath(languages(), keyPath)
// @ts-expect-error no types
return isFunction(text) ? text(...args) : text
}
return {
translate,
}
}
export function translateChange() {
let href = ''
const { location } = window.parent
const currentLang = useCurrentLang()
if (currentLang.value === 'zh-CN') {
href = location.href.replace('zh-CN', 'en-US')
Locale.use('en-US')
}
else {
href = location.href.replace('en-US', 'zh-CN')
Locale.use('zh-CN')
}
location.href = href
}