[+] i18n framework

This commit is contained in:
2025-11-22 23:17:26 +08:00
parent 810bbcb604
commit 7a02d455a7
4 changed files with 114 additions and 15 deletions
+17
View File
@@ -0,0 +1,17 @@
export default {
home: {
titles: {
continue: 'Continue from pause',
history: 'History',
myPlaylists: 'My Playlists',
recPlaylists: 'Recommended Playlists'
},
btn: {
importFromNetease: 'Import from NetEase'
},
text: {
playlistCreatedBy: 'From {u}'
}
}
}
+62
View File
@@ -0,0 +1,62 @@
import EN from "./en"
import ZH from "./zh"
type Lang = 'en' | 'zh'
const msgs: Record<Lang, typeof ZH> = {
en: EN,
zh: ZH
}
let lang: Lang = 'en'
// Infer language from browser
if (navigator.language.startsWith('zh')) {
lang = 'zh'
}
export const getI18n = () => msgs[lang]
// export function ts(key: string, variables?: { [index: string]: any }) {
// return t(key as keyof LocalizedMessages, variables)
// }
// /**
// * Load the translation for the given key
// *
// * @param key
// * @param variables
// */
// export function t(key: keyof LocalizedMessages, variables?: { [index: string]: any }) {
// // Check if the key exists
// let msg = getI18n()[key]
// if (!msg) {
// // Check if the key exists in English
// if (!(msg = getI18n()[key])) {
// msg = key
// console.error(`ERROR!! Missing translation reference entry (English) for ${key}`)
// }
// else console.warn(`Missing translation for ${key} in ${lang}`)
// }
// // Replace variables
// if (variables) {
// return msg.replace(/\${(.*?)}/g, (_: string, v: string | number) => variables[v] + "")
// }
// return msg
// }
// Object.assign(window, { t })
export {}
declare global {
interface String {
sed(variables: { [index: string]: any }): string
}
}
String.prototype.sed = function (variables: { [index: string]: any }) {
return this.replace(/\${(.*?)}/g, (_: string, v: string | number) => variables[v] + "")
}
Object.defineProperty(String.prototype, 'sed', { enumerable: false })
+17
View File
@@ -0,0 +1,17 @@
export default {
home: {
titles: {
continue: '从暂停的位置继续',
history: '历史数据',
myPlaylists: '我的歌单',
recPlaylists: '推荐歌单'
},
btn: {
importFromNetease: '从网易云导入'
},
text: {
playlistCreatedBy: '{u} 创建'
}
}
}
+18 -15
View File
@@ -1,14 +1,17 @@
<script lang="ts">
import AppBar from "$lib/ui/appbar/AppBar.svelte";
import TitleHeader from "$lib/ui/TitleHeader.svelte";
import SongInfo from "$lib/ui/listitem/SongInfo.svelte";
import type { PageProps } from "./$types";
import Button from "$lib/ui/Button.svelte";
import { Layer } from "m3-svelte";
import { goto } from "$app/navigation";
import AppBar from "$lib/ui/appbar/AppBar.svelte"
import TitleHeader from "$lib/ui/TitleHeader.svelte"
import SongInfo from "$lib/ui/listitem/SongInfo.svelte"
import type { PageProps } from "./$types"
import Button from "$lib/ui/Button.svelte"
import { Layer } from "m3-svelte"
import { goto } from "$app/navigation"
import { getI18n } from "$lib/i18n"
let { data }: PageProps = $props()
const t = getI18n().home
console.log(data.recPlaylists)
const loc = data.user.data.loc
@@ -23,22 +26,22 @@
<div class="vbox gap-16px overflow-y-auto flex-1">
{#if data.last}
<a {href}>
<TitleHeader title="从暂停的位置继续"/>
<TitleHeader title={t.titles.continue}/>
<div class="p-content">
<SongInfo info={data.last}></SongInfo>
</div>
</a>
{/if}
<div>
<TitleHeader title="历史数据"/>
<!-- <div>
<TitleHeader title={t.titles.history}/>
<div class="p-content">
TODO
</div>
</div>
</div> -->
<div>
<a href="/playlists/my"><TitleHeader title="我的歌单"/></a>
<a href="/playlists/my"><TitleHeader title={t.titles.myPlaylists}/></a>
<div class="p-content hbox gap-8px w-auto overflow-x-auto py-8px">
{#each data.myPlaylists as playlist}
<a class="vbox flex-shrink-0 gap-4px w-96px relative" href="/playlist/{playlist.id}">
@@ -51,12 +54,12 @@
{/each}
</div>
<div class="p-content">
<a href="/import/netease"><Button icon="i-material-symbols:cloud-download-outline">从网易云导入</Button></a>
<a href="/import/netease"><Button icon="i-material-symbols:cloud-download-outline">{t.btn.importFromNetease}</Button></a>
</div>
</div>
<div>
<a href="/playlists/rec"><TitleHeader title="推荐歌单"/></a>
<a href="/playlists/rec"><TitleHeader title={t.titles.recPlaylists}/></a>
<div class="p-content hbox gap-8px w-auto overflow-x-auto py-8px">
{#each data.recPlaylists as playlist}
<a class="vbox flex-shrink-0 p-8px gap-8px rounded-12px mbg-surface-container-high relative" href="/playlist/{playlist.id}">
@@ -64,7 +67,7 @@
<img src="{playlist.coverImgUrl}" alt="" class="size-116px rounded-8px">
<div>
<div class="m3-font-title-small font-bold truncate">{playlist.name}</div>
<div class="m3-font-body-small truncate">{playlist.creator.nickname} 创建</div>
<div class="m3-font-body-small truncate">{t.text.playlistCreatedBy.sed({u: playlist.creator.nickname})}</div>
</div>
</a>
{/each}