checkpoint: v3.1.0 — 国际化:9语种 JSON 翻译文件 + Language 菜单 + L10n/tr()
4 files modified
12 files added
| | |
| | | private var isInEditMode = false // Suppress auto-dismiss during editing |
| | | |
| | | func applicationDidFinishLaunching(_ notification: Notification) { |
| | | // Use .accessory so the app appears in Force Quit (unlike LSUIElement) |
| | | L10n.setup() |
| | | NSApp.setActivationPolicy(.accessory) |
| | | setupMenuBar() |
| | | registerHotkey() |
| | |
| | | let menu = NSMenu() |
| | | menu.addItem( |
| | | NSMenuItem( |
| | | title: "Show Apptag", |
| | | title: tr("menu.show"), |
| | | action: #selector(toggleOverlay), |
| | | keyEquivalent: "" |
| | | ) |
| | |
| | | let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "?" |
| | | let buildNumber = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "?" |
| | | let versionItem = NSMenuItem( |
| | | title: "Version \(appVersion) (\(buildNumber))", |
| | | title: "\(tr("menu.version")) \(appVersion) (\(buildNumber))", |
| | | action: nil, |
| | | keyEquivalent: "" |
| | | ) |
| | |
| | | |
| | | menu.addItem(.separator()) |
| | | let prefsItem = NSMenuItem( |
| | | title: "Preferences...", |
| | | title: tr("menu.preferences"), |
| | | action: #selector(openPreferences), |
| | | keyEquivalent: "," |
| | | ) |
| | | prefsItem.keyEquivalentModifierMask = .command |
| | | menu.addItem(prefsItem) |
| | | menu.addItem(.separator()) |
| | | |
| | | // Language submenu |
| | | let langMenu = NSMenu() |
| | | let currentLang = UserDefaults.standard.string(forKey: "appLanguage") ?? "en" |
| | | for (code, name) in L10n.supported { |
| | | let item = NSMenuItem(title: name, action: #selector(switchLanguage(_:)), keyEquivalent: "") |
| | | item.representedObject = code |
| | | item.state = (code == currentLang) ? .on : .off |
| | | langMenu.addItem(item) |
| | | } |
| | | let langItem = NSMenuItem(title: tr("menu.language"), action: nil, keyEquivalent: "") |
| | | langItem.submenu = langMenu |
| | | menu.addItem(langItem) |
| | | |
| | | menu.addItem(.separator()) |
| | | menu.addItem( |
| | | NSMenuItem( |
| | | title: "Quit", |
| | | title: tr("menu.quit"), |
| | | action: #selector(NSApplication.terminate(_:)), |
| | | keyEquivalent: "q" |
| | | ) |
| | |
| | | hideOverlay() |
| | | NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil) |
| | | } |
| | | |
| | | @objc private func switchLanguage(_ sender: NSMenuItem) { |
| | | guard let code = sender.representedObject as? String else { return } |
| | | L10n.switchTo(code) |
| | | // Rebuild menu to update checkmarks |
| | | setupMenuBar() |
| | | } |
| | | } |
| | | |
| | | // MARK: - NSView-level backdrop dismiss (works even if SwiftUI rendering is slow) |
| | |
| | | <key>CFBundlePackageType</key> |
| | | <string>APPL</string> |
| | | <key>CFBundleShortVersionString</key> |
| | | <string>3.0.11</string> |
| | | <string>3.1.0</string> |
| | | <key>CFBundleVersion</key> |
| | | <string>1</string> |
| | | <key>LSMinimumSystemVersion</key> |
| New file |
| | |
| | | import Foundation |
| | | |
| | | // MARK: - Localization Manager |
| | | |
| | | enum L10n { |
| | | static var current: [String: String] = [:] |
| | | |
| | | /// Call once at startup to load the saved or system language. |
| | | static func setup() { |
| | | let code = UserDefaults.standard.string(forKey: "appLanguage") ?? fallbackCode() |
| | | load(code) |
| | | } |
| | | |
| | | static let supported: [(code: String, name: String)] = [ |
| | | ("en", "English"), |
| | | ("zh-Hans", "简体中文"), |
| | | ("zh-Hant", "繁體中文"), |
| | | ("ja", "日本語"), |
| | | ("ko", "한국어"), |
| | | ("ru", "Русский"), |
| | | ("fr", "Français"), |
| | | ("it", "Italiano"), |
| | | ("es", "Español"), |
| | | ] |
| | | |
| | | static func switchTo(_ code: String) { |
| | | load(code) |
| | | UserDefaults.standard.set(code, forKey: "appLanguage") |
| | | } |
| | | |
| | | private static func load(_ code: String) { |
| | | guard let url = Bundle.main.url( |
| | | forResource: code, withExtension: "json", |
| | | subdirectory: "Localization" |
| | | ) else { |
| | | // Fallback to bundled English |
| | | if code != "en" { load("en") } |
| | | return |
| | | } |
| | | guard let data = try? Data(contentsOf: url), |
| | | let dict = try? JSONSerialization.jsonObject(with: data) as? [String: String] |
| | | else { return } |
| | | current = dict |
| | | } |
| | | |
| | | private static func fallbackCode() -> String { |
| | | let lang = Locale.preferredLanguages.first ?? "en" |
| | | if lang.hasPrefix("zh-Hant") || lang.hasPrefix("zh-HK") || lang.hasPrefix("zh-TW") { return "zh-Hant" } |
| | | if lang.hasPrefix("zh") { return "zh-Hans" } |
| | | if lang.hasPrefix("ja") { return "ja" } |
| | | if lang.hasPrefix("ko") { return "ko" } |
| | | if lang.hasPrefix("ru") { return "ru" } |
| | | if lang.hasPrefix("fr") { return "fr" } |
| | | if lang.hasPrefix("it") { return "it" } |
| | | if lang.hasPrefix("es") { return "es" } |
| | | return "en" |
| | | } |
| | | } |
| | | |
| | | /// Convenience function — use `tr("key")` everywhere. |
| | | func tr(_ key: String) -> String { |
| | | L10n.current[key] ?? key |
| | | } |
| New file |
| | |
| | | { |
| | | "menu.show": "Show Apptag", |
| | | "menu.preferences": "Preferences…", |
| | | "menu.quit": "Quit", |
| | | "menu.language": "Language", |
| | | "menu.version": "Version", |
| | | "settings.general": "General", |
| | | "settings.tags": "Tags", |
| | | "settings.data": "Data", |
| | | "settings.about": "About", |
| | | "settings.appListStyle": "App list style:", |
| | | "settings.flat": "Flat", |
| | | "settings.container": "Container", |
| | | "settings.flatDesc": "\"Flat\" shows apps directly. \"Container\" wraps each tag group in a rounded box.", |
| | | "settings.hideAppNames": "Hide app names", |
| | | "settings.tagPosition": "Tag position:", |
| | | "settings.left": "Left", |
| | | "settings.right": "Right", |
| | | "settings.top": "Top", |
| | | "settings.tagPosDesc": "Where the tag navigation bar appears. Left/Right puts tags in a sidebar.", |
| | | "settings.tagFontSize": "Tag font size:", |
| | | "settings.tagFontDesc": "Adjust the size of group-name labels in the overlay.", |
| | | "settings.iconSize": "Icon size:", |
| | | "settings.iconSizeDesc": "Icon display size. Grid columns adjust automatically.", |
| | | "settings.reindex": "Re-index Now", |
| | | "settings.reindexDesc": "Re-scan all app directories and refresh the tag database.", |
| | | "settings.indexMgmt": "Index Management", |
| | | "settings.backup": "Backup & Restore", |
| | | "settings.export": "Export Tags…", |
| | | "settings.import": "Import Tags…", |
| | | "settings.backupDesc": "Export saves your tag assignments to a JSON file. Import replaces the current database with one from a file.", |
| | | "settings.importFailed": "Import Failed", |
| | | "edit.exit": "Exit editing", |
| | | "edit.title": "Edit App Categories", |
| | | "edit.confirm": "Confirm", |
| | | "edit.selectTags": "Select tags:", |
| | | "edit.dragHint": "Drag to reorder", |
| | | "edit.success": "分类成功", |
| | | "edit.noTags": "No tags yet. Click \"New Tag\" to create one.", |
| | | "tag.newTag": "New Tag", |
| | | "tag.name": "Tag name", |
| | | "tag.save": "Save", |
| | | "tag.cancel": "Cancel", |
| | | "tag.add": "Add", |
| | | "tag.newName": "New tag name" |
| | | } |
| New file |
| | |
| | | { |
| | | "menu.show": "Mostrar Apptag", |
| | | "menu.preferences": "Preferencias…", |
| | | "menu.quit": "Salir", |
| | | "menu.language": "Idioma", |
| | | "menu.version": "Versión", |
| | | "settings.general": "General", |
| | | "settings.tags": "Etiquetas", |
| | | "settings.data": "Datos", |
| | | "settings.about": "Acerca de", |
| | | "settings.appListStyle": "Estilo de lista:", |
| | | "settings.flat": "Plano", |
| | | "settings.container": "Contenedor", |
| | | "settings.flatDesc": "«Plano» muestra apps directamente. «Contenedor» agrupa etiquetas en cajas.", |
| | | "settings.hideAppNames": "Ocultar nombres", |
| | | "settings.tagPosition": "Posición de etiquetas:", |
| | | "settings.left": "Izquierda", |
| | | "settings.right": "Derecha", |
| | | "settings.top": "Arriba", |
| | | "settings.tagPosDesc": "Dónde aparece la barra de navegación de etiquetas.", |
| | | "settings.tagFontSize": "Tamaño de fuente:", |
| | | "settings.tagFontDesc": "Ajusta el tamaño de las etiquetas de grupo.", |
| | | "settings.iconSize": "Tamaño de iconos:", |
| | | "settings.iconSizeDesc": "Tamaño de visualización de iconos.", |
| | | "settings.reindex": "Reindexar", |
| | | "settings.reindexDesc": "Volver a escanear todos los directorios de apps.", |
| | | "settings.indexMgmt": "Gestión de índice", |
| | | "settings.backup": "Copia de seguridad", |
| | | "settings.export": "Exportar etiquetas…", |
| | | "settings.import": "Importar etiquetas…", |
| | | "settings.backupDesc": "La exportación guarda las asignaciones de etiquetas en un archivo JSON.", |
| | | "settings.importFailed": "Error de importación", |
| | | "edit.exit": "Salir de edición", |
| | | "edit.title": "Editar categorías", |
| | | "edit.confirm": "Confirmar", |
| | | "edit.selectTags": "Seleccionar:", |
| | | "edit.dragHint": "Arrastrar para reordenar", |
| | | "edit.success": "Éxito", |
| | | "edit.noTags": "Sin etiquetas.", |
| | | "tag.newTag": "Nueva etiqueta", |
| | | "tag.name": "Nombre", |
| | | "tag.save": "Guardar", |
| | | "tag.cancel": "Cancelar", |
| | | "tag.add": "Añadir", |
| | | "tag.newName": "Nuevo nombre" |
| | | } |
| New file |
| | |
| | | { |
| | | "menu.show": "Afficher Apptag", |
| | | "menu.preferences": "Préférences…", |
| | | "menu.quit": "Quitter", |
| | | "menu.language": "Langue", |
| | | "menu.version": "Version", |
| | | "settings.general": "Général", |
| | | "settings.tags": "Étiquettes", |
| | | "settings.data": "Données", |
| | | "settings.about": "À propos", |
| | | "settings.appListStyle": "Style de liste:", |
| | | "settings.flat": "Plat", |
| | | "settings.container": "Conteneur", |
| | | "settings.flatDesc": "«Plat» affiche les apps directement. «Conteneur» groupe les étiquettes dans des boîtes.", |
| | | "settings.hideAppNames": "Masquer les noms", |
| | | "settings.tagPosition": "Position des étiquettes:", |
| | | "settings.left": "Gauche", |
| | | "settings.right": "Droite", |
| | | "settings.top": "Haut", |
| | | "settings.tagPosDesc": "Où la barre de navigation des étiquettes apparaît.", |
| | | "settings.tagFontSize": "Taille de police:", |
| | | "settings.tagFontDesc": "Ajuste la taille des noms de groupe dans l'overlay.", |
| | | "settings.iconSize": "Taille des icônes:", |
| | | "settings.iconSizeDesc": "Taille d'affichage des icônes.", |
| | | "settings.reindex": "Réindexer", |
| | | "settings.reindexDesc": "Réanalyser tous les répertoires d'applications.", |
| | | "settings.indexMgmt": "Gestion de l'index", |
| | | "settings.backup": "Sauvegarde et restauration", |
| | | "settings.export": "Exporter les étiquettes…", |
| | | "settings.import": "Importer les étiquettes…", |
| | | "settings.backupDesc": "L'exportation enregistre les attributions d'étiquettes dans un fichier JSON.", |
| | | "settings.importFailed": "Échec de l'importation", |
| | | "edit.exit": "Quitter l'édition", |
| | | "edit.title": "Modifier les catégories", |
| | | "edit.confirm": "Confirmer", |
| | | "edit.selectTags": "Sélectionner:", |
| | | "edit.dragHint": "Glisser pour réorganiser", |
| | | "edit.success": "Succès", |
| | | "edit.noTags": "Aucune étiquette.", |
| | | "tag.newTag": "Nouvelle étiquette", |
| | | "tag.name": "Nom", |
| | | "tag.save": "Enregistrer", |
| | | "tag.cancel": "Annuler", |
| | | "tag.add": "Ajouter", |
| | | "tag.newName": "Nouveau nom" |
| | | } |
| New file |
| | |
| | | { |
| | | "menu.show": "Mostra Apptag", |
| | | "menu.preferences": "Preferenze…", |
| | | "menu.quit": "Esci", |
| | | "menu.language": "Lingua", |
| | | "menu.version": "Versione", |
| | | "settings.general": "Generale", |
| | | "settings.tags": "Tag", |
| | | "settings.data": "Dati", |
| | | "settings.about": "Informazioni", |
| | | "settings.appListStyle": "Stile elenco:", |
| | | "settings.flat": "Piatto", |
| | | "settings.container": "Contenitore", |
| | | "settings.flatDesc": "«Piatto» mostra le app direttamente. «Contenitore» raggruppa i tag in riquadri.", |
| | | "settings.hideAppNames": "Nascondi nomi", |
| | | "settings.tagPosition": "Posizione tag:", |
| | | "settings.left": "Sinistra", |
| | | "settings.right": "Destra", |
| | | "settings.top": "Alto", |
| | | "settings.tagPosDesc": "Dove appare la barra di navigazione dei tag.", |
| | | "settings.tagFontSize": "Dimensione font:", |
| | | "settings.tagFontDesc": "Regola la dimensione delle etichette dei gruppi.", |
| | | "settings.iconSize": "Dimensione icone:", |
| | | "settings.iconSizeDesc": "Dimensione di visualizzazione delle icone.", |
| | | "settings.reindex": "Reindicizza", |
| | | "settings.reindexDesc": "Ripeti la scansione di tutte le directory delle app.", |
| | | "settings.indexMgmt": "Gestione indice", |
| | | "settings.backup": "Backup e ripristino", |
| | | "settings.export": "Esporta tag…", |
| | | "settings.import": "Importa tag…", |
| | | "settings.backupDesc": "L'esportazione salva le assegnazioni dei tag in un file JSON.", |
| | | "settings.importFailed": "Importazione fallita", |
| | | "edit.exit": "Esci dalla modifica", |
| | | "edit.title": "Modifica categorie", |
| | | "edit.confirm": "Conferma", |
| | | "edit.selectTags": "Seleziona tag:", |
| | | "edit.dragHint": "Trascina per riordinare", |
| | | "edit.success": "Successo", |
| | | "edit.noTags": "Nessun tag.", |
| | | "tag.newTag": "Nuovo tag", |
| | | "tag.name": "Nome tag", |
| | | "tag.save": "Salva", |
| | | "tag.cancel": "Annulla", |
| | | "tag.add": "Aggiungi", |
| | | "tag.newName": "Nuovo nome" |
| | | } |
| New file |
| | |
| | | { |
| | | "menu.show": "Apptagを表示", |
| | | "menu.preferences": "設定…", |
| | | "menu.quit": "終了", |
| | | "menu.language": "言語", |
| | | "menu.version": "バージョン", |
| | | "settings.general": "一般", |
| | | "settings.tags": "タグ", |
| | | "settings.data": "データ", |
| | | "settings.about": "について", |
| | | "settings.appListStyle": "Appリストスタイル:", |
| | | "settings.flat": "フラット", |
| | | "settings.container": "コンテナ", |
| | | "settings.flatDesc": "「フラット」はAppを直接表示。「コンテナ」は各タググループを角丸ボックスで囲みます。", |
| | | "settings.hideAppNames": "App名を隠す", |
| | | "settings.tagPosition": "タグナビ位置:", |
| | | "settings.left": "左", |
| | | "settings.right": "右", |
| | | "settings.top": "上", |
| | | "settings.tagPosDesc": "タグナビゲーションバーの表示位置。左/右はサイドバーに表示します。", |
| | | "settings.tagFontSize": "タグフォントサイズ:", |
| | | "settings.tagFontDesc": "オーバーレイ内のグループ名ラベルのサイズを調整します。", |
| | | "settings.iconSize": "アイコンサイズ:", |
| | | "settings.iconSizeDesc": "アイコンの表示サイズ。グリッド列は自動調整されます。", |
| | | "settings.reindex": "再インデックス", |
| | | "settings.reindexDesc": "すべてのAppディレクトリを再スキャンし、タグデータベースを更新します。", |
| | | "settings.indexMgmt": "インデックス管理", |
| | | "settings.backup": "バックアップと復元", |
| | | "settings.export": "タグをエクスポート…", |
| | | "settings.import": "タグをインポート…", |
| | | "settings.backupDesc": "エクスポートはタグ割り当てをJSONファイルに保存します。インポートはファイルのデータで現在のデータベースを置き換えます。", |
| | | "settings.importFailed": "インポート失敗", |
| | | "edit.exit": "編集を終了", |
| | | "edit.title": "App分類を編集", |
| | | "edit.confirm": "確認", |
| | | "edit.selectTags": "タグを選択:", |
| | | "edit.dragHint": "ドラッグで並び替え", |
| | | "edit.success": "分類成功", |
| | | "edit.noTags": "タグがありません。「新規タグ」をクリックして作成してください。", |
| | | "tag.newTag": "新規タグ", |
| | | "tag.name": "タグ名", |
| | | "tag.save": "保存", |
| | | "tag.cancel": "キャンセル", |
| | | "tag.add": "追加", |
| | | "tag.newName": "新規タグ名" |
| | | } |
| New file |
| | |
| | | { |
| | | "menu.show": "Apptag 보기", |
| | | "menu.preferences": "설정…", |
| | | "menu.quit": "종료", |
| | | "menu.language": "언어", |
| | | "menu.version": "버전", |
| | | "settings.general": "일반", |
| | | "settings.tags": "태그", |
| | | "settings.data": "데이터", |
| | | "settings.about": "정보", |
| | | "settings.appListStyle": "앱 목록 스타일:", |
| | | "settings.flat": "플랫", |
| | | "settings.container": "컨테이너", |
| | | "settings.flatDesc": "「플랫」은 앱을 직접 표시합니다.「컨테이너」는 각 태그 그룹을 둥근 상자로 묶습니다.", |
| | | "settings.hideAppNames": "앱 이름 숨기기", |
| | | "settings.tagPosition": "태그 탐색 위치:", |
| | | "settings.left": "왼쪽", |
| | | "settings.right": "오른쪽", |
| | | "settings.top": "위쪽", |
| | | "settings.tagPosDesc": "태그 탐색 막대의 표시 위치입니다.", |
| | | "settings.tagFontSize": "태그 글꼴 크기:", |
| | | "settings.tagFontDesc": "오버레이에서 그룹 이름 레이블의 크기를 조정합니다.", |
| | | "settings.iconSize": "아이콘 크기:", |
| | | "settings.iconSizeDesc": "아이콘 표시 크기입니다.", |
| | | "settings.reindex": "재인덱싱", |
| | | "settings.reindexDesc": "모든 앱 디렉토리를 다시 스캔하고 태그 데이터베이스를 새로 고칩니다.", |
| | | "settings.indexMgmt": "인덱스 관리", |
| | | "settings.backup": "백업 및 복원", |
| | | "settings.export": "태그 내보내기…", |
| | | "settings.import": "태그 가져오기…", |
| | | "settings.backupDesc": "내보내기는 태그 할당을 JSON 파일로 저장합니다.", |
| | | "settings.importFailed": "가져오기 실패", |
| | | "edit.exit": "편집 종료", |
| | | "edit.title": "앱 분류 편집", |
| | | "edit.confirm": "확인", |
| | | "edit.selectTags": "태그 선택:", |
| | | "edit.dragHint": "드래그하여 재정렬", |
| | | "edit.success": "분류 성공", |
| | | "edit.noTags": "태그가 없습니다.", |
| | | "tag.newTag": "새 태그", |
| | | "tag.name": "태그 이름", |
| | | "tag.save": "저장", |
| | | "tag.cancel": "취소", |
| | | "tag.add": "추가", |
| | | "tag.newName": "새 태그 이름" |
| | | } |
| New file |
| | |
| | | { |
| | | "menu.show": "Показать Apptag", |
| | | "menu.preferences": "Настройки…", |
| | | "menu.quit": "Выход", |
| | | "menu.language": "Язык", |
| | | "menu.version": "Версия", |
| | | "settings.general": "Общие", |
| | | "settings.tags": "Теги", |
| | | "settings.data": "Данные", |
| | | "settings.about": "О программе", |
| | | "settings.appListStyle": "Стиль списка:", |
| | | "settings.flat": "Плитка", |
| | | "settings.container": "Контейнер", |
| | | "settings.flatDesc": "«Плитка» показывает приложения напрямую. «Контейнер» помещает группы в рамки.", |
| | | "settings.hideAppNames": "Скрыть названия", |
| | | "settings.tagPosition": "Позиция тегов:", |
| | | "settings.left": "Слева", |
| | | "settings.right": "Справа", |
| | | "settings.top": "Сверху", |
| | | "settings.tagPosDesc": "Где отображается панель навигации по тегам.", |
| | | "settings.tagFontSize": "Размер шрифта:", |
| | | "settings.tagFontDesc": "Размер названий групп в оверлее.", |
| | | "settings.iconSize": "Размер иконок:", |
| | | "settings.iconSizeDesc": "Размер отображения иконок.", |
| | | "settings.reindex": "Переиндексировать", |
| | | "settings.reindexDesc": "Пересканировать все директории приложений.", |
| | | "settings.indexMgmt": "Управление индексом", |
| | | "settings.backup": "Резервное копирование", |
| | | "settings.export": "Экспорт тегов…", |
| | | "settings.import": "Импорт тегов…", |
| | | "settings.backupDesc": "Экспорт сохраняет назначения тегов в JSON-файл.", |
| | | "settings.importFailed": "Ошибка импорта", |
| | | "edit.exit": "Выйти из редактирования", |
| | | "edit.title": "Редактировать категории", |
| | | "edit.confirm": "Подтвердить", |
| | | "edit.selectTags": "Выбрать теги:", |
| | | "edit.dragHint": "Перетащите для сортировки", |
| | | "edit.success": "Готово", |
| | | "edit.noTags": "Нет тегов.", |
| | | "tag.newTag": "Новый тег", |
| | | "tag.name": "Имя тега", |
| | | "tag.save": "Сохранить", |
| | | "tag.cancel": "Отмена", |
| | | "tag.add": "Добавить", |
| | | "tag.newName": "Новое имя тега" |
| | | } |
| New file |
| | |
| | | { |
| | | "menu.show": "显示 Apptag", |
| | | "menu.preferences": "设置…", |
| | | "menu.quit": "退出", |
| | | "menu.language": "语言", |
| | | "menu.version": "版本", |
| | | "settings.general": "通用", |
| | | "settings.tags": "标签", |
| | | "settings.data": "数据", |
| | | "settings.about": "关于", |
| | | "settings.appListStyle": "App 列表样式:", |
| | | "settings.flat": "平铺", |
| | | "settings.container": "容器", |
| | | "settings.flatDesc": "「平铺」直接显示 App。「容器」将每个标签组放入圆角框。", |
| | | "settings.hideAppNames": "隐藏 App 名称", |
| | | "settings.tagPosition": "标签导航位置:", |
| | | "settings.left": "左侧", |
| | | "settings.right": "右侧", |
| | | "settings.top": "顶部", |
| | | "settings.tagPosDesc": "标签导航栏的显示位置。左/右将在侧边栏中显示标签。", |
| | | "settings.tagFontSize": "标签字体大小:", |
| | | "settings.tagFontDesc": "调整浮层中分组名称标签的大小。", |
| | | "settings.iconSize": "图标大小:", |
| | | "settings.iconSizeDesc": "图标显示尺寸。网格列会自动调整。", |
| | | "settings.reindex": "重新索引", |
| | | "settings.reindexDesc": "重新扫描所有 App 目录并刷新标签数据库。", |
| | | "settings.indexMgmt": "索引管理", |
| | | "settings.backup": "备份与恢复", |
| | | "settings.export": "导出标签…", |
| | | "settings.import": "导入标签…", |
| | | "settings.backupDesc": "导出将标签分配保存为 JSON 文件。导入将用文件中的数据替换当前数据库。", |
| | | "settings.importFailed": "导入失败", |
| | | "edit.exit": "退出编辑", |
| | | "edit.title": "编辑 App 分类", |
| | | "edit.confirm": "确认", |
| | | "edit.selectTags": "选择标签:", |
| | | "edit.dragHint": "拖动排序", |
| | | "edit.success": "分类成功", |
| | | "edit.noTags": "暂无标签。点击「新建标签」创建一个。", |
| | | "tag.newTag": "新建标签", |
| | | "tag.name": "标签名称", |
| | | "tag.save": "保存", |
| | | "tag.cancel": "取消", |
| | | "tag.add": "添加", |
| | | "tag.newName": "新标签名称" |
| | | } |
| New file |
| | |
| | | { |
| | | "menu.show": "顯示 Apptag", |
| | | "menu.preferences": "設定…", |
| | | "menu.quit": "退出", |
| | | "menu.language": "語言", |
| | | "menu.version": "版本", |
| | | "settings.general": "一般", |
| | | "settings.tags": "標籤", |
| | | "settings.data": "資料", |
| | | "settings.about": "關於", |
| | | "settings.appListStyle": "App 列表樣式:", |
| | | "settings.flat": "平鋪", |
| | | "settings.container": "容器", |
| | | "settings.flatDesc": "「平鋪」直接顯示 App。「容器」將每個標籤組放入圓角框。", |
| | | "settings.hideAppNames": "隱藏 App 名稱", |
| | | "settings.tagPosition": "標籤導航位置:", |
| | | "settings.left": "左側", |
| | | "settings.right": "右側", |
| | | "settings.top": "頂部", |
| | | "settings.tagPosDesc": "標籤導航欄的顯示位置。左/右將在側邊欄中顯示標籤。", |
| | | "settings.tagFontSize": "標籤字型大小:", |
| | | "settings.tagFontDesc": "調整浮層中分組名稱標籤的大小。", |
| | | "settings.iconSize": "圖示大小:", |
| | | "settings.iconSizeDesc": "圖示顯示尺寸。網格列會自動調整。", |
| | | "settings.reindex": "重新索引", |
| | | "settings.reindexDesc": "重新掃描所有 App 目錄並重新整理標籤資料庫。", |
| | | "settings.indexMgmt": "索引管理", |
| | | "settings.backup": "備份與還原", |
| | | "settings.export": "匯出標籤…", |
| | | "settings.import": "匯入標籤…", |
| | | "settings.backupDesc": "匯出將標籤指派儲存為 JSON 檔案。匯入將用檔案中的資料取代目前資料庫。", |
| | | "settings.importFailed": "匯入失敗", |
| | | "edit.exit": "退出編輯", |
| | | "edit.title": "編輯 App 分類", |
| | | "edit.confirm": "確認", |
| | | "edit.selectTags": "選擇標籤:", |
| | | "edit.dragHint": "拖曳排序", |
| | | "edit.success": "分類成功", |
| | | "edit.noTags": "尚無標籤。點擊「新增標籤」建立一個。", |
| | | "tag.newTag": "新增標籤", |
| | | "tag.name": "標籤名稱", |
| | | "tag.save": "儲存", |
| | | "tag.cancel": "取消", |
| | | "tag.add": "新增", |
| | | "tag.newName": "新標籤名稱" |
| | | } |
| | |
| | | # Apptag Changelog |
| | | |
| | | ## [3.1.0] — 2026-05-06 |
| | | |
| | | - 国际化:支持 9 种语言(英/简中/繁中/日/韩/俄/法/意/西) |
| | | - 菜单栏新增 Language 子菜单切换语言 |
| | | - 翻译文件独立存储在 `Apptag/Localization/*.json` |
| | | - `L10n.swift` 管理器 + `tr()` 辅助函数 |
| | | |
| | | ## [3.0.11] — 2026-05-06 |
| | | |
| | | - 容器标题最小宽度优化:`.layoutPriority(1)` 让文字优先占空间,分隔线压缩,可显示约15字符 |
| | |
| | | iconutil -c icns "$SWIFT_DIR/AppIcon.iconset" -o "$RESOURCES_DIR/AppIcon.icns" 2>/dev/null |
| | | fi |
| | | |
| | | echo "==> Copying Localization files..." |
| | | cp -r "$SWIFT_DIR/Localization" "$RESOURCES_DIR/Localization" |
| | | |
| | | echo "==> Setting permissions..." |
| | | chmod +x "$MACOS_DIR/$APP_NAME" |
| | | |