feat: add i18n support and app icon in top-left corner
- Added i18n JSON files (zh.json, en.json) with auto language detection - Updated miniplayer.ui to use i18n keys for all button labels - Added MiniPlayerIcon overlay in top-left corner of main window - Load i18n translations on app startup based on system locale
This commit is contained in:
parent
49bc681cf7
commit
e7bca7269e
@ -15,6 +15,7 @@ struct MiniPlayerApp: App {
|
|||||||
.onAppear {
|
.onAppear {
|
||||||
bridge.setup()
|
bridge.setup()
|
||||||
setupBridgeEvents()
|
setupBridgeEvents()
|
||||||
|
loadI18n()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
@ -108,6 +109,20 @@ struct MiniPlayerApp: App {
|
|||||||
bridge.openFileDialog()
|
bridge.openFileDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载 i18n 翻译
|
||||||
|
private func loadI18n() {
|
||||||
|
// 检测系统语言
|
||||||
|
let locale = Locale.current.language.languageCode?.identifier ?? "en"
|
||||||
|
let langFile = (locale == "zh") ? "zh" : "en"
|
||||||
|
|
||||||
|
if let url = Bundle.module.url(forResource: "i18n/\(langFile)", withExtension: "json"),
|
||||||
|
let data = try? Data(contentsOf: url),
|
||||||
|
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: String] {
|
||||||
|
engine.i18n.loadMessages(dict)
|
||||||
|
engine.i18n.locale = langFile
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - BricksView wrapper
|
// MARK: - BricksView wrapper
|
||||||
@ -117,7 +132,7 @@ struct BricksAppView: View {
|
|||||||
@State private var jsonString: String?
|
@State private var jsonString: String?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack(alignment: .topLeading) {
|
||||||
if let json = jsonString {
|
if let json = jsonString {
|
||||||
BricksView(json: json, engine: engine)
|
BricksView(json: json, engine: engine)
|
||||||
} else {
|
} else {
|
||||||
@ -126,6 +141,11 @@ struct BricksAppView: View {
|
|||||||
jsonString = loadJSONString()
|
jsonString = loadJSONString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 左上角应用图标
|
||||||
|
MiniPlayerIcon()
|
||||||
|
.frame(width: 32, height: 32)
|
||||||
|
.padding(12)
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $bridge.showURLDialog) {
|
.sheet(isPresented: $bridge.showURLDialog) {
|
||||||
URLInputDialog(bridge: bridge)
|
URLInputDialog(bridge: bridge)
|
||||||
|
|||||||
9
Sources/Resources/i18n/en.json
Normal file
9
Sources/Resources/i18n/en.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"open": "Open",
|
||||||
|
"prev": "Prev",
|
||||||
|
"play_pause": "Play/Pause",
|
||||||
|
"next": "Next",
|
||||||
|
"url_placeholder": "Enter video URL...",
|
||||||
|
"load": "Load",
|
||||||
|
"fullscreen": "Fullscreen"
|
||||||
|
}
|
||||||
9
Sources/Resources/i18n/zh.json
Normal file
9
Sources/Resources/i18n/zh.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"open": "打开",
|
||||||
|
"prev": "上一个",
|
||||||
|
"play_pause": "播放/暂停",
|
||||||
|
"next": "下一个",
|
||||||
|
"url_placeholder": "输入视频URL...",
|
||||||
|
"load": "加载",
|
||||||
|
"fullscreen": "全屏"
|
||||||
|
}
|
||||||
@ -28,7 +28,7 @@
|
|||||||
"widgettype": "Button",
|
"widgettype": "Button",
|
||||||
"id": "openBtn",
|
"id": "openBtn",
|
||||||
"options": {
|
"options": {
|
||||||
"label": "Open",
|
"label": "open",
|
||||||
"icon": "folder",
|
"icon": "folder",
|
||||||
"cwidth": 8,
|
"cwidth": 8,
|
||||||
"cheight": 4,
|
"cheight": 4,
|
||||||
@ -48,7 +48,7 @@
|
|||||||
"widgettype": "Button",
|
"widgettype": "Button",
|
||||||
"id": "prevBtn",
|
"id": "prevBtn",
|
||||||
"options": {
|
"options": {
|
||||||
"label": "Prev",
|
"label": "prev",
|
||||||
"icon": "backward.fill",
|
"icon": "backward.fill",
|
||||||
"cwidth": 8,
|
"cwidth": 8,
|
||||||
"cheight": 4,
|
"cheight": 4,
|
||||||
@ -68,7 +68,7 @@
|
|||||||
"widgettype": "Button",
|
"widgettype": "Button",
|
||||||
"id": "toggleBtn",
|
"id": "toggleBtn",
|
||||||
"options": {
|
"options": {
|
||||||
"label": "Play/Pause",
|
"label": "play_pause",
|
||||||
"icon": "playpause.fill",
|
"icon": "playpause.fill",
|
||||||
"cwidth": 12,
|
"cwidth": 12,
|
||||||
"cheight": 4,
|
"cheight": 4,
|
||||||
@ -88,7 +88,7 @@
|
|||||||
"widgettype": "Button",
|
"widgettype": "Button",
|
||||||
"id": "nextBtn",
|
"id": "nextBtn",
|
||||||
"options": {
|
"options": {
|
||||||
"label": "Next",
|
"label": "next",
|
||||||
"icon": "forward.fill",
|
"icon": "forward.fill",
|
||||||
"cwidth": 8,
|
"cwidth": 8,
|
||||||
"cheight": 4,
|
"cheight": 4,
|
||||||
@ -112,7 +112,7 @@
|
|||||||
"id": "videoUrlInput",
|
"id": "videoUrlInput",
|
||||||
"options": {
|
"options": {
|
||||||
"name": "url",
|
"name": "url",
|
||||||
"placeholder": "Enter video URL...",
|
"placeholder": "url_placeholder",
|
||||||
"cwidth": 30,
|
"cwidth": 30,
|
||||||
"cheight": 4
|
"cheight": 4
|
||||||
}
|
}
|
||||||
@ -121,7 +121,7 @@
|
|||||||
"widgettype": "Button",
|
"widgettype": "Button",
|
||||||
"id": "loadBtn",
|
"id": "loadBtn",
|
||||||
"options": {
|
"options": {
|
||||||
"label": "Load",
|
"label": "load",
|
||||||
"icon": "arrow.down.circle.fill",
|
"icon": "arrow.down.circle.fill",
|
||||||
"cwidth": 8,
|
"cwidth": 8,
|
||||||
"cheight": 4,
|
"cheight": 4,
|
||||||
@ -142,7 +142,7 @@
|
|||||||
"widgettype": "Button",
|
"widgettype": "Button",
|
||||||
"id": "fullscreenBtn",
|
"id": "fullscreenBtn",
|
||||||
"options": {
|
"options": {
|
||||||
"label": "Fullscreen",
|
"label": "fullscreen",
|
||||||
"icon": "arrow.up.left.and.arrow.down.right",
|
"icon": "arrow.up.left.and.arrow.down.right",
|
||||||
"cwidth": 10,
|
"cwidth": 10,
|
||||||
"cheight": 4,
|
"cheight": 4,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user