diff --git a/OpenSim.xcodeproj/project.pbxproj b/OpenSim.xcodeproj/project.pbxproj index 40513cd..acdf0e0 100644 --- a/OpenSim.xcodeproj/project.pbxproj +++ b/OpenSim.xcodeproj/project.pbxproj @@ -7,7 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 312A27FD21A753E600699668 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 312A27FC21A753E600699668 /* Constants.swift */; }; 31A79B15219B81660024DF7B /* Simulator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31A79B14219B81660024DF7B /* Simulator.swift */; }; + 31C4BF3221A8AC56008B97A1 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 31C4BF3021A8AC56008B97A1 /* Localizable.strings */; }; AF9D003A1D110E750065AFD0 /* Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF9D00391D110E750065AFD0 /* Helper.swift */; }; B3054E181BF381CE00F433C2 /* FileInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3054E161BF381CE00F433C2 /* FileInfo.swift */; }; B3054E1A1BF3958500F433C2 /* DirectoryWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3054E191BF3958500F433C2 /* DirectoryWatcher.swift */; }; @@ -38,7 +40,10 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 312A27FC21A753E600699668 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 31A79B14219B81660024DF7B /* Simulator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Simulator.swift; sourceTree = ""; }; + 31C4BF3121A8AC56008B97A1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; + 31C4BF3421A8AC72008B97A1 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; AF9D00391D110E750065AFD0 /* Helper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Helper.swift; sourceTree = ""; }; B3054E161BF381CE00F433C2 /* FileInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileInfo.swift; sourceTree = ""; }; B3054E191BF3958500F433C2 /* DirectoryWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DirectoryWatcher.swift; sourceTree = ""; }; @@ -81,6 +86,25 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 31C4BF2321A8AA3D008B97A1 /* AppDelegate */ = { + isa = PBXGroup; + children = ( + B3A1E3221BF049690090EC58 /* AppDelegate.swift */, + ); + name = AppDelegate; + sourceTree = ""; + }; + 31C4BF2721A8AA52008B97A1 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + B3A1E3241BF049690090EC58 /* Assets.xcassets */, + 31C4BF3021A8AC56008B97A1 /* Localizable.strings */, + B3A1E3261BF049690090EC58 /* MainMenu.xib */, + B3A1E3291BF049690090EC58 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; B36856AC1CA41BA8009BE4CC /* Models */ = { isa = PBXGroup; children = ( @@ -117,6 +141,7 @@ B39B2B601EBEF1A100CDD74C /* AppMenuItem.swift */, B32480541EBEB705000633FC /* ActionMenu.swift */, B39B2B621EBEFFB700CDD74C /* AppInfoView.swift */, + 312A27FC21A753E600699668 /* Constants.swift */, ); name = UI; sourceTree = ""; @@ -153,14 +178,12 @@ B3A1E3211BF049690090EC58 /* OpenSim */ = { isa = PBXGroup; children = ( - B3A1E3221BF049690090EC58 /* AppDelegate.swift */, + 31C4BF2321A8AA3D008B97A1 /* AppDelegate */, B36856AC1CA41BA8009BE4CC /* Models */, B39B2B6D1EBF511200CDD74C /* Actions */, B36856AD1CA41BCB009BE4CC /* Utilities */, B36856AE1CA41BD4009BE4CC /* UI */, - B3A1E3241BF049690090EC58 /* Assets.xcassets */, - B3A1E3261BF049690090EC58 /* MainMenu.xib */, - B3A1E3291BF049690090EC58 /* Info.plist */, + 31C4BF2721A8AA52008B97A1 /* Supporting Files */, ); path = OpenSim; sourceTree = ""; @@ -208,6 +231,7 @@ knownRegions = ( en, Base, + "pt-BR", ); mainGroup = B3A1E3161BF049690090EC58; productRefGroup = B3A1E3201BF049690090EC58 /* Products */; @@ -225,6 +249,7 @@ buildActionMask = 2147483647; files = ( B3A1E3251BF049690090EC58 /* Assets.xcassets in Resources */, + 31C4BF3221A8AC56008B97A1 /* Localizable.strings in Resources */, B3A1E3281BF049690090EC58 /* MainMenu.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -256,6 +281,7 @@ B39B2B671EBF0D0600CDD74C /* LaunchAtLoginHelper.swift in Sources */, B39B2B631EBEFFB700CDD74C /* AppInfoView.swift in Sources */, B3A1E3301BF05F980090EC58 /* DeviceManager.swift in Sources */, + 312A27FD21A753E600699668 /* Constants.swift in Sources */, B3A1E3231BF049690090EC58 /* AppDelegate.swift in Sources */, B39B2B6F1EBF567F00CDD74C /* CopyToPasteboardAction.swift in Sources */, B39B2B731EBF573C00CDD74C /* UninstallAction.swift in Sources */, @@ -268,6 +294,15 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ + 31C4BF3021A8AC56008B97A1 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 31C4BF3121A8AC56008B97A1 /* Base */, + 31C4BF3421A8AC72008B97A1 /* pt-BR */, + ); + name = Localizable.strings; + sourceTree = ""; + }; B3A1E3261BF049690090EC58 /* MainMenu.xib */ = { isa = PBXVariantGroup; children = ( @@ -283,6 +318,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -338,6 +374,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; diff --git a/OpenSim/ActionMenu.swift b/OpenSim/ActionMenu.swift index c14d115..cc7924d 100644 --- a/OpenSim/ActionMenu.swift +++ b/OpenSim/ActionMenu.swift @@ -45,9 +45,9 @@ final class ActionMenu: NSMenu { private func buildMenuItems() { let createAction: (ApplicationActionable.Type) -> ApplicationActionable = { $0.init(application: self.application) } - self.buildMenuSection(title: NSLocalizedString("Actions", comment: ""), actions: ActionMenu.standardActions.map(createAction)) - self.buildMenuSection(title: NSLocalizedString("Extensions", comment: ""), actions: ActionMenu.extraActions.map(createAction)) - self.addItem(self.buildSectionTitle(title: NSLocalizedString("App Information", comment: ""))) + self.buildMenuSection(title: UIConstants.strings.menuHeaderActions, actions: ActionMenu.standardActions.map(createAction)) + self.buildMenuSection(title: UIConstants.strings.menuHeaderExtensions, actions: ActionMenu.extraActions.map(createAction)) + self.addItem(self.buildSectionTitle(title: UIConstants.strings.menuHeaderAppInformation)) self.addItem(appInfoItem) } diff --git a/OpenSim/AppInfoView.swift b/OpenSim/AppInfoView.swift index ad40673..9c7b9c5 100644 --- a/OpenSim/AppInfoView.swift +++ b/OpenSim/AppInfoView.swift @@ -56,8 +56,8 @@ final class AppInfoView: NSView { sizeDescription = ByteCountFormatter.string(fromByteCount: Int64(size), countStyle: .file) } let string = "\(application.bundleID)\n" + - "Version: \(application.bundleVersion) (\(application.bundleShortVersion))\n" + - "Size: \(sizeDescription)" + "\(UIConstants.strings.appInfoVersion): \(application.bundleVersion) (\(application.bundleShortVersion))\n" + + "\(UIConstants.strings.appInfoSize): \(sizeDescription)" textField.stringValue = string } diff --git a/OpenSim/Application.swift b/OpenSim/Application.swift index 55b2a72..e8d4c00 100755 --- a/OpenSim/Application.swift +++ b/OpenSim/Application.swift @@ -89,16 +89,14 @@ final class Application { block(size) } else { Application.sizeDispatchQueue.async { - var size: UInt64 = 0 - let filesEnumerator = FileManager.default.enumerator(at: self.url, includingPropertiesForKeys: nil, options: [], errorHandler: { (url, error) -> Bool in - return true - }) - while let fileUrl = filesEnumerator?.nextObject() as? URL { - let attributes = try? FileManager.default.attributesOfItem(atPath: fileUrl.path) as NSDictionary - size += attributes?.fileSize() ?? 0 + let duResult = shell("/usr/bin/du", arguments: ["-sk", self.url.path]) + let stringBytes = String(duResult.split(separator: "\t").first ?? "") + var bytes: UInt64 = 0 + if let kbytes = UInt64(stringBytes) { + bytes = kbytes * 1000 + self.size = bytes; } - self.size = size - block(size) + block(bytes) } } } diff --git a/OpenSim/Base.lproj/Localizable.strings b/OpenSim/Base.lproj/Localizable.strings new file mode 100644 index 0000000..cfa724a --- /dev/null +++ b/OpenSim/Base.lproj/Localizable.strings @@ -0,0 +1,18 @@ +"Menu.Quit" = "Quit"; +"Menu.LaunchAtLogin" = "Launch at Login"; +"Menu.Refresh" = "Refresh"; +"Menu.Version" = "Version"; +"Action.RevealInFinder" = "Reveal Sandbox in Finder"; +"Action.CopyPathPasteboard" = "Copy Sandbox Path to Pasteboard"; +"Action.OpenInTerminal" = "Open Sandbox in Terminal"; +"Action.Uninstall" = "Uninstall"; +"Action.UninstallAlertConfirmButton" = "Uninstall"; +"Action.UninstallAlertCancelButton" = "Cancel"; +"Action.UninstallAlertMessage" = "Are you sure you want to uninstall %@ from %@?"; +"Extension.OpenInIterm" = "Open Sandbox in iTerm"; +"Extension.OpenRealmDatabase" = "Open Realm Database"; +"AppInfo.Version" = "Version"; +"AppInfo.Size" = "Size"; +"MenuHeader.Actions" = "Actions"; +"MenuHeader.Extensions" = "Extensions"; +"MenuHeader.AppInformation" = "App Information"; diff --git a/OpenSim/Constants.swift b/OpenSim/Constants.swift new file mode 100644 index 0000000..52db941 --- /dev/null +++ b/OpenSim/Constants.swift @@ -0,0 +1,26 @@ + +import Foundation + +struct UIConstants { + + struct strings { + static let menuQuitButton = NSLocalizedString("Menu.Quit", comment: "Quit menu button") + static let menuLaunchAtLoginButton = NSLocalizedString("Menu.LaunchAtLogin", comment: "Launch at login menu button") + static let menuRefreshButton = NSLocalizedString("Menu.Refresh", comment: "Refresh menu button") + static let menuVersionLabel = NSLocalizedString("Menu.Version", comment: "Version label") + static let actionRevealInFinder = NSLocalizedString("Action.RevealInFinder", comment: "Reveal in Finder label") + static let actionCopyPathPasteboard = NSLocalizedString("Action.CopyPathPasteboard", comment: "Copy Sandbox path to pasteboard label") + static let actionOpenInTerminal = NSLocalizedString("Action.OpenInTerminal", comment: "Open in Terminal label") + static let actionUninstall = NSLocalizedString("Action.Uninstall", comment: "Uninstall label") + static let actionUninstallAlertConfirmButton = NSLocalizedString("Action.UninstallAlertConfirmButton", comment: "Uninstall confirm button") + static let actionUninstallAlertCancelButton = NSLocalizedString("Action.UninstallAlertCancelButton", comment: "Uninstall cancel button") + static let actionUninstallAlertMessage = NSLocalizedString("Action.UninstallAlertMessage", comment: "Uninstall confirmation message") + static let extensionOpenInIterm = NSLocalizedString("Extension.OpenInIterm", comment: "Open in iTerm label") + static let extensionOpenRealmDatabase = NSLocalizedString("Extension.OpenRealmDatabase", comment: "Open Realm Database label") + static let appInfoVersion = NSLocalizedString("AppInfo.Version", comment: "App Info Version Label") + static let appInfoSize = NSLocalizedString("AppInfo.Size", comment: "App Info Size Label") + static let menuHeaderActions = NSLocalizedString("MenuHeader.Actions", comment: "Actions header label") + static let menuHeaderExtensions = NSLocalizedString("MenuHeader.Extensions", comment: "Extensions header label") + static let menuHeaderAppInformation = NSLocalizedString("MenuHeader.AppInformation", comment: "App Information header label") + } +} diff --git a/OpenSim/CopyToPasteboardAction.swift b/OpenSim/CopyToPasteboardAction.swift index c46567e..6fde863 100644 --- a/OpenSim/CopyToPasteboardAction.swift +++ b/OpenSim/CopyToPasteboardAction.swift @@ -12,7 +12,7 @@ final class CopyToPasteboardAction: ApplicationActionable { var application: Application? - let title = NSLocalizedString("Copy Sandbox Path to Pasteboard", comment: "") + let title = UIConstants.strings.actionCopyPathPasteboard let icon = templatize(#imageLiteral(resourceName: "share")) diff --git a/OpenSim/Info.plist b/OpenSim/Info.plist index 40565c6..93d46bf 100644 --- a/OpenSim/Info.plist +++ b/OpenSim/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.4.1 + 0.4.2 CFBundleSignature ???? CFBundleVersion diff --git a/OpenSim/MenuManager.swift b/OpenSim/MenuManager.swift index ed47887..5b77958 100644 --- a/OpenSim/MenuManager.swift +++ b/OpenSim/MenuManager.swift @@ -61,7 +61,7 @@ protocol MenuManagerDelegate { return } menu.addItem(NSMenuItem.separator()) - let titleItem = NSMenuItem(title: "\(runtime) Simulators", action: nil, keyEquivalent: "") + let titleItem = NSMenuItem(title: "\(runtime)", action: nil, keyEquivalent: "") titleItem.isEnabled = false menu.addItem(titleItem) @@ -90,21 +90,26 @@ protocol MenuManagerDelegate { } menu.addItem(NSMenuItem.separator()) - - let refreshMenuItem = menu.addItem(withTitle: NSLocalizedString("Refresh", comment: ""), action: #selector(self.refreshItemClicked(_:)), keyEquivalent: "r") + + let refreshMenuItem = menu.addItem(withTitle: UIConstants.strings.menuRefreshButton, action: #selector(self.refreshItemClicked(_:)), keyEquivalent: "r") refreshMenuItem.target = self - - let launchAtLoginMenuItem = menu.addItem(withTitle: NSLocalizedString("Launch at Login", comment: ""), action: #selector(self.launchItemClicked(_:)), keyEquivalent: "") + + let launchAtLoginMenuItem = menu.addItem(withTitle: UIConstants.strings.menuLaunchAtLoginButton, action: #selector(self.launchItemClicked(_:)), keyEquivalent: "") launchAtLoginMenuItem.target = self if existingItem(itemUrl: Bundle.main.bundleURL) != nil { launchAtLoginMenuItem.state = .on } else { launchAtLoginMenuItem.state = .off } - - let quitMenu = menu.addItem(withTitle: NSLocalizedString("Quit", comment: ""), action: #selector(self.quitItemClicked(_:)), keyEquivalent: "q") + + let quitMenu = menu.addItem(withTitle: UIConstants.strings.menuQuitButton, action: #selector(self.quitItemClicked(_:)), keyEquivalent: "q") quitMenu.target = self + if let versionNumber = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String { + menu.addItem(NSMenuItem.separator()) + menu.addItem(withTitle: "\(UIConstants.strings.menuVersionLabel) \(versionNumber)", action: nil, keyEquivalent: "") + } + self.statusItem.menu = menu } } @@ -112,7 +117,7 @@ protocol MenuManagerDelegate { private func buildWatcher() { watcher = DirectoryWatcher(in: URLHelper.deviceURL) watcher.completionCallback = { [weak self] in - self?.reloadWhenReady() + self?.reloadWhenReady(delay: 5) self?.buildSubWatchers() } try? watcher.start() @@ -137,9 +142,9 @@ protocol MenuManagerDelegate { } - private func reloadWhenReady() { + private func reloadWhenReady(delay: TimeInterval = 1) { dispatch_cancel_block_t(self.block) - self.block = dispatch_block_t(1) { [weak self] in + self.block = dispatch_block_t(delay) { [weak self] in self?.watcher.stop() self?.buildMenu() try? self?.watcher.start() diff --git a/OpenSim/OpenInItermAction.swift b/OpenSim/OpenInItermAction.swift index a0a1d4c..05bd2e0 100644 --- a/OpenSim/OpenInItermAction.swift +++ b/OpenSim/OpenInItermAction.swift @@ -14,7 +14,7 @@ class OpenInItermAction: ExtraApplicationActionable { let appBundleIdentifier = "com.googlecode.iterm2" - let title = NSLocalizedString("Open Sandbox in iTerm", comment: "") + let title = UIConstants.strings.extensionOpenInIterm required init(application: Application) { self.application = application diff --git a/OpenSim/OpenInTerminalAction.swift b/OpenSim/OpenInTerminalAction.swift index d9bc8f0..7404f92 100644 --- a/OpenSim/OpenInTerminalAction.swift +++ b/OpenSim/OpenInTerminalAction.swift @@ -12,7 +12,7 @@ final class OpenInTerminalAction: ApplicationActionable { var application: Application? - let title = NSLocalizedString("Open Sandbox in Terminal", comment: "") + let title = UIConstants.strings.actionOpenInTerminal let icon = templatize(#imageLiteral(resourceName: "terminal")) diff --git a/OpenSim/OpenRealmAction.swift b/OpenSim/OpenRealmAction.swift index be35e16..2c6548d 100644 --- a/OpenSim/OpenRealmAction.swift +++ b/OpenSim/OpenRealmAction.swift @@ -14,7 +14,7 @@ final class OpenRealmAction: ExtraApplicationActionable { let appBundleIdentifier = "io.realm.realmbrowser" - let title = "Open Realm Database" + let title = UIConstants.strings.extensionOpenRealmDatabase var isAvailable: Bool { return appPath != nil && realmPath != nil diff --git a/OpenSim/RevealInFinderAction.swift b/OpenSim/RevealInFinderAction.swift index 924231d..c77562d 100644 --- a/OpenSim/RevealInFinderAction.swift +++ b/OpenSim/RevealInFinderAction.swift @@ -12,7 +12,7 @@ final class RevealInFinderAction: ApplicationActionable { var application: Application? - let title = NSLocalizedString("Reveal Sandbox in Finder", comment: "") + let title = UIConstants.strings.actionRevealInFinder let icon = templatize(#imageLiteral(resourceName: "reveal")) diff --git a/OpenSim/UninstallAction.swift b/OpenSim/UninstallAction.swift index 15b103d..e87f8bd 100644 --- a/OpenSim/UninstallAction.swift +++ b/OpenSim/UninstallAction.swift @@ -12,7 +12,7 @@ final class UninstallAction: ApplicationActionable { var application: Application? - let title = NSLocalizedString("Uninstall…", comment: "") + let title = UIConstants.strings.actionUninstall let icon = templatize(#imageLiteral(resourceName: "uninstall")) @@ -27,11 +27,10 @@ final class UninstallAction: ApplicationActionable { return } let alert: NSAlert = NSAlert() - let alertFormat = "Are you sure you want to uninstall %1$@ from %1$@?" - alert.messageText = String(format: NSLocalizedString(alertFormat, comment: ""), application.bundleDisplayName, application.device.name) + alert.messageText = String(format: UIConstants.strings.actionUninstallAlertMessage, application.bundleDisplayName, application.device.name) alert.alertStyle = .critical - alert.addButton(withTitle: NSLocalizedString("Uninstall", comment: "")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) + alert.addButton(withTitle: UIConstants.strings.actionUninstallAlertConfirmButton) + alert.addButton(withTitle: UIConstants.strings.actionUninstallAlertCancelButton) let response = alert.runModal() if response == NSApplication.ModalResponse.alertFirstButtonReturn { application.uninstall() diff --git a/OpenSim/pt-BR.lproj/Localizable.strings b/OpenSim/pt-BR.lproj/Localizable.strings new file mode 100644 index 0000000..d169da8 --- /dev/null +++ b/OpenSim/pt-BR.lproj/Localizable.strings @@ -0,0 +1,18 @@ +"Menu.Quit" = "Sair"; +"Menu.LaunchAtLogin" = "Iniciar junto com sistema"; +"Menu.Refresh" = "Atualizar"; +"Menu.Version" = "Versão"; +"Action.RevealInFinder" = "Abrir Sandbox no Finder"; +"Action.CopyPathPasteboard" = "Copiar caminho do Sandbox para o Pasteboard"; +"Action.OpenInTerminal" = "Abrir Sandbox no Terminal"; +"Action.Uninstall" = "Desinstalar"; +"Action.UninstallAlertConfirmButton" = "Desinstalar"; +"Action.UninstallAlertCancelButton" = "Cancelar"; +"Action.UninstallAlertMessage" = "Você tem certeza que quer desinstalar %@ do %@?"; +"Extension.OpenInIterm" = "Abrir Sandbox no iTerm"; +"Extension.OpenRealmDatabase" = "Abrir Realm Database"; +"AppInfo.Version" = "Versão"; +"AppInfo.Size" = "Tamanho"; +"MenuHeader.Actions" = "Ações"; +"MenuHeader.Extensions" = "Extensões"; +"MenuHeader.AppInformation" = "Informação do App";