├── .gitignore ├── LocalizationTooForReplaceKey1.png ├── LocalizationTooForReplaceKey3.png ├── LocalizationTooForReplaceKey4.png ├── LocalizationToolForReplaceKey.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ ├── IDEWorkspaceChecks.plist │ └── WorkspaceSettings.xcsettings ├── LocalizationToolForValue ├── Info.plist ├── LocalizationToolForValue.entitlements ├── Main │ ├── AppDelegate.swift │ ├── Base.lproj │ │ └── Main.storyboard │ ├── YQMainViewController.swift │ ├── YQMainWindowController.swift │ ├── YQQuestionViewController.swift │ └── YQQuestionViewController.xib ├── Model │ ├── EnumeratorFileProtocol.swift │ ├── JointManager.swift │ ├── KeyValueModel.swift │ ├── SplitManager.swift │ ├── YQFileModel.swift │ └── YQTableViewDelegate.swift ├── Resource │ └── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon_128x128.png │ │ ├── icon_128x128@2x.png │ │ ├── icon_16x16.png │ │ ├── icon_16x16@2x.png │ │ ├── icon_256x256.png │ │ ├── icon_256x256@2x.png │ │ ├── icon_32x32.png │ │ ├── icon_32x32@2x.png │ │ ├── icon_512x512.png │ │ └── icon_512x512@2x.png │ │ ├── Contents.json │ │ ├── clear.imageset │ │ ├── Contents.json │ │ ├── clear@2x.png │ │ └── clear@3x.png │ │ ├── file.imageset │ │ ├── Contents.json │ │ ├── file (3).png │ │ └── file (4).png │ │ ├── folder.imageset │ │ ├── Contents.json │ │ ├── folder@2x.png │ │ └── folder@3x.png │ │ ├── openFolder.imageset │ │ ├── Contents.json │ │ ├── openFolder@2x.png │ │ └── openFolder@3x.png │ │ ├── placerholder.imageset │ │ ├── Contents.json │ │ ├── file (1).png │ │ └── file (2).png │ │ ├── set.imageset │ │ ├── Contents.json │ │ ├── setting (1).png │ │ └── setting.png │ │ └── star.imageset │ │ ├── Contents.json │ │ ├── 开始 (1).png │ │ └── 开始.png └── View │ ├── YQDragDropView.swift │ ├── YQGradientView.swift │ ├── YQTableCellView.swift │ └── YQTableCellView.xib ├── LocalizationToolForValueTests ├── Info.plist └── LocalizationToolForValueTests.swift ├── LocalizationToolForValueUITests ├── Info.plist └── LocalizationToolForValueUITests.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/swift 3 | 4 | ### Swift ### 5 | # Xcode 6 | # 7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 8 | 9 | ## Build generated 10 | build/ 11 | DerivedData/ 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata/ 23 | 24 | ## Other 25 | *.moved-aside 26 | *.xccheckout 27 | *.xcscmblueprint 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | *.ipa 32 | *.dSYM.zip 33 | *.dSYM 34 | 35 | ## Playgrounds 36 | timeline.xctimeline 37 | playground.xcworkspace 38 | 39 | # Swift Package Manager 40 | # 41 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 42 | # Packages/ 43 | # Package.pins 44 | .build/ 45 | 46 | # CocoaPods - Refactored to standalone file 47 | 48 | # Carthage - Refactored to standalone file 49 | 50 | # fastlane 51 | # 52 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 53 | # screenshots whenever they are needed. 54 | # For more information about the recommended setup visit: 55 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 56 | 57 | fastlane/report.xml 58 | fastlane/Preview.html 59 | fastlane/screenshots 60 | fastlane/test_output 61 | 62 | 63 | # End of https://www.gitignore.io/api/swift 64 | -------------------------------------------------------------------------------- /LocalizationTooForReplaceKey1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationTooForReplaceKey1.png -------------------------------------------------------------------------------- /LocalizationTooForReplaceKey3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationTooForReplaceKey3.png -------------------------------------------------------------------------------- /LocalizationTooForReplaceKey4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationTooForReplaceKey4.png -------------------------------------------------------------------------------- /LocalizationToolForReplaceKey.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2F09ADC0202D28CF007FD91E /* EnumeratorFileProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F09ADBF202D28CF007FD91E /* EnumeratorFileProtocol.swift */; }; 11 | 2F09ADC3202D3EFD007FD91E /* KeyValueModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F09ADC2202D3EFD007FD91E /* KeyValueModel.swift */; }; 12 | 2F09ADC5202D424E007FD91E /* SplitManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F09ADC4202D424E007FD91E /* SplitManager.swift */; }; 13 | 2F09ADC7202D4D83007FD91E /* JointManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F09ADC6202D4D83007FD91E /* JointManager.swift */; }; 14 | 2F2981F220300E8800925E7F /* YQQuestionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F2981F020300E8800925E7F /* YQQuestionViewController.swift */; }; 15 | 2F2981F320300E8800925E7F /* YQQuestionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2F2981F120300E8800925E7F /* YQQuestionViewController.xib */; }; 16 | 2FC0C7B6202C339400D7CC80 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FC0C7B5202C339400D7CC80 /* AppDelegate.swift */; }; 17 | 2FC0C7B8202C339400D7CC80 /* YQMainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FC0C7B7202C339400D7CC80 /* YQMainViewController.swift */; }; 18 | 2FC0C7BA202C339400D7CC80 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2FC0C7B9202C339400D7CC80 /* Assets.xcassets */; }; 19 | 2FC0C7BD202C339400D7CC80 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2FC0C7BB202C339400D7CC80 /* Main.storyboard */; }; 20 | 2FC0C7C9202C339400D7CC80 /* LocalizationToolForValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FC0C7C8202C339400D7CC80 /* LocalizationToolForValueTests.swift */; }; 21 | 2FC0C7D4202C339400D7CC80 /* LocalizationToolForValueUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FC0C7D3202C339400D7CC80 /* LocalizationToolForValueUITests.swift */; }; 22 | 2FC0C7E4202C340C00D7CC80 /* YQMainWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FC0C7E3202C340C00D7CC80 /* YQMainWindowController.swift */; }; 23 | 2FC0C7E7202C347E00D7CC80 /* YQFileModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FC0C7E6202C347E00D7CC80 /* YQFileModel.swift */; }; 24 | 2FC0C7EA202C34A100D7CC80 /* YQTableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FC0C7E9202C34A100D7CC80 /* YQTableViewDelegate.swift */; }; 25 | 2FC0C7EC202C34AC00D7CC80 /* YQDragDropView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FC0C7EB202C34AC00D7CC80 /* YQDragDropView.swift */; }; 26 | 2FC0C7EE202C34B200D7CC80 /* YQGradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FC0C7ED202C34B200D7CC80 /* YQGradientView.swift */; }; 27 | 2FC0C7F1202C3B7000D7CC80 /* YQTableCellView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2FC0C7EF202C3B7000D7CC80 /* YQTableCellView.xib */; }; 28 | 2FC0C7F2202C3B7000D7CC80 /* YQTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FC0C7F0202C3B7000D7CC80 /* YQTableCellView.swift */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXContainerItemProxy section */ 32 | 2FC0C7C5202C339400D7CC80 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 2FC0C7AA202C339400D7CC80 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = 2FC0C7B1202C339400D7CC80; 37 | remoteInfo = LocalizationToolForValue; 38 | }; 39 | 2FC0C7D0202C339400D7CC80 /* PBXContainerItemProxy */ = { 40 | isa = PBXContainerItemProxy; 41 | containerPortal = 2FC0C7AA202C339400D7CC80 /* Project object */; 42 | proxyType = 1; 43 | remoteGlobalIDString = 2FC0C7B1202C339400D7CC80; 44 | remoteInfo = LocalizationToolForValue; 45 | }; 46 | /* End PBXContainerItemProxy section */ 47 | 48 | /* Begin PBXFileReference section */ 49 | 2F09ADBF202D28CF007FD91E /* EnumeratorFileProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumeratorFileProtocol.swift; sourceTree = ""; }; 50 | 2F09ADC2202D3EFD007FD91E /* KeyValueModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyValueModel.swift; sourceTree = ""; }; 51 | 2F09ADC4202D424E007FD91E /* SplitManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitManager.swift; sourceTree = ""; }; 52 | 2F09ADC6202D4D83007FD91E /* JointManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JointManager.swift; sourceTree = ""; }; 53 | 2F2981F020300E8800925E7F /* YQQuestionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YQQuestionViewController.swift; sourceTree = ""; }; 54 | 2F2981F120300E8800925E7F /* YQQuestionViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = YQQuestionViewController.xib; sourceTree = ""; }; 55 | 2FC0C7B2202C339400D7CC80 /* LocalizationToolForReplaceKey.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LocalizationToolForReplaceKey.app; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 2FC0C7B5202C339400D7CC80 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 57 | 2FC0C7B7202C339400D7CC80 /* YQMainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YQMainViewController.swift; sourceTree = ""; }; 58 | 2FC0C7B9202C339400D7CC80 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 59 | 2FC0C7BC202C339400D7CC80 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 60 | 2FC0C7BE202C339400D7CC80 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 61 | 2FC0C7BF202C339400D7CC80 /* LocalizationToolForValue.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LocalizationToolForValue.entitlements; sourceTree = ""; }; 62 | 2FC0C7C4202C339400D7CC80 /* LocalizationToolForReplaceKeyTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LocalizationToolForReplaceKeyTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 2FC0C7C8202C339400D7CC80 /* LocalizationToolForValueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationToolForValueTests.swift; sourceTree = ""; }; 64 | 2FC0C7CA202C339400D7CC80 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 65 | 2FC0C7CF202C339400D7CC80 /* LocalizationToolForReplaceKeyUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LocalizationToolForReplaceKeyUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 2FC0C7D3202C339400D7CC80 /* LocalizationToolForValueUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationToolForValueUITests.swift; sourceTree = ""; }; 67 | 2FC0C7D5202C339400D7CC80 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | 2FC0C7E3202C340C00D7CC80 /* YQMainWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YQMainWindowController.swift; sourceTree = ""; }; 69 | 2FC0C7E6202C347E00D7CC80 /* YQFileModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YQFileModel.swift; sourceTree = ""; }; 70 | 2FC0C7E9202C34A100D7CC80 /* YQTableViewDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YQTableViewDelegate.swift; sourceTree = ""; }; 71 | 2FC0C7EB202C34AC00D7CC80 /* YQDragDropView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YQDragDropView.swift; sourceTree = ""; }; 72 | 2FC0C7ED202C34B200D7CC80 /* YQGradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YQGradientView.swift; sourceTree = ""; }; 73 | 2FC0C7EF202C3B7000D7CC80 /* YQTableCellView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = YQTableCellView.xib; sourceTree = ""; }; 74 | 2FC0C7F0202C3B7000D7CC80 /* YQTableCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YQTableCellView.swift; sourceTree = ""; }; 75 | /* End PBXFileReference section */ 76 | 77 | /* Begin PBXFrameworksBuildPhase section */ 78 | 2FC0C7AF202C339400D7CC80 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | 2FC0C7C1202C339400D7CC80 /* Frameworks */ = { 86 | isa = PBXFrameworksBuildPhase; 87 | buildActionMask = 2147483647; 88 | files = ( 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | 2FC0C7CC202C339400D7CC80 /* Frameworks */ = { 93 | isa = PBXFrameworksBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXFrameworksBuildPhase section */ 100 | 101 | /* Begin PBXGroup section */ 102 | 2FC0C7A9202C339400D7CC80 = { 103 | isa = PBXGroup; 104 | children = ( 105 | 2FC0C7B4202C339400D7CC80 /* LocalizationToolForValue */, 106 | 2FC0C7C7202C339400D7CC80 /* LocalizationToolForValueTests */, 107 | 2FC0C7D2202C339400D7CC80 /* LocalizationToolForValueUITests */, 108 | 2FC0C7B3202C339400D7CC80 /* Products */, 109 | ); 110 | sourceTree = ""; 111 | }; 112 | 2FC0C7B3202C339400D7CC80 /* Products */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 2FC0C7B2202C339400D7CC80 /* LocalizationToolForReplaceKey.app */, 116 | 2FC0C7C4202C339400D7CC80 /* LocalizationToolForReplaceKeyTests.xctest */, 117 | 2FC0C7CF202C339400D7CC80 /* LocalizationToolForReplaceKeyUITests.xctest */, 118 | ); 119 | name = Products; 120 | sourceTree = ""; 121 | }; 122 | 2FC0C7B4202C339400D7CC80 /* LocalizationToolForValue */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 2FC0C7E8202C349100D7CC80 /* Model */, 126 | 2FC0C7E5202C345700D7CC80 /* View */, 127 | 2FC0C7E2202C33B500D7CC80 /* Main */, 128 | 2FC0C7E1202C33A100D7CC80 /* Resource */, 129 | 2FC0C7BE202C339400D7CC80 /* Info.plist */, 130 | 2FC0C7BF202C339400D7CC80 /* LocalizationToolForValue.entitlements */, 131 | ); 132 | path = LocalizationToolForValue; 133 | sourceTree = ""; 134 | }; 135 | 2FC0C7C7202C339400D7CC80 /* LocalizationToolForValueTests */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 2FC0C7C8202C339400D7CC80 /* LocalizationToolForValueTests.swift */, 139 | 2FC0C7CA202C339400D7CC80 /* Info.plist */, 140 | ); 141 | path = LocalizationToolForValueTests; 142 | sourceTree = ""; 143 | }; 144 | 2FC0C7D2202C339400D7CC80 /* LocalizationToolForValueUITests */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | 2FC0C7D3202C339400D7CC80 /* LocalizationToolForValueUITests.swift */, 148 | 2FC0C7D5202C339400D7CC80 /* Info.plist */, 149 | ); 150 | path = LocalizationToolForValueUITests; 151 | sourceTree = ""; 152 | }; 153 | 2FC0C7E1202C33A100D7CC80 /* Resource */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 2FC0C7B9202C339400D7CC80 /* Assets.xcassets */, 157 | ); 158 | path = Resource; 159 | sourceTree = ""; 160 | }; 161 | 2FC0C7E2202C33B500D7CC80 /* Main */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 2FC0C7B5202C339400D7CC80 /* AppDelegate.swift */, 165 | 2F2981F020300E8800925E7F /* YQQuestionViewController.swift */, 166 | 2F2981F120300E8800925E7F /* YQQuestionViewController.xib */, 167 | 2FC0C7B7202C339400D7CC80 /* YQMainViewController.swift */, 168 | 2FC0C7E3202C340C00D7CC80 /* YQMainWindowController.swift */, 169 | 2FC0C7BB202C339400D7CC80 /* Main.storyboard */, 170 | ); 171 | path = Main; 172 | sourceTree = ""; 173 | }; 174 | 2FC0C7E5202C345700D7CC80 /* View */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | 2FC0C7EB202C34AC00D7CC80 /* YQDragDropView.swift */, 178 | 2FC0C7ED202C34B200D7CC80 /* YQGradientView.swift */, 179 | 2FC0C7F0202C3B7000D7CC80 /* YQTableCellView.swift */, 180 | 2FC0C7EF202C3B7000D7CC80 /* YQTableCellView.xib */, 181 | ); 182 | path = View; 183 | sourceTree = ""; 184 | }; 185 | 2FC0C7E8202C349100D7CC80 /* Model */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | 2FC0C7E6202C347E00D7CC80 /* YQFileModel.swift */, 189 | 2FC0C7E9202C34A100D7CC80 /* YQTableViewDelegate.swift */, 190 | 2F09ADBF202D28CF007FD91E /* EnumeratorFileProtocol.swift */, 191 | 2F09ADC2202D3EFD007FD91E /* KeyValueModel.swift */, 192 | 2F09ADC4202D424E007FD91E /* SplitManager.swift */, 193 | 2F09ADC6202D4D83007FD91E /* JointManager.swift */, 194 | ); 195 | path = Model; 196 | sourceTree = ""; 197 | }; 198 | /* End PBXGroup section */ 199 | 200 | /* Begin PBXNativeTarget section */ 201 | 2FC0C7B1202C339400D7CC80 /* LocalizationToolForReplaceKey */ = { 202 | isa = PBXNativeTarget; 203 | buildConfigurationList = 2FC0C7D8202C339400D7CC80 /* Build configuration list for PBXNativeTarget "LocalizationToolForReplaceKey" */; 204 | buildPhases = ( 205 | 2FC0C7AE202C339400D7CC80 /* Sources */, 206 | 2FC0C7AF202C339400D7CC80 /* Frameworks */, 207 | 2FC0C7B0202C339400D7CC80 /* Resources */, 208 | ); 209 | buildRules = ( 210 | ); 211 | dependencies = ( 212 | ); 213 | name = LocalizationToolForReplaceKey; 214 | productName = LocalizationToolForValue; 215 | productReference = 2FC0C7B2202C339400D7CC80 /* LocalizationToolForReplaceKey.app */; 216 | productType = "com.apple.product-type.application"; 217 | }; 218 | 2FC0C7C3202C339400D7CC80 /* LocalizationToolForReplaceKeyTests */ = { 219 | isa = PBXNativeTarget; 220 | buildConfigurationList = 2FC0C7DB202C339400D7CC80 /* Build configuration list for PBXNativeTarget "LocalizationToolForReplaceKeyTests" */; 221 | buildPhases = ( 222 | 2FC0C7C0202C339400D7CC80 /* Sources */, 223 | 2FC0C7C1202C339400D7CC80 /* Frameworks */, 224 | 2FC0C7C2202C339400D7CC80 /* Resources */, 225 | ); 226 | buildRules = ( 227 | ); 228 | dependencies = ( 229 | 2FC0C7C6202C339400D7CC80 /* PBXTargetDependency */, 230 | ); 231 | name = LocalizationToolForReplaceKeyTests; 232 | productName = LocalizationToolForValueTests; 233 | productReference = 2FC0C7C4202C339400D7CC80 /* LocalizationToolForReplaceKeyTests.xctest */; 234 | productType = "com.apple.product-type.bundle.unit-test"; 235 | }; 236 | 2FC0C7CE202C339400D7CC80 /* LocalizationToolForReplaceKeyUITests */ = { 237 | isa = PBXNativeTarget; 238 | buildConfigurationList = 2FC0C7DE202C339400D7CC80 /* Build configuration list for PBXNativeTarget "LocalizationToolForReplaceKeyUITests" */; 239 | buildPhases = ( 240 | 2FC0C7CB202C339400D7CC80 /* Sources */, 241 | 2FC0C7CC202C339400D7CC80 /* Frameworks */, 242 | 2FC0C7CD202C339400D7CC80 /* Resources */, 243 | ); 244 | buildRules = ( 245 | ); 246 | dependencies = ( 247 | 2FC0C7D1202C339400D7CC80 /* PBXTargetDependency */, 248 | ); 249 | name = LocalizationToolForReplaceKeyUITests; 250 | productName = LocalizationToolForValueUITests; 251 | productReference = 2FC0C7CF202C339400D7CC80 /* LocalizationToolForReplaceKeyUITests.xctest */; 252 | productType = "com.apple.product-type.bundle.ui-testing"; 253 | }; 254 | /* End PBXNativeTarget section */ 255 | 256 | /* Begin PBXProject section */ 257 | 2FC0C7AA202C339400D7CC80 /* Project object */ = { 258 | isa = PBXProject; 259 | attributes = { 260 | CLASSPREFIX = YQ; 261 | LastSwiftUpdateCheck = 0920; 262 | LastUpgradeCheck = 0920; 263 | ORGANIZATIONNAME = sungrow; 264 | TargetAttributes = { 265 | 2FC0C7B1202C339400D7CC80 = { 266 | CreatedOnToolsVersion = 9.2; 267 | ProvisioningStyle = Automatic; 268 | SystemCapabilities = { 269 | com.apple.Sandbox = { 270 | enabled = 0; 271 | }; 272 | }; 273 | }; 274 | 2FC0C7C3202C339400D7CC80 = { 275 | CreatedOnToolsVersion = 9.2; 276 | ProvisioningStyle = Automatic; 277 | TestTargetID = 2FC0C7B1202C339400D7CC80; 278 | }; 279 | 2FC0C7CE202C339400D7CC80 = { 280 | CreatedOnToolsVersion = 9.2; 281 | ProvisioningStyle = Automatic; 282 | TestTargetID = 2FC0C7B1202C339400D7CC80; 283 | }; 284 | }; 285 | }; 286 | buildConfigurationList = 2FC0C7AD202C339400D7CC80 /* Build configuration list for PBXProject "LocalizationToolForReplaceKey" */; 287 | compatibilityVersion = "Xcode 8.0"; 288 | developmentRegion = en; 289 | hasScannedForEncodings = 0; 290 | knownRegions = ( 291 | en, 292 | Base, 293 | ); 294 | mainGroup = 2FC0C7A9202C339400D7CC80; 295 | productRefGroup = 2FC0C7B3202C339400D7CC80 /* Products */; 296 | projectDirPath = ""; 297 | projectRoot = ""; 298 | targets = ( 299 | 2FC0C7B1202C339400D7CC80 /* LocalizationToolForReplaceKey */, 300 | 2FC0C7C3202C339400D7CC80 /* LocalizationToolForReplaceKeyTests */, 301 | 2FC0C7CE202C339400D7CC80 /* LocalizationToolForReplaceKeyUITests */, 302 | ); 303 | }; 304 | /* End PBXProject section */ 305 | 306 | /* Begin PBXResourcesBuildPhase section */ 307 | 2FC0C7B0202C339400D7CC80 /* Resources */ = { 308 | isa = PBXResourcesBuildPhase; 309 | buildActionMask = 2147483647; 310 | files = ( 311 | 2FC0C7BA202C339400D7CC80 /* Assets.xcassets in Resources */, 312 | 2FC0C7BD202C339400D7CC80 /* Main.storyboard in Resources */, 313 | 2F2981F320300E8800925E7F /* YQQuestionViewController.xib in Resources */, 314 | 2FC0C7F1202C3B7000D7CC80 /* YQTableCellView.xib in Resources */, 315 | ); 316 | runOnlyForDeploymentPostprocessing = 0; 317 | }; 318 | 2FC0C7C2202C339400D7CC80 /* Resources */ = { 319 | isa = PBXResourcesBuildPhase; 320 | buildActionMask = 2147483647; 321 | files = ( 322 | ); 323 | runOnlyForDeploymentPostprocessing = 0; 324 | }; 325 | 2FC0C7CD202C339400D7CC80 /* Resources */ = { 326 | isa = PBXResourcesBuildPhase; 327 | buildActionMask = 2147483647; 328 | files = ( 329 | ); 330 | runOnlyForDeploymentPostprocessing = 0; 331 | }; 332 | /* End PBXResourcesBuildPhase section */ 333 | 334 | /* Begin PBXSourcesBuildPhase section */ 335 | 2FC0C7AE202C339400D7CC80 /* Sources */ = { 336 | isa = PBXSourcesBuildPhase; 337 | buildActionMask = 2147483647; 338 | files = ( 339 | 2F09ADC0202D28CF007FD91E /* EnumeratorFileProtocol.swift in Sources */, 340 | 2FC0C7B8202C339400D7CC80 /* YQMainViewController.swift in Sources */, 341 | 2FC0C7E4202C340C00D7CC80 /* YQMainWindowController.swift in Sources */, 342 | 2F09ADC3202D3EFD007FD91E /* KeyValueModel.swift in Sources */, 343 | 2FC0C7B6202C339400D7CC80 /* AppDelegate.swift in Sources */, 344 | 2F09ADC5202D424E007FD91E /* SplitManager.swift in Sources */, 345 | 2F2981F220300E8800925E7F /* YQQuestionViewController.swift in Sources */, 346 | 2FC0C7EA202C34A100D7CC80 /* YQTableViewDelegate.swift in Sources */, 347 | 2FC0C7E7202C347E00D7CC80 /* YQFileModel.swift in Sources */, 348 | 2F09ADC7202D4D83007FD91E /* JointManager.swift in Sources */, 349 | 2FC0C7F2202C3B7000D7CC80 /* YQTableCellView.swift in Sources */, 350 | 2FC0C7EC202C34AC00D7CC80 /* YQDragDropView.swift in Sources */, 351 | 2FC0C7EE202C34B200D7CC80 /* YQGradientView.swift in Sources */, 352 | ); 353 | runOnlyForDeploymentPostprocessing = 0; 354 | }; 355 | 2FC0C7C0202C339400D7CC80 /* Sources */ = { 356 | isa = PBXSourcesBuildPhase; 357 | buildActionMask = 2147483647; 358 | files = ( 359 | 2FC0C7C9202C339400D7CC80 /* LocalizationToolForValueTests.swift in Sources */, 360 | ); 361 | runOnlyForDeploymentPostprocessing = 0; 362 | }; 363 | 2FC0C7CB202C339400D7CC80 /* Sources */ = { 364 | isa = PBXSourcesBuildPhase; 365 | buildActionMask = 2147483647; 366 | files = ( 367 | 2FC0C7D4202C339400D7CC80 /* LocalizationToolForValueUITests.swift in Sources */, 368 | ); 369 | runOnlyForDeploymentPostprocessing = 0; 370 | }; 371 | /* End PBXSourcesBuildPhase section */ 372 | 373 | /* Begin PBXTargetDependency section */ 374 | 2FC0C7C6202C339400D7CC80 /* PBXTargetDependency */ = { 375 | isa = PBXTargetDependency; 376 | target = 2FC0C7B1202C339400D7CC80 /* LocalizationToolForReplaceKey */; 377 | targetProxy = 2FC0C7C5202C339400D7CC80 /* PBXContainerItemProxy */; 378 | }; 379 | 2FC0C7D1202C339400D7CC80 /* PBXTargetDependency */ = { 380 | isa = PBXTargetDependency; 381 | target = 2FC0C7B1202C339400D7CC80 /* LocalizationToolForReplaceKey */; 382 | targetProxy = 2FC0C7D0202C339400D7CC80 /* PBXContainerItemProxy */; 383 | }; 384 | /* End PBXTargetDependency section */ 385 | 386 | /* Begin PBXVariantGroup section */ 387 | 2FC0C7BB202C339400D7CC80 /* Main.storyboard */ = { 388 | isa = PBXVariantGroup; 389 | children = ( 390 | 2FC0C7BC202C339400D7CC80 /* Base */, 391 | ); 392 | name = Main.storyboard; 393 | sourceTree = ""; 394 | }; 395 | /* End PBXVariantGroup section */ 396 | 397 | /* Begin XCBuildConfiguration section */ 398 | 2FC0C7D6202C339400D7CC80 /* Debug */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | ALWAYS_SEARCH_USER_PATHS = NO; 402 | CLANG_ANALYZER_NONNULL = YES; 403 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 404 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 405 | CLANG_CXX_LIBRARY = "libc++"; 406 | CLANG_ENABLE_MODULES = YES; 407 | CLANG_ENABLE_OBJC_ARC = YES; 408 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 409 | CLANG_WARN_BOOL_CONVERSION = YES; 410 | CLANG_WARN_COMMA = YES; 411 | CLANG_WARN_CONSTANT_CONVERSION = YES; 412 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 413 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 414 | CLANG_WARN_EMPTY_BODY = YES; 415 | CLANG_WARN_ENUM_CONVERSION = YES; 416 | CLANG_WARN_INFINITE_RECURSION = YES; 417 | CLANG_WARN_INT_CONVERSION = YES; 418 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 419 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 420 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 421 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 422 | CLANG_WARN_STRICT_PROTOTYPES = YES; 423 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 424 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 425 | CLANG_WARN_UNREACHABLE_CODE = YES; 426 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 427 | CODE_SIGN_IDENTITY = "-"; 428 | COPY_PHASE_STRIP = NO; 429 | DEBUG_INFORMATION_FORMAT = dwarf; 430 | ENABLE_STRICT_OBJC_MSGSEND = YES; 431 | ENABLE_TESTABILITY = YES; 432 | GCC_C_LANGUAGE_STANDARD = gnu11; 433 | GCC_DYNAMIC_NO_PIC = NO; 434 | GCC_NO_COMMON_BLOCKS = YES; 435 | GCC_OPTIMIZATION_LEVEL = 0; 436 | GCC_PREPROCESSOR_DEFINITIONS = ( 437 | "DEBUG=1", 438 | "$(inherited)", 439 | ); 440 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 441 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 442 | GCC_WARN_UNDECLARED_SELECTOR = YES; 443 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 444 | GCC_WARN_UNUSED_FUNCTION = YES; 445 | GCC_WARN_UNUSED_VARIABLE = YES; 446 | MACOSX_DEPLOYMENT_TARGET = 10.12; 447 | MTL_ENABLE_DEBUG_INFO = YES; 448 | ONLY_ACTIVE_ARCH = YES; 449 | SDKROOT = macosx; 450 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 451 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 452 | }; 453 | name = Debug; 454 | }; 455 | 2FC0C7D7202C339400D7CC80 /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | ALWAYS_SEARCH_USER_PATHS = NO; 459 | CLANG_ANALYZER_NONNULL = YES; 460 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 461 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 462 | CLANG_CXX_LIBRARY = "libc++"; 463 | CLANG_ENABLE_MODULES = YES; 464 | CLANG_ENABLE_OBJC_ARC = YES; 465 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 466 | CLANG_WARN_BOOL_CONVERSION = YES; 467 | CLANG_WARN_COMMA = YES; 468 | CLANG_WARN_CONSTANT_CONVERSION = YES; 469 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 470 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 471 | CLANG_WARN_EMPTY_BODY = YES; 472 | CLANG_WARN_ENUM_CONVERSION = YES; 473 | CLANG_WARN_INFINITE_RECURSION = YES; 474 | CLANG_WARN_INT_CONVERSION = YES; 475 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 476 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 477 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 478 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 479 | CLANG_WARN_STRICT_PROTOTYPES = YES; 480 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 481 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 482 | CLANG_WARN_UNREACHABLE_CODE = YES; 483 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 484 | CODE_SIGN_IDENTITY = "-"; 485 | COPY_PHASE_STRIP = NO; 486 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 487 | ENABLE_NS_ASSERTIONS = NO; 488 | ENABLE_STRICT_OBJC_MSGSEND = YES; 489 | GCC_C_LANGUAGE_STANDARD = gnu11; 490 | GCC_NO_COMMON_BLOCKS = YES; 491 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 492 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 493 | GCC_WARN_UNDECLARED_SELECTOR = YES; 494 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 495 | GCC_WARN_UNUSED_FUNCTION = YES; 496 | GCC_WARN_UNUSED_VARIABLE = YES; 497 | MACOSX_DEPLOYMENT_TARGET = 10.12; 498 | MTL_ENABLE_DEBUG_INFO = NO; 499 | SDKROOT = macosx; 500 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 501 | }; 502 | name = Release; 503 | }; 504 | 2FC0C7D9202C339400D7CC80 /* Debug */ = { 505 | isa = XCBuildConfiguration; 506 | buildSettings = { 507 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 508 | CODE_SIGN_STYLE = Automatic; 509 | COMBINE_HIDPI_IMAGES = YES; 510 | INFOPLIST_FILE = LocalizationToolForValue/Info.plist; 511 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 512 | PRODUCT_BUNDLE_IDENTIFIER = com.sungrow.LocalizationToolForValue; 513 | PRODUCT_NAME = "$(TARGET_NAME)"; 514 | SWIFT_VERSION = 5.0; 515 | }; 516 | name = Debug; 517 | }; 518 | 2FC0C7DA202C339400D7CC80 /* Release */ = { 519 | isa = XCBuildConfiguration; 520 | buildSettings = { 521 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 522 | CODE_SIGN_STYLE = Automatic; 523 | COMBINE_HIDPI_IMAGES = YES; 524 | INFOPLIST_FILE = LocalizationToolForValue/Info.plist; 525 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 526 | PRODUCT_BUNDLE_IDENTIFIER = com.sungrow.LocalizationToolForValue; 527 | PRODUCT_NAME = "$(TARGET_NAME)"; 528 | SWIFT_VERSION = 5.0; 529 | }; 530 | name = Release; 531 | }; 532 | 2FC0C7DC202C339400D7CC80 /* Debug */ = { 533 | isa = XCBuildConfiguration; 534 | buildSettings = { 535 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 536 | BUNDLE_LOADER = "$(TEST_HOST)"; 537 | CODE_SIGN_STYLE = Automatic; 538 | COMBINE_HIDPI_IMAGES = YES; 539 | INFOPLIST_FILE = LocalizationToolForValueTests/Info.plist; 540 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 541 | PRODUCT_BUNDLE_IDENTIFIER = com.sungrow.LocalizationToolForValueTests; 542 | PRODUCT_NAME = "$(TARGET_NAME)"; 543 | SWIFT_VERSION = 4.0; 544 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LocalizationToolForReplaceKey.app/Contents/MacOS/LocalizationToolForReplaceKey"; 545 | }; 546 | name = Debug; 547 | }; 548 | 2FC0C7DD202C339400D7CC80 /* Release */ = { 549 | isa = XCBuildConfiguration; 550 | buildSettings = { 551 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 552 | BUNDLE_LOADER = "$(TEST_HOST)"; 553 | CODE_SIGN_STYLE = Automatic; 554 | COMBINE_HIDPI_IMAGES = YES; 555 | INFOPLIST_FILE = LocalizationToolForValueTests/Info.plist; 556 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 557 | PRODUCT_BUNDLE_IDENTIFIER = com.sungrow.LocalizationToolForValueTests; 558 | PRODUCT_NAME = "$(TARGET_NAME)"; 559 | SWIFT_VERSION = 4.0; 560 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/LocalizationToolForReplaceKey.app/Contents/MacOS/LocalizationToolForReplaceKey"; 561 | }; 562 | name = Release; 563 | }; 564 | 2FC0C7DF202C339400D7CC80 /* Debug */ = { 565 | isa = XCBuildConfiguration; 566 | buildSettings = { 567 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 568 | CODE_SIGN_STYLE = Automatic; 569 | COMBINE_HIDPI_IMAGES = YES; 570 | INFOPLIST_FILE = LocalizationToolForValueUITests/Info.plist; 571 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 572 | PRODUCT_BUNDLE_IDENTIFIER = com.sungrow.LocalizationToolForValueUITests; 573 | PRODUCT_NAME = "$(TARGET_NAME)"; 574 | SWIFT_VERSION = 4.0; 575 | TEST_TARGET_NAME = LocalizationToolForValue; 576 | }; 577 | name = Debug; 578 | }; 579 | 2FC0C7E0202C339400D7CC80 /* Release */ = { 580 | isa = XCBuildConfiguration; 581 | buildSettings = { 582 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 583 | CODE_SIGN_STYLE = Automatic; 584 | COMBINE_HIDPI_IMAGES = YES; 585 | INFOPLIST_FILE = LocalizationToolForValueUITests/Info.plist; 586 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 587 | PRODUCT_BUNDLE_IDENTIFIER = com.sungrow.LocalizationToolForValueUITests; 588 | PRODUCT_NAME = "$(TARGET_NAME)"; 589 | SWIFT_VERSION = 4.0; 590 | TEST_TARGET_NAME = LocalizationToolForValue; 591 | }; 592 | name = Release; 593 | }; 594 | /* End XCBuildConfiguration section */ 595 | 596 | /* Begin XCConfigurationList section */ 597 | 2FC0C7AD202C339400D7CC80 /* Build configuration list for PBXProject "LocalizationToolForReplaceKey" */ = { 598 | isa = XCConfigurationList; 599 | buildConfigurations = ( 600 | 2FC0C7D6202C339400D7CC80 /* Debug */, 601 | 2FC0C7D7202C339400D7CC80 /* Release */, 602 | ); 603 | defaultConfigurationIsVisible = 0; 604 | defaultConfigurationName = Release; 605 | }; 606 | 2FC0C7D8202C339400D7CC80 /* Build configuration list for PBXNativeTarget "LocalizationToolForReplaceKey" */ = { 607 | isa = XCConfigurationList; 608 | buildConfigurations = ( 609 | 2FC0C7D9202C339400D7CC80 /* Debug */, 610 | 2FC0C7DA202C339400D7CC80 /* Release */, 611 | ); 612 | defaultConfigurationIsVisible = 0; 613 | defaultConfigurationName = Release; 614 | }; 615 | 2FC0C7DB202C339400D7CC80 /* Build configuration list for PBXNativeTarget "LocalizationToolForReplaceKeyTests" */ = { 616 | isa = XCConfigurationList; 617 | buildConfigurations = ( 618 | 2FC0C7DC202C339400D7CC80 /* Debug */, 619 | 2FC0C7DD202C339400D7CC80 /* Release */, 620 | ); 621 | defaultConfigurationIsVisible = 0; 622 | defaultConfigurationName = Release; 623 | }; 624 | 2FC0C7DE202C339400D7CC80 /* Build configuration list for PBXNativeTarget "LocalizationToolForReplaceKeyUITests" */ = { 625 | isa = XCConfigurationList; 626 | buildConfigurations = ( 627 | 2FC0C7DF202C339400D7CC80 /* Debug */, 628 | 2FC0C7E0202C339400D7CC80 /* Release */, 629 | ); 630 | defaultConfigurationIsVisible = 0; 631 | defaultConfigurationName = Release; 632 | }; 633 | /* End XCConfigurationList section */ 634 | }; 635 | rootObject = 2FC0C7AA202C339400D7CC80 /* Project object */; 636 | } 637 | -------------------------------------------------------------------------------- /LocalizationToolForReplaceKey.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LocalizationToolForReplaceKey.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LocalizationToolForReplaceKey.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /LocalizationToolForValue/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2018年 sungrow. All rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /LocalizationToolForValue/LocalizationToolForValue.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LocalizationToolForValue/Main/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // LocalizationToolForValue 4 | // 5 | // Created by sungrow on 2018/2/8. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | 15 | 16 | func applicationDidFinishLaunching(_ aNotification: Notification) { 17 | // Insert code here to initialize your application 18 | } 19 | 20 | func applicationWillTerminate(_ aNotification: Notification) { 21 | // Insert code here to tear down your application 22 | } 23 | 24 | func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 25 | return true 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /LocalizationToolForValue/Main/YQMainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YQMainViewController 3 | // LocalizationToolForValue 4 | // 5 | // Created by sungrow on 2018/2/8. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | enum ToolFunc: Int { 12 | /// 输出 I18N 开头的key 13 | case inputI18N = 0 14 | /// 输出 非I18N 开头的key 15 | case inputNOI18N 16 | /// 输出 key相同 - value相同的key 17 | case inputKeySameValueSame 18 | /// 输出 key不同 - value相同的key 19 | case inputKeySameValueDifferent 20 | /// 输出 key相同同 - value不同的key 21 | case inputKeyDifferentValueSame 22 | /// 输出目标文件中存在, 源文件不存在的key 23 | case inputTargetExistOriginalNoExist 24 | /// 使用目标文件的key 替换源文件的key; 使用 源文件key 和 目标文件的value 匹配 25 | case replaceKeyUseTargetKey 26 | } 27 | 28 | class YQMainViewController: NSViewController { 29 | 30 | @IBOutlet weak var sourceTableView: NSTableView! 31 | @IBOutlet weak var targetTableView: NSTableView! 32 | 33 | /// 局部常量 34 | private let settingViewHeight: CGFloat = 300 35 | 36 | /// 控件属性 37 | @IBOutlet weak var settingView: NSView! 38 | @IBOutlet weak var sourceDragDropView: YQDragDropView! 39 | @IBOutlet weak var targetDragDropView: YQDragDropView! 40 | @IBOutlet weak var bgGradientView: YQGradientView! 41 | 42 | @IBOutlet weak var starExecuteBTN: NSButton! 43 | @IBOutlet weak var executeIndicator: NSProgressIndicator! 44 | @IBOutlet weak var sourceMessageLBL: NSTextField! 45 | @IBOutlet weak var targetMessageLBL: NSTextField! 46 | @IBOutlet weak var bottomConstraint: NSLayoutConstraint! 47 | @IBOutlet weak var outFolderNameTF: NSTextField! 48 | @IBOutlet weak var outPathTF: NSTextField! 49 | @IBOutlet weak var selectPathBTN: NSButton! 50 | 51 | private lazy var sourceDataList: [YQFileModel] = [YQFileModel]() 52 | private lazy var targetDataList: [YQFileModel] = [YQFileModel]() 53 | 54 | @IBOutlet weak var inputI18NRadio: NSButton! 55 | private lazy var toolFucn: ToolFunc = .inputI18N 56 | private lazy var selectRadio: NSButton = inputI18NRadio 57 | 58 | fileprivate lazy var sourceDelegate: YQTableViewDelegate = { 59 | let delegate = YQTableViewDelegate() 60 | delegate.tableview = sourceTableView 61 | delegate.fileModels = sourceDataList 62 | return delegate 63 | }() 64 | 65 | fileprivate lazy var popOver: NSPopover = { 66 | let popView = NSPopover() 67 | popView.contentViewController = YQQuestionViewController() 68 | popView.behavior = .transient 69 | popView.appearance = NSAppearance(named: NSAppearance.Name.vibrantLight) 70 | return popView 71 | }() 72 | 73 | private lazy var targetDelegate: YQTableViewDelegate = { 74 | let delegate = YQTableViewDelegate() 75 | delegate.tableview = targetTableView 76 | delegate.fileModels = targetDataList 77 | return delegate 78 | }() 79 | 80 | 81 | override func viewDidLoad() { 82 | super.viewDidLoad() 83 | 84 | bottomConstraint.constant = 0 85 | 86 | sourceDragDropView.delegate = self 87 | targetDragDropView.delegate = self 88 | 89 | sourceTableView.delegate = sourceDelegate 90 | sourceTableView.dataSource = sourceDelegate 91 | 92 | targetTableView.delegate = targetDelegate 93 | targetTableView.dataSource = targetDelegate 94 | 95 | configOutpath() 96 | outFolderNameTF.delegate = self 97 | } 98 | 99 | override var representedObject: Any? { 100 | didSet { 101 | // Update the view, if already loaded. 102 | } 103 | } 104 | 105 | @IBAction func radioAction(_ sender: NSButton) { 106 | if selectRadio == sender { 107 | return 108 | } 109 | selectRadio.state = .off 110 | sender.state = sender.state == .off ? .off : .on 111 | selectRadio = sender 112 | toolFucn = ToolFunc(rawValue: sender.tag - 2000)! 113 | } 114 | 115 | @IBAction func startAction(_ sender: NSButton) { 116 | switch toolFucn { 117 | case .inputI18N: 118 | inputI18NAction() 119 | break 120 | case .inputNOI18N: 121 | inputNOI18NAction() 122 | break 123 | case .inputKeySameValueSame: 124 | inputKeySameValueSameAction() 125 | break 126 | case .inputKeySameValueDifferent: 127 | inputKeySameValueDifferent() 128 | break 129 | case .inputKeyDifferentValueSame: 130 | inputKeyDifferentValueSame() 131 | break 132 | case .inputTargetExistOriginalNoExist: 133 | inputTargetExistOriginalNoExistAction() 134 | break 135 | case .replaceKeyUseTargetKey: 136 | replaceKeyUseTargetKeyAction() 137 | break 138 | } 139 | } 140 | 141 | @IBAction func selectPathAction(_ sender: NSButton) { 142 | let path = openPanel(canChooseFile: false) 143 | JointManager.shared.homePath = path 144 | configOutpath() 145 | } 146 | 147 | @IBAction func questionAction(_ sender: NSButton) { 148 | popOver.show(relativeTo: sender.bounds, of: sender, preferredEdge: NSRectEdge.maxY) 149 | } 150 | 151 | @IBAction func openOutFolderAction(_ sender: NSButton) { 152 | NSWorkspace.shared.openFile(JointManager.shared.outPath) 153 | } 154 | 155 | @IBAction func restoreDefaultAction(_ sender: NSButton) { 156 | JointManager.shared.restoreDefaultPath() 157 | configOutpath() 158 | } 159 | 160 | @IBAction func settingAction(_ sender: NSButton) { 161 | if bottomConstraint.constant > 0 { 162 | bottomConstraint.constant = 0 163 | } else { 164 | bottomConstraint.constant = settingViewHeight 165 | } 166 | NSAnimationContext.runAnimationGroup({ (context) in 167 | context.allowsImplicitAnimation = true 168 | context.duration = 0.25 169 | }, completionHandler: nil) 170 | } 171 | 172 | @IBAction func clearSourceDataList(_ sender: NSButton) { 173 | sourceDataList.removeAll() 174 | reloadSourceTableVC() 175 | } 176 | 177 | @IBAction func clearTargetDataList(_ sender: NSButton) { 178 | targetDataList.removeAll() 179 | reloadTargetTableVC() 180 | } 181 | 182 | } 183 | 184 | // MARK: - private Action 185 | extension YQMainViewController { 186 | /// 开始执行 187 | private func starExecute() { 188 | DispatchQueue.main.async { 189 | self.starExecuteBTN.isHidden = true 190 | self.executeIndicator.isHidden = false 191 | self.executeIndicator.startAnimation(nil) 192 | } 193 | showMessage("开始处理", label: self.targetMessageLBL) 194 | showMessage("开始处理", label: self.sourceMessageLBL) 195 | } 196 | 197 | /// 结束执行 198 | private func endExecute() { 199 | DispatchQueue.main.async { 200 | self.starExecuteBTN.isHidden = false 201 | self.executeIndicator.isHidden = true 202 | self.executeIndicator.stopAnimation(nil) 203 | } 204 | showMessage("处理完成", label: self.targetMessageLBL) 205 | showMessage("处理完成", label: self.sourceMessageLBL) 206 | } 207 | 208 | /// 显示信息 209 | /// 210 | /// - Parameters: 211 | /// - message: 信息 212 | /// - label: 标签 213 | private func showMessage(_ message: String, label: NSTextField) { 214 | DispatchQueue.main.async { 215 | label.stringValue = message 216 | } 217 | } 218 | 219 | @discardableResult 220 | private func allKeyValueModels(_ fileModels: [YQFileModel], forEach: ((KeyValueModel) -> Bool)? = nil) -> [KeyValueModel] { 221 | var keyValueModels = [KeyValueModel]() 222 | fileModels.forEach { (fileModel) in 223 | fileModel.enumeratorFile({ (filePath) in 224 | let models = SplitManager.shared.split(YQFileModel(filePath: filePath), forEach: forEach) 225 | keyValueModels = keyValueModels + models 226 | }) 227 | } 228 | return keyValueModels 229 | } 230 | 231 | private func reloadSourceTableVC() { 232 | sourceDragDropView.isHidden = sourceDataList.count > 0 233 | sourceTableView.isHidden = !sourceDragDropView.isHidden 234 | sourceDelegate.fileModels = sourceDataList 235 | } 236 | 237 | private func reloadTargetTableVC() { 238 | targetDragDropView.isHidden = targetDataList.count > 0 239 | targetTableView.isHidden = !targetDragDropView.isHidden 240 | targetDelegate.fileModels = targetDataList 241 | } 242 | 243 | private func configOutpath() { 244 | outFolderNameTF.stringValue = JointManager.shared.outFolderName 245 | outPathTF.placeholderString = JointManager.shared.outPath 246 | } 247 | 248 | /// 从Finder中选择文件/文件夹 249 | /// 250 | /// - Parameter canChooseFile: 是否是文件 251 | /// - Returns: 文件/文件夹路径 252 | fileprivate func openPanel(canChooseFile: Bool) -> String { 253 | let openPanel = NSOpenPanel() 254 | openPanel.allowsMultipleSelection = false 255 | openPanel.canChooseDirectories = !canChooseFile 256 | openPanel.canChooseFiles = canChooseFile 257 | openPanel.canCreateDirectories = true 258 | openPanel.title = "选择输出路径" 259 | openPanel.message = "转换后的资源文件将会保存到该目录下" 260 | if openPanel.runModal() == .OK { 261 | let path = openPanel.urls.first?.absoluteString.components(separatedBy: ":").last?.removingPercentEncoding as NSString? 262 | return path?.expandingTildeInPath ?? "" 263 | } 264 | return "" 265 | } 266 | } 267 | 268 | // MARK: - ToolFunc 269 | extension YQMainViewController { 270 | /// 输出 I18N 开头的词条 271 | fileprivate func inputI18NAction() { 272 | starExecute() 273 | DispatchQueue.global().async { 274 | let sourceKeyValueModels = self.allKeyValueModels(self.sourceDataList) 275 | var content = "" 276 | sourceKeyValueModels.forEach({ (sourceKeyValueModel) in 277 | self.showMessage("正在处理:" + sourceKeyValueModel.key, label: self.sourceMessageLBL) 278 | if sourceKeyValueModel.key.lowercased().hasPrefix("i18n") { 279 | content += "\"\(sourceKeyValueModel.key)\" = \"\(sourceKeyValueModel.value)\";\n" 280 | } 281 | }) 282 | JointManager.shared.Joint("\(self.toolFucn)", content: content) 283 | JointManager.shared.JointForWeb(sourceKeyValueModels, fileName: "\(self.toolFucn)") 284 | self.endExecute() 285 | } 286 | } 287 | 288 | /// 输出 非I18N 开头的词条 289 | fileprivate func inputNOI18NAction() { 290 | starExecute() 291 | DispatchQueue.global().async { 292 | let sourceKeyValueModels = self.allKeyValueModels(self.sourceDataList) 293 | var content = "" 294 | sourceKeyValueModels.forEach({ (sourceKeyValueModel) in 295 | self.showMessage("正在处理:" + sourceKeyValueModel.key, label: self.sourceMessageLBL) 296 | if !sourceKeyValueModel.key.lowercased().hasPrefix("i18n") { 297 | content += "\"\(sourceKeyValueModel.key)\" = \"\(sourceKeyValueModel.value)\";\n" 298 | } 299 | }) 300 | JointManager.shared.Joint("\(self.toolFucn)", content: content) 301 | JointManager.shared.JointForWeb(sourceKeyValueModels, fileName: "\(self.toolFucn)") 302 | self.endExecute() 303 | } 304 | } 305 | 306 | /// 输出 key 相同 value 相同的 词条 307 | fileprivate func inputKeySameValueSameAction() { 308 | starExecute() 309 | let sourceValueModels = allKeyValueModels(sourceDataList) 310 | DispatchQueue.global().async { 311 | var repeatKeys: [String] = [] 312 | var content = "" 313 | var resultModels: [KeyValueModel] = [] 314 | sourceValueModels.forEach { (targetModel) in 315 | sourceValueModels.forEach({ (sourceModel) in 316 | if targetModel.key == sourceModel.key && 317 | targetModel.value == sourceModel.value && 318 | targetModel.filePath! != sourceModel.filePath! && 319 | !repeatKeys.contains(sourceModel.key) && 320 | !repeatKeys.contains(targetModel.key) { 321 | repeatKeys.append(sourceModel.key) 322 | repeatKeys.append(targetModel.key) 323 | content += "\"\(targetModel.key)\" = \"\(sourceModel.value)\";\n" 324 | resultModels.append(targetModel) 325 | } 326 | }) 327 | } 328 | JointManager.shared.Joint("\(self.toolFucn)", content: content) 329 | JointManager.shared.JointForWeb(resultModels, fileName: "\(self.toolFucn)") 330 | self.endExecute() 331 | } 332 | } 333 | 334 | /// 输出 key 相同 value 不同的 词条 335 | fileprivate func inputKeySameValueDifferent() { 336 | starExecute() 337 | let sourceValueModels = allKeyValueModels(sourceDataList) 338 | DispatchQueue.global().async { 339 | var repeatKeys: [String] = [] 340 | var content = "" 341 | var resultModels: [KeyValueModel] = [] 342 | sourceValueModels.forEach { (targetModel) in 343 | sourceValueModels.forEach({ (sourceModel) in 344 | if targetModel.key == sourceModel.key && 345 | targetModel.value != sourceModel.value && 346 | targetModel.filePath! != sourceModel.filePath! && 347 | !repeatKeys.contains(sourceModel.key) && 348 | !repeatKeys.contains(targetModel.key) { 349 | repeatKeys.append(sourceModel.key) 350 | repeatKeys.append(targetModel.key) 351 | content += "\"\(targetModel.key)\" = \"\(sourceModel.value)\";\n" 352 | resultModels.append(targetModel) 353 | } 354 | }) 355 | } 356 | JointManager.shared.Joint("\(self.toolFucn)", content: content) 357 | JointManager.shared.JointForWeb(resultModels, fileName: "\(self.toolFucn)") 358 | self.endExecute() 359 | } 360 | } 361 | 362 | /// 输出 key 不同 value 相同的 词条 363 | fileprivate func inputKeyDifferentValueSame() { 364 | starExecute() 365 | let sourceValueModels = allKeyValueModels(sourceDataList) 366 | DispatchQueue.global().async { 367 | var repeatKeys: [String] = [] 368 | var content = "" 369 | var resultModels: [KeyValueModel] = [] 370 | sourceValueModels.forEach { (targetModel) in 371 | sourceValueModels.forEach({ (sourceModel) in 372 | if targetModel.key != sourceModel.key && 373 | targetModel.value == sourceModel.value && 374 | targetModel.filePath! != sourceModel.filePath! && 375 | !repeatKeys.contains(sourceModel.key) && 376 | !repeatKeys.contains(targetModel.key) { 377 | repeatKeys.append(sourceModel.key) 378 | repeatKeys.append(targetModel.key) 379 | content += "\"\(targetModel.key)\" = \"\(sourceModel.value)\";\n" 380 | resultModels.append(targetModel) 381 | } 382 | }) 383 | } 384 | JointManager.shared.Joint("\(self.toolFucn)", content: content) 385 | JointManager.shared.JointForWeb(resultModels, fileName: "\(self.toolFucn)") 386 | self.endExecute() 387 | } 388 | } 389 | 390 | /// 输出 目标文件中存在, 源文件中不存在的 词条 391 | fileprivate func inputTargetExistOriginalNoExistAction() { 392 | starExecute() 393 | DispatchQueue.global().async { 394 | let targetKeyValueModels = self.allKeyValueModels(self.targetDataList) 395 | let sourceKeyValueModels = self.allKeyValueModels(self.sourceDataList) 396 | var content = "" 397 | var androidContent = "" 398 | var resultModels: [KeyValueModel] = [] 399 | targetKeyValueModels.forEach({ (targetKeyValueModel) in 400 | self.showMessage("正在处理:" + targetKeyValueModel.key, label: self.targetMessageLBL) 401 | var isUsedKey = false 402 | for sourceKeyValueModel in sourceKeyValueModels { 403 | self.showMessage("正在处理:" + sourceKeyValueModel.key, label: self.sourceMessageLBL) 404 | if targetKeyValueModel.key == sourceKeyValueModel.key { 405 | isUsedKey = true 406 | break; 407 | } 408 | } 409 | if !isUsedKey { 410 | content += "\"\(targetKeyValueModel.key)\" = \"\(targetKeyValueModel.value)\";\n" 411 | var value = targetKeyValueModel.value 412 | value = value.replacingOccurrences(of: "<", with: "<") 413 | value = value.replacingOccurrences(of: ">", with: ">") 414 | androidContent = androidContent + "" + targetKeyValueModel.value + "" + "\n" 415 | resultModels.append(targetKeyValueModel); 416 | } 417 | }) 418 | JointManager.shared.Joint("\(self.toolFucn)", content: content) 419 | JointManager.shared.Joint("android.xml", content: androidContent) 420 | JointManager.shared.JointForWeb(resultModels, fileName: "\(self.toolFucn)") 421 | self.endExecute() 422 | } 423 | } 424 | 425 | /// 使用目标文件中的key 替换 源文件中的 key; 通过目标文件的value 和资源文件的key 去匹配 426 | fileprivate func replaceKeyUseTargetKeyAction() { 427 | starExecute() 428 | let targetKeyValueModels = allKeyValueModels(targetDataList) 429 | DispatchQueue.global().async { 430 | targetKeyValueModels.forEach({ (targetKeyValueModel) in 431 | self.showMessage("正在处理:" + targetKeyValueModel.key, label: self.targetMessageLBL) 432 | self.allKeyValueModels(self.sourceDataList, forEach: { (sourceKeyValueModel) -> Bool in 433 | if sourceKeyValueModel.key.lowercased().hasPrefix("i18n") { 434 | self.showMessage("正在处理:" + sourceKeyValueModel.key, label: self.sourceMessageLBL) 435 | if sourceKeyValueModel.key == targetKeyValueModel.value { 436 | if let path = sourceKeyValueModel.filePath, 437 | let range = sourceKeyValueModel.range { 438 | var content = try? String.init(contentsOfFile: path) 439 | content = ((content ?? "") as NSString).replacingOccurrences(of: sourceKeyValueModel.key, with: targetKeyValueModel.key, options: .anchored, range: range) 440 | try? content?.write(toFile: path, atomically: false, encoding: String.Encoding.utf8) 441 | return true 442 | } 443 | } 444 | } 445 | return false 446 | }) 447 | }) 448 | self.endExecute() 449 | } 450 | } 451 | } 452 | 453 | // MARK: - YQDragDropViewDelegate 454 | extension YQMainViewController: YQDragDropViewDelegate { 455 | func draggingFileAccept(_ dragDropView: YQDragDropView, files: [String]) { 456 | if dragDropView == sourceDragDropView { 457 | sourceDataList.removeAll() 458 | files.forEach { (pathStr) in 459 | let fileModel = YQFileModel(filePath: pathStr) 460 | sourceDataList.append(fileModel) 461 | } 462 | reloadSourceTableVC() 463 | } else { 464 | targetDataList.removeAll() 465 | files.forEach { (pathStr) in 466 | let fileModel = YQFileModel(filePath: pathStr) 467 | targetDataList.append(fileModel) 468 | } 469 | reloadTargetTableVC() 470 | } 471 | } 472 | } 473 | 474 | // MARK: - 拓展功能 475 | extension YQMainViewController { 476 | 477 | /// 1. 当目标文件的value和源文件的value不同时,是否更新目标文件的value 478 | /// 2. 当源文件中没有目标文件的key-value 时,是否添加 479 | /// - Parameter isUpdate: 是否更新 480 | /// - Parameter isAdd: 是否添加 481 | fileprivate func updateSourceFromeTarget(whenDifferentValue isUpdate: Bool = true, whenNoValue isAdd: Bool = true) { 482 | if !isUpdate && !isAdd { 483 | return; 484 | } 485 | starExecute() 486 | DispatchQueue.global().async { 487 | // 源文件key-value 488 | let sourceKeyValueModels = self.allKeyValueModels(self.sourceDataList) 489 | // 目标文件key-value 490 | let targetKeyValueModels = self.allKeyValueModels(self.targetDataList) 491 | // 待添加的key-value 492 | var addKeyValueModels: [KeyValueModel] = [] 493 | 494 | for tarketKeyValueModel in targetKeyValueModels { 495 | self.showMessage("正在处理:" + tarketKeyValueModel.key, label: self.targetMessageLBL) 496 | var isCanAdd = true 497 | for sourceKeyValueModel in sourceKeyValueModels { 498 | self.showMessage("正在处理:" + sourceKeyValueModel.key, label: self.sourceMessageLBL) 499 | if sourceKeyValueModel.key == tarketKeyValueModel.key { 500 | if sourceKeyValueModel.value != tarketKeyValueModel.value { 501 | if isUpdate { 502 | sourceKeyValueModel.value = tarketKeyValueModel.value 503 | } 504 | } 505 | isCanAdd = false 506 | break 507 | } 508 | } 509 | if isCanAdd, isAdd { 510 | addKeyValueModels.append(tarketKeyValueModel) 511 | } 512 | } 513 | JointManager.shared.JointForIOS(sourceKeyValueModels + addKeyValueModels) 514 | JointManager.shared.JointForAndroid(sourceKeyValueModels + addKeyValueModels) 515 | self.endExecute() 516 | } 517 | } 518 | 519 | /// 使用目标资源文件的value 作为占位词条替换 源文件的value 520 | fileprivate func placeholderForValue() { 521 | starExecute() 522 | DispatchQueue.global().async { 523 | // 德文文件 524 | let sourceKeyValueModels = self.allKeyValueModels(self.sourceDataList) 525 | // 中文文件 526 | let targetKeyValueModels = self.allKeyValueModels(self.targetDataList) 527 | for sourceKeyValueModel in sourceKeyValueModels { 528 | self.showMessage("正在处理:" + sourceKeyValueModel.key, label: self.sourceMessageLBL) 529 | if sourceKeyValueModel.value.count > 0 { 530 | continue 531 | } 532 | for tarketKeyValueModel in targetKeyValueModels { 533 | self.showMessage("正在处理:" + tarketKeyValueModel.key, label: self.targetMessageLBL) 534 | if sourceKeyValueModel.key == tarketKeyValueModel.key { 535 | sourceKeyValueModel.value = tarketKeyValueModel.value 536 | break 537 | } 538 | } 539 | } 540 | JointManager.shared.JointForIOS(sourceKeyValueModels) 541 | JointManager.shared.JointForAndroid(sourceKeyValueModels) 542 | self.endExecute() 543 | } 544 | } 545 | } 546 | 547 | extension YQMainViewController: NSTextFieldDelegate { 548 | func controlTextDidChange(_ obj: Notification) { 549 | if let textField = obj.object as? NSTextField { 550 | JointManager.shared.outFolderName = textField.stringValue 551 | configOutpath() 552 | } 553 | } 554 | } 555 | -------------------------------------------------------------------------------- /LocalizationToolForValue/Main/YQMainWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YQMainWindowController.swift 3 | // LocalizationToolForValue 4 | // 5 | // Created by sungrow on 2018/2/8. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class YQMainWindowController: NSWindowController { 12 | 13 | override func windowDidLoad() { 14 | super.windowDidLoad() 15 | 16 | window?.titlebarAppearsTransparent = true 17 | window?.standardWindowButton(.zoomButton)?.isHidden = true 18 | window?.titleVisibility = .hidden 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /LocalizationToolForValue/Main/YQQuestionViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YQQuestionViewController.swift 3 | // LocalizationToolForValue 4 | // 5 | // Created by sungrow on 2018/2/11. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class YQQuestionViewController: NSViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do view setup here. 16 | } 17 | 18 | } 19 | 20 | extension YQQuestionViewController { 21 | 22 | } 23 | -------------------------------------------------------------------------------- /LocalizationToolForValue/Main/YQQuestionViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 1. 输出资源文件中 `I18N` 开头的词条; 44 | * 拖入资源文件 `xxx.strings` 到左侧面板, 点击开始. 45 | * 执行完毕, 可点击文件夹icon, 自动打开输出的文件夹. 46 | 2. 输出资源文件中 `非I18N` 开头的词条; 47 | * 拖入资源文件 `xxx.strings` 到左侧面板, 点击开始. 48 | * 执行完毕, 可点击文件夹icon, 自动打开输出的文件夹. 49 | 3. 输出资源文件中 `key相同, value相同` 的词条; 50 | * 拖入资源文件 `xxx.strings` 到左侧面板, 点击开始. 51 | * 执行完毕, 可点击文件夹icon, 自动打开输出的文件夹. 52 | 4. 输出资源文件中 `key相同, value不同` 的词条; 53 | * 拖入资源文件 `xxx.strings` 到左侧面板, 点击开始. 54 | * 执行完毕, 可点击文件夹icon, 自动打开输出的文件夹. 55 | 5. 输出资源文件中 `key不同, value不同` 的词条; 56 | * 拖入资源文件 `xxx.strings` 到左侧面板, 点击开始. 57 | * 执行完毕, 可点击文件夹icon, 自动打开输出的文件夹. 58 | 6. 输出资源文件中 `目标文件中存在(面板右侧), 源文件中不存在(页面左侧)` 的词条; 59 | * 拖入源资源文件到左侧面板, 点击开始. 60 | * 拖入目标资源文件到右侧侧面板, 点击开始. 61 | * 执行完毕, 可点击文件夹icon, 自动打开输出的文件夹. 62 | * 该功能可查询源文件中未用到的词条, 或进行词条去重. 63 | 7. 使用目标文件中 `key-value` 的 `value` 匹配源文件中的 `key`, 并使用目标文件的 `key` 替换源文件中的 `key`; 64 | * 拖入源资源文件到左侧面板, 点击开始. 65 | * 拖入目标资源文件到右侧侧面板, 点击开始. 66 | * 此操作无文件输出, 因为直接在源文件上做替换操作. 67 | * 可替换代码中不规范的 `key`. 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 支持的文件格式如下: 116 | * [x] OC代码 `.m` 117 | * [x] Swift代码 `.swift` 118 | * [x] iOS 国际化资源 `.strings` 119 | * [x] Android 国际化资源 `.xml` 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /LocalizationToolForValue/Model/EnumeratorFileProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnumeratorFileProtocol.swift 3 | // LocalizationToolForValue 4 | // 5 | // Created by sungrow on 2018/2/9. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol EnumeratorFileProtocol { 12 | var filePath: String { get set } 13 | } 14 | 15 | extension EnumeratorFileProtocol { 16 | /// 是否是文件夹 17 | func isFolder() -> Bool { 18 | var folder: ObjCBool = false 19 | FileManager.default.fileExists(atPath: filePath, isDirectory: &folder) 20 | return folder.boolValue 21 | } 22 | 23 | /// 文件名称 24 | func fileName() -> String { 25 | return (filePath as NSString?)?.lastPathComponent ?? "" 26 | } 27 | 28 | /// 不带扩展名的文件名称 29 | func fileNameWithoutExtension() -> String { 30 | return (fileName() as NSString?)?.deletingPathExtension ?? "" 31 | } 32 | 33 | /// 文件扩展名 34 | func fileExtension() -> String { 35 | return (filePath as NSString?)?.pathExtension ?? "" 36 | } 37 | } 38 | 39 | extension EnumeratorFileProtocol { 40 | /// 获取到当前文件目录下的每一个文件 41 | /// 42 | /// - Parameter forEach: 回调闭包 43 | func enumeratorFile(_ forEach: ((String) -> Void)?) { 44 | if !isFolder() { 45 | if let closure = forEach { 46 | closure(filePath) 47 | } 48 | return; 49 | } 50 | let fileManager = FileManager.default 51 | let homePath = (filePath as NSString).expandingTildeInPath 52 | let directoryEnumerator = fileManager.enumerator(atPath: homePath) 53 | var fileName: String? = (directoryEnumerator?.nextObject() as! String?) 54 | while (fileName != nil) { 55 | if let closure = forEach { 56 | closure(homePath + "/" + fileName!) 57 | } 58 | fileName = (directoryEnumerator?.nextObject() as! String?) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /LocalizationToolForValue/Model/JointManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JointManager.swift 3 | // LocalizationToolForValue 4 | // 5 | // Created by sungrow on 2018/2/9. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class JointManager { 12 | static let shared = JointManager.init() 13 | private init(){} 14 | 15 | private let defaultHomePath = NSHomeDirectory() + "/Desktop" 16 | private let defaultOutFolderName = "LocalizationToolForReplaceKey" 17 | 18 | lazy var homePath: String = defaultHomePath 19 | 20 | lazy var outFolderName: String = defaultOutFolderName 21 | 22 | var outPath: String { 23 | let path = homePath + "/" + outFolderName 24 | return path 25 | } 26 | 27 | func JointForIOS(_ keyValueModels: [KeyValueModel]) { 28 | let chContent = keyValueModels.map({$0.iOSString}).joined(separator: "\n") 29 | let fileManager = FileManager.default 30 | if !fileManager.fileExists(atPath: outPath) { 31 | try! fileManager.createDirectory(atPath: outPath, withIntermediateDirectories: true, attributes: nil) 32 | } 33 | try? chContent.write(toFile: "\(outPath)/ch.Strings", atomically: true, encoding: String.Encoding.utf8) 34 | } 35 | 36 | func JointForWeb(_ keyValueModels: [KeyValueModel], fileName: String) { 37 | let chContent = keyValueModels.map({$0.webString}).joined(separator: "\n") 38 | let finalPath = outPath + "/web" 39 | let fileManager = FileManager.default 40 | if !fileManager.fileExists(atPath: finalPath) { 41 | try! fileManager.createDirectory(atPath: finalPath, withIntermediateDirectories: true, attributes: nil) 42 | } 43 | try? chContent.write(toFile: "\(finalPath)/\(fileName).properties", atomically: true, encoding: String.Encoding.utf8) 44 | } 45 | 46 | func JointForXML(_ keyValueModels: [KeyValueModel]) { 47 | let chContent = "\n\n" + keyValueModels.map({$0.xmlString}).joined(separator: "\n") + "\n" 48 | 49 | let finalPath = outPath + "/web" 50 | let fileManager = FileManager.default 51 | if !fileManager.fileExists(atPath: finalPath) { 52 | try! fileManager.createDirectory(atPath: finalPath, withIntermediateDirectories: true, attributes: nil) 53 | } 54 | 55 | try? chContent.write(toFile: "\(finalPath)/webserver.xml", atomically: true, encoding: String.Encoding.utf8) 56 | } 57 | 58 | func JointForAndroid(_ keyValueModels: [KeyValueModel]) { 59 | let chContent = keyValueModels.map({$0.androidString}).joined(separator: "\n") 60 | 61 | let fileManager = FileManager.default 62 | if !fileManager.fileExists(atPath: outPath) { 63 | try! fileManager.createDirectory(atPath: outPath, withIntermediateDirectories: true, attributes: nil) 64 | } 65 | 66 | try? chContent.write(toFile: "\(outPath)/android.xml", atomically: true, encoding: String.Encoding.utf8) 67 | } 68 | 69 | func Joint(_ fileName: String, content: String) { 70 | let fileManager = FileManager.default 71 | if !fileManager.fileExists(atPath: outPath) { 72 | try! fileManager.createDirectory(atPath: outPath, withIntermediateDirectories: true, attributes: nil) 73 | } 74 | try? content.write(toFile: "\(outPath)/\(fileName).Strings", atomically: true, encoding: String.Encoding.utf8) 75 | } 76 | 77 | /// 恢复默认的输出路径 78 | func restoreDefaultPath() { 79 | homePath = defaultHomePath 80 | outFolderName = defaultOutFolderName 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /LocalizationToolForValue/Model/KeyValueModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyValueModel.swift 3 | // LocalizationToolForValue 4 | // 5 | // Created by sungrow on 2018/2/9. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class KeyValueModel: Decodable { 12 | // 键 13 | var key: String = "" 14 | // 值 15 | var value: String = "" 16 | 17 | var range: NSRange? 18 | var filePath: String? 19 | 20 | init(key: String, value: String) { 21 | self.key = key 22 | self.value = value 23 | } 24 | 25 | } 26 | 27 | extension KeyValueModel { 28 | var androidString: String { 29 | var resultValue = value 30 | resultValue = resultValue.replacingOccurrences(of: "<", with: "<") 31 | resultValue = resultValue.replacingOccurrences(of: ">", with: ">") 32 | return "" + resultValue + "" 33 | } 34 | 35 | var iOSString: String { 36 | return "\"" + key + "\" = \"" + value + "\";" 37 | } 38 | 39 | var webString: String { 40 | return key + "=" + value 41 | } 42 | 43 | var xmlString: String { 44 | var resultValue = value 45 | resultValue = resultValue.replacingOccurrences(of: "<", with: "<") 46 | resultValue = resultValue.replacingOccurrences(of: ">", with: ">") 47 | return " " + resultValue + "" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /LocalizationToolForValue/Model/SplitManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SplitManager.swift 3 | // LocalizationToolForValue 4 | // 5 | // Created by sungrow on 2018/2/9. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum CodeFileType { 12 | case OC 13 | case Swift 14 | case js 15 | case html 16 | case c 17 | } 18 | 19 | enum SplitFileType { 20 | case strings 21 | case codeFileType(codeFileType: CodeFileType) 22 | case json 23 | case xls 24 | case db 25 | case xml 26 | case properties 27 | } 28 | 29 | final class SplitManager { 30 | static let shared = SplitManager.init() 31 | private init(){} 32 | 33 | func split(_ fileModel: YQFileModel, forEach: ((KeyValueModel) -> Bool)? = nil) -> [KeyValueModel] { 34 | var splitFiletype: SplitFileType = .strings 35 | if ["strings", "txt"].contains(fileModel.fileExtension.lowercased()) { 36 | splitFiletype = .strings 37 | } 38 | if ["json"].contains(fileModel.fileExtension.lowercased()) { 39 | splitFiletype = .json 40 | } 41 | if ["swift"].contains(fileModel.fileExtension.lowercased()) { 42 | splitFiletype = .codeFileType(codeFileType: .Swift) 43 | } 44 | if ["m"].contains(fileModel.fileExtension.lowercased()) { 45 | splitFiletype = .codeFileType(codeFileType: .OC) 46 | } 47 | if ["js"].contains(fileModel.fileExtension.lowercased()) { 48 | splitFiletype = .codeFileType(codeFileType: .js) 49 | } 50 | if ["html"].contains(fileModel.fileExtension.lowercased()) { 51 | splitFiletype = .codeFileType(codeFileType: .html) 52 | } 53 | if ["c"].contains(fileModel.fileExtension.lowercased()) { 54 | splitFiletype = .codeFileType(codeFileType: .c) 55 | } 56 | if ["xml"].contains(fileModel.fileExtension.lowercased()) { 57 | splitFiletype = .xml 58 | } 59 | if ["properties"].contains(fileModel.fileExtension.lowercased()) { 60 | splitFiletype = .properties 61 | } 62 | return split(fileModel, splitFileType: splitFiletype, forEach: forEach) 63 | } 64 | } 65 | 66 | extension SplitManager { 67 | 68 | private func splitProperties(_ fileModel: YQFileModel) -> [KeyValueModel] { 69 | var sourceKeyValueModels = [KeyValueModel]() 70 | let jsonContent = try? String.init(contentsOfFile: fileModel.filePath) 71 | if let lines = jsonContent?.components(separatedBy: "\n") { 72 | for (index, line) in lines.enumerated() { 73 | let keyValue = line.components(separatedBy: "=") 74 | if keyValue.count == 2 { 75 | let keyP = keyValue.first!.trimmingCharacters(in: .whitespaces) 76 | let valueS = keyValue.last! 77 | let keyValueModel = KeyValueModel(key: keyP, value: valueS) 78 | keyValueModel.filePath = "\(index)" 79 | sourceKeyValueModels.append(keyValueModel) 80 | } 81 | } 82 | } 83 | return sourceKeyValueModels 84 | } 85 | 86 | private func splitStrings(_ fileModel: YQFileModel, separateStr: String) -> [KeyValueModel] { 87 | var sourceKeyValueModels = [KeyValueModel]() 88 | let jsonContent = try? String.init(contentsOfFile: fileModel.filePath) 89 | if let lines = jsonContent?.components(separatedBy: "\n") { 90 | for (index, line) in lines.enumerated() { 91 | let keyValue = line.components(separatedBy: "\" = \"") 92 | if keyValue.count == 2 { 93 | var keyP = keyValue.first!.trimmingCharacters(in: .whitespaces) 94 | var valueS = keyValue.last! 95 | var dropString = "\"" 96 | if keyP.hasPrefix(dropString) { 97 | keyP = keyP.replacingOccurrences(of: dropString, with: "") 98 | } 99 | 100 | dropString = "\";" 101 | if valueS.hasSuffix(dropString) { 102 | valueS = valueS.replacingOccurrences(of: dropString, with: "") 103 | } 104 | let keyValueModel = KeyValueModel(key: keyP, value: valueS) 105 | keyValueModel.filePath = "\(index)" 106 | sourceKeyValueModels.append(keyValueModel) 107 | } 108 | } 109 | } 110 | return sourceKeyValueModels 111 | } 112 | 113 | private func split(_ fileModel: YQFileModel, splitFileType: SplitFileType, forEach: ((KeyValueModel) -> Bool)?) -> [KeyValueModel] { 114 | switch splitFileType { 115 | case .strings: 116 | if let _ = forEach { 117 | return splitStrings(fileModel, forEach: forEach) 118 | } 119 | return splitStrings(fileModel, separateStr: "\" = \"") 120 | case .json: 121 | return splitJson(fileModel) 122 | case .codeFileType(codeFileType: let type): 123 | return splitCodeFile(fileModel, codeFileType: type, forEach: forEach) 124 | case .xml: 125 | return splitXml(fileModel) 126 | case .properties: 127 | return splitProperties(fileModel) 128 | default: 129 | break 130 | } 131 | return [KeyValueModel]() 132 | } 133 | 134 | private func splitXml(_ fileModel: YQFileModel) -> [KeyValueModel] { 135 | var sourceKeyValueModels = [KeyValueModel]() 136 | let jsonContent = try? String.init(contentsOfFile: fileModel.filePath) 137 | if let lines = jsonContent?.components(separatedBy: "\n") { 138 | for line in lines { 139 | var keyValue = line.components(separatedBy: "\">") 140 | if keyValue.count != 2 { 141 | keyValue = line.components(separatedBy: "\" >") 142 | } 143 | if keyValue.count == 2 { 144 | var keyP = keyValue.first!.trimmingCharacters(in: .whitespaces) 145 | var valueS = keyValue.last!.trimmingCharacters(in: .controlCharacters) 146 | var dropString = " [KeyValueModel] { 164 | var sourceKeyValueModels = [KeyValueModel]() 165 | let jsonContent = try? String.init(contentsOfFile: fileModel.filePath) 166 | let jsonData = jsonContent?.data(using: .utf8) 167 | let deCoder = JSONDecoder() 168 | if let data = jsonData { 169 | let keyValueModels = try? deCoder.decode([KeyValueModel].self, from: data) 170 | if let models = keyValueModels { 171 | sourceKeyValueModels = sourceKeyValueModels + models 172 | } 173 | } 174 | return sourceKeyValueModels 175 | } 176 | 177 | private func splitStrings(_ fileModel: YQFileModel, forEach: ((KeyValueModel) -> Bool)?) -> [KeyValueModel] { 178 | return enumeratorFile(fileModel, prefix: "\"", suffix: "\" = \"", forEach: forEach) 179 | } 180 | 181 | private func splitCodeFile(_ fileModel: YQFileModel, codeFileType: CodeFileType, forEach: ((KeyValueModel) -> Bool)?) -> [KeyValueModel] { 182 | var prefix = "NSLocalizedString\\(@\"" 183 | var suffix = "\"," 184 | if codeFileType == .Swift { 185 | prefix = "NSLocalizedString\\(\"" 186 | } 187 | if codeFileType == .js { 188 | prefix = "localized\\('" 189 | suffix = "'" 190 | var part = enumeratorFile(fileModel, prefix: prefix, suffix: suffix, forEach: forEach) 191 | prefix = "localized\\(\"" 192 | suffix = "\"" 193 | part += enumeratorFile(fileModel, prefix: prefix, suffix: suffix, forEach: forEach) 194 | prefix = "localized\\([\"" 195 | suffix = "\"" 196 | part += enumeratorFile(fileModel, prefix: prefix, suffix: suffix, forEach: forEach) 197 | prefix = "localized\\(['" 198 | suffix = "'" 199 | part += enumeratorFile(fileModel, prefix: prefix, suffix: suffix, forEach: forEach) 200 | return part 201 | } 202 | if codeFileType == .html { 203 | prefix = "I18N_" 204 | suffix = "'" 205 | let part = enumeratorFile(fileModel, prefix: prefix, suffix: suffix, containPrefix: true, forEach: forEach) 206 | prefix = "I18N_" 207 | suffix = "\"" 208 | return part + enumeratorFile(fileModel, prefix: prefix, suffix: suffix, containPrefix: true, forEach: forEach) 209 | } 210 | if codeFileType == .c { 211 | prefix = "I18" 212 | suffix = "\"," 213 | return enumeratorFile(fileModel, prefix: prefix, suffix: suffix, containPrefix: true, forEach: forEach) 214 | } 215 | return enumeratorFile(fileModel, prefix: prefix, suffix: suffix, forEach: forEach) 216 | } 217 | 218 | private func enumeratorFile(_ fileModel: YQFileModel, prefix: String, suffix: String, containPrefix: Bool = false, loop: Bool = true, forEach: ((KeyValueModel) -> Bool)?) -> [KeyValueModel] { 219 | let content = try? String.init(contentsOfFile: fileModel.filePath) 220 | var sourceKeyValueModels = [KeyValueModel]() 221 | if let content = content { 222 | let pattern = containPrefix ? "(?=\(prefix)).*?(?=\(suffix))" : "(?<=\(prefix)).*?(?=\(suffix))" 223 | let regular = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive) 224 | let matches = regular?.matches(in: content, options: .reportProgress, range: NSRange.init(location: 0, length: content.count)) 225 | if let checkResults = matches { 226 | for checkResult in checkResults { 227 | let key = (content as NSString).substring(with: checkResult.range) 228 | let keyValueModel = KeyValueModel(key: key, value: "") 229 | keyValueModel.filePath = fileModel.filePath 230 | keyValueModel.range = checkResult.range 231 | sourceKeyValueModels.append(keyValueModel) 232 | if let closure = forEach { 233 | let haveTargetKey = closure(keyValueModel) 234 | if haveTargetKey && loop && !["strings", "txt"].contains(fileModel.fileExtension.lowercased()) { 235 | for _ in 0.. Int { 37 | return fileModels?.count ?? 0 38 | } 39 | 40 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { 41 | print("------ \(row)") 42 | let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "YQTableCellView"), owner: self) as? YQTableCellView 43 | cell?.fileModel = fileModels?[row] 44 | return cell 45 | } 46 | 47 | func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { 48 | return 60 49 | } 50 | 51 | } 52 | 53 | // MARK: - NSTableViewDelegate 54 | extension YQTableViewDelegate: NSTableViewDelegate { 55 | 56 | func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { 57 | if let filePath = fileModels?[row].filePath { 58 | NSWorkspace.shared.openFile(filePath) 59 | } 60 | return true 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon_16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon_16x16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon_32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon_32x32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon_128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon_128x128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon_256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon_256x256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon_512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon_512x512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/clear.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "clear@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "clear@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/clear.imageset/clear@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/clear.imageset/clear@2x.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/clear.imageset/clear@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/clear.imageset/clear@3x.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/file.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "file (3).png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "file (4).png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/file.imageset/file (3).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/file.imageset/file (3).png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/file.imageset/file (4).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/file.imageset/file (4).png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/folder.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "folder@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "folder@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/folder.imageset/folder@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/folder.imageset/folder@2x.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/folder.imageset/folder@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/folder.imageset/folder@3x.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/openFolder.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "openFolder@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "openFolder@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/openFolder.imageset/openFolder@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/openFolder.imageset/openFolder@2x.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/openFolder.imageset/openFolder@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/openFolder.imageset/openFolder@3x.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/placerholder.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "file (1).png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "file (2).png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/placerholder.imageset/file (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/placerholder.imageset/file (1).png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/placerholder.imageset/file (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/placerholder.imageset/file (2).png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/set.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "setting (1).png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "setting.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/set.imageset/setting (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/set.imageset/setting (1).png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/set.imageset/setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/set.imageset/setting.png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/star.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "开始.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "开始 (1).png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/star.imageset/开始 (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/star.imageset/开始 (1).png -------------------------------------------------------------------------------- /LocalizationToolForValue/Resource/Assets.xcassets/star.imageset/开始.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YQqiang/LocalizationToolForReplaceKey/575bec09cfe09f68901af5e7d0835672251df753/LocalizationToolForValue/Resource/Assets.xcassets/star.imageset/开始.png -------------------------------------------------------------------------------- /LocalizationToolForValue/View/YQDragDropView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YQDragDropView.swift 3 | // LocalizationToolForValue 4 | // 5 | // Created by sungrow on 2018/2/6. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @objc protocol YQDragDropViewDelegate:NSObjectProtocol { 12 | @objc optional func draggingEntered(_ dragDropView: YQDragDropView) 13 | @objc optional func draggingExit(_ dragDropView: YQDragDropView) 14 | func draggingFileAccept(_ dragDropView: YQDragDropView, files:[String]) 15 | } 16 | 17 | final class YQDragDropView: NSView { 18 | 19 | @IBInspectable var image: NSImage? { 20 | didSet { 21 | if let img = image { 22 | imageV.image = img 23 | } 24 | } 25 | } 26 | 27 | @IBInspectable var text: String? { 28 | didSet { 29 | if let txt = text { 30 | textF.stringValue = txt 31 | } 32 | } 33 | } 34 | 35 | @IBInspectable var textColor: NSColor? { 36 | didSet { 37 | textF.textColor = textColor 38 | } 39 | } 40 | 41 | @IBInspectable var fontSize: CGFloat = 16 { 42 | didSet { 43 | textF.font = NSFont.systemFont(ofSize: fontSize) 44 | } 45 | } 46 | 47 | var delegate : YQDragDropViewDelegate? 48 | 49 | fileprivate lazy var imageV: NSImageView = { 50 | let imageV = NSImageView() 51 | imageV.isEnabled = false 52 | imageV.unregisterDraggedTypes() 53 | addSubview(imageV) 54 | return imageV 55 | }() 56 | 57 | fileprivate lazy var textF: NSTextField = { 58 | let textField = NSTextField() 59 | textField.isEnabled = false 60 | textField.isEditable = false 61 | textField.backgroundColor = NSColor.clear 62 | textField.isBordered = false 63 | textField.alignment = .center 64 | textField.font = NSFont.systemFont(ofSize: fontSize) 65 | addSubview(textField) 66 | return textField 67 | }() 68 | 69 | override func awakeFromNib() { 70 | super.awakeFromNib() 71 | createView() 72 | 73 | imageV.translatesAutoresizingMaskIntoConstraints = false 74 | let imgVConstraintCenterY = NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: imageV, attribute: .bottom, multiplier: 1, constant: 0) 75 | let imgVConstraintCenterX = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: imageV, attribute: .centerX, multiplier: 1, constant: 0) 76 | addConstraints([imgVConstraintCenterY, imgVConstraintCenterX]) 77 | 78 | textF.translatesAutoresizingMaskIntoConstraints = false 79 | let textFConstraintTop = NSLayoutConstraint(item: imageV, attribute: .bottom, relatedBy: .equal, toItem: textF, attribute: .top, multiplier: 1, constant: -24) 80 | let textFConstraintLeft = NSLayoutConstraint(item: self, attribute: .left, relatedBy: .equal, toItem: textF, attribute: .left, multiplier: 1, constant: -32) 81 | let textFConstraintRight = NSLayoutConstraint(item: self, attribute: .right, relatedBy: .equal, toItem: textF, attribute: .right, multiplier: 1, constant: 32) 82 | let textFConstraintBottom = NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .greaterThanOrEqual, toItem: textF, attribute: .bottom, multiplier: 1, constant: 0) 83 | addConstraints([textFConstraintTop, textFConstraintLeft, textFConstraintRight, textFConstraintBottom]) 84 | 85 | } 86 | 87 | override func draw(_ dirtyRect: NSRect) { 88 | super.draw(dirtyRect) 89 | } 90 | 91 | } 92 | 93 | extension YQDragDropView { 94 | private func createView() { 95 | registerForDraggedTypes([.backwardsCompatibleFileURL]) 96 | // layer?.borderColor = NSColor.red.cgColor 97 | // layer?.borderWidth = 3 98 | } 99 | 100 | override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { 101 | if let delegate = self.delegate { 102 | if delegate.responds(to: #selector(draggingEntered(_:))) { 103 | delegate.draggingEntered!(self); 104 | } 105 | } 106 | let pastboard = sender.draggingPasteboard 107 | if (pastboard.types?.contains(.backwardsCompatibleFileURL))! { 108 | return .copy 109 | } 110 | return .every 111 | } 112 | 113 | override func draggingExited(_ sender: NSDraggingInfo?) { 114 | if let delegate = self.delegate { 115 | if delegate.responds(to: #selector(draggingExited(_:))) { 116 | delegate.draggingExit!(self); 117 | } 118 | } 119 | } 120 | 121 | override func prepareForDragOperation(_ sender: NSDraggingInfo) -> Bool { 122 | return true 123 | } 124 | 125 | override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { 126 | let files: [String]? = sender.draggingPasteboard.propertyList(forType: .backwardsCompatibleFileURL) as? [String] 127 | print("------ \(String(describing: files))") 128 | if self.delegate != nil, let files = files { 129 | self.delegate?.draggingFileAccept(self, files: files); 130 | } 131 | return true 132 | } 133 | } 134 | 135 | extension NSPasteboard.PasteboardType { 136 | static let backwardsCompatibleFileURL: NSPasteboard.PasteboardType = { 137 | return NSPasteboard.PasteboardType("NSFilenamesPboardType") 138 | }() 139 | } 140 | -------------------------------------------------------------------------------- /LocalizationToolForValue/View/YQGradientView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YQGradientView.swift 3 | // LocalizationToolForValue 4 | // 5 | // Created by sungrow on 2018/2/6. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class YQGradientView: NSView { 12 | 13 | @IBInspectable var lowGradientColor: NSColor = NSColor(deviceRed:0.08, green:0.66, blue:0.84, alpha:1.00) 14 | @IBInspectable var highGradientColor: NSColor = NSColor(deviceRed:0.05, green:0.47, blue:0.73, alpha:1.00) 15 | 16 | override func draw(_ dirtyRect: NSRect) { 17 | super.draw(dirtyRect) 18 | 19 | let path = NSBezierPath() 20 | path.appendRect(dirtyRect) 21 | 22 | let gradient = NSGradient(colors: [lowGradientColor, highGradientColor], atLocations: [0, 1], colorSpace: NSColorSpace.deviceRGB) 23 | gradient?.draw(in: path, angle: -90) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /LocalizationToolForValue/View/YQTableCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YQTableCellView.swift 3 | // LocalizationToolForValue 4 | // 5 | // Created by sungrow on 2018/2/7. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class YQTableCellView: NSTableCellView { 12 | 13 | var fileModel: YQFileModel? { 14 | didSet { 15 | guard let model = fileModel else { 16 | return 17 | } 18 | if ["png", "jpg", "jpeg"].contains(model.fileExtension) { 19 | fileType.image = NSImage(contentsOfFile: model.filePath) 20 | } else { 21 | fileType.image = model.isFolder ? NSImage.init(imageLiteralResourceName: "folder") : NSImage.init(imageLiteralResourceName: "file") 22 | } 23 | fileName.stringValue = model.fileName 24 | 25 | print("------- filename = \(fileName.stringValue)") 26 | } 27 | } 28 | 29 | 30 | @IBOutlet weak var fileType: NSImageView! 31 | @IBOutlet weak var fileName: NSTextField! 32 | 33 | override func draw(_ dirtyRect: NSRect) { 34 | super.draw(dirtyRect) 35 | 36 | // Drawing code here. 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /LocalizationToolForValue/View/YQTableCellView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /LocalizationToolForValueTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LocalizationToolForValueTests/LocalizationToolForValueTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocalizationToolForValueTests.swift 3 | // LocalizationToolForValueTests 4 | // 5 | // Created by sungrow on 2018/2/8. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import LocalizationToolForValue 11 | 12 | class LocalizationToolForValueTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /LocalizationToolForValueUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LocalizationToolForValueUITests/LocalizationToolForValueUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocalizationToolForValueUITests.swift 3 | // LocalizationToolForValueUITests 4 | // 5 | // Created by sungrow on 2018/2/8. 6 | // Copyright © 2018年 sungrow. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class LocalizationToolForValueUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LocalizationToolForReplaceKey 2 | ![](http://yuqiangcoder.com/assets/postImages/ios/201806/1.png) 3 | ### 前言 4 | 之前发过如何查询项目代码中未国际化的字符串工具 [LocalizationTool](http://yuqiangcoder.com/2017/12/22/LocalizationTool-%E5%9B%BD%E9%99%85%E5%8C%96%E5%B7%A5%E5%85%B7.html), 但是全部词条翻译后, 资源文件仍然有很多问题. 5 | 6 | 1. 资源文件中词条重复 7 | 8 | * 可能是 key 相同, value 相同; 9 | * 也可能是 key 相同, value 不同; 10 | * 甚至可能是 key 不同, value 相同; 11 | 12 | 2. 存在资源文件中存在的词条, 实际项目代码中却未使用到(可能是功能更改或者提示文案变更, 而资源文件未同步更新的原因); 13 | 3. 和安卓组的资源文件不统一; 14 | 4. 定义的 `key` 不符合规定, 需要重新替换 `key`; 15 | 16 | 本工具力求解决上述问题: 去重, 删除无用词条, 替换key 等. 17 | 18 | ### 下载 19 | 下载地址: 20 | [LocalizationToolForReplaceKey 源码](https://github.com/YQqiang/LocalizationToolForReplaceKey) 21 | 22 | 博客地址: [iOS国际化利器](http://yuqiangcoder.com/2018/06/16/iOS%E5%9B%BD%E9%99%85%E5%8C%96%E5%88%A9%E5%99%A8.html) 23 | 24 | ### 使用 25 | ![应用程序界面](http://yuqiangcoder.com/assets/postImages/ios/201806/2.png) 26 | 27 | 1. 输出资源文件中 `I18N` 开头的词条; 28 | * 拖入资源文件 `xxx.strings` 到左侧面板, 点击开始. 29 | * 执行完毕, 可点击文件夹icon, 自动打开输出的文件夹. 30 | 2. 输出资源文件中 `非I18N` 开头的词条; 31 | * 拖入资源文件 `xxx.strings` 到左侧面板, 点击开始. 32 | * 执行完毕, 可点击文件夹icon, 自动打开输出的文件夹. 33 | 3. 输出资源文件中 `key相同, value相同` 的词条; 34 | * 拖入资源文件 `xxx.strings` 到左侧面板, 点击开始. 35 | * 执行完毕, 可点击文件夹icon, 自动打开输出的文件夹. 36 | 4. 输出资源文件中 `key相同, value不同` 的词条; 37 | * 拖入资源文件 `xxx.strings` 到左侧面板, 点击开始. 38 | * 执行完毕, 可点击文件夹icon, 自动打开输出的文件夹. 39 | 5. 输出资源文件中 `key不同, value不同` 的词条; 40 | * 拖入资源文件 `xxx.strings` 到左侧面板, 点击开始. 41 | * 执行完毕, 可点击文件夹icon, 自动打开输出的文件夹. 42 | 6. 输出资源文件中 `目标文件中存在(面板右侧), 源文件中不存在(页面左侧)` 的词条; 43 | * 拖入源资源文件到左侧面板, 点击开始. 44 | * 拖入目标资源文件到右侧侧面板, 点击开始. 45 | * 执行完毕, 可点击文件夹icon, 自动打开输出的文件夹. 46 | * 该功能可查询源文件中未用到的词条, 或进行词条去重. 47 | 7. 使用目标文件中 `key-value` 的 `value` 匹配源文件中的 `key`, 并使用目标文件的 `key` 替换源文件中的 `key`; 48 | * 拖入源资源文件到左侧面板, 点击开始. 49 | * 拖入目标资源文件到右侧侧面板, 点击开始. 50 | * 此操作无文件输出, 因为直接在源文件上做替换操作. 51 | * 可替换代码中不规范的 `key`. 52 | 53 | 54 | ### 支持的文件格式如下: 55 | 56 | * [x] OC代码 `.m` 57 | * [x] Swift代码 `.swift` 58 | * [x] iOS 国际化资源 `.strings` 59 | * [x] Android 国际化资源 `.xml` 60 | 61 | 62 | ## 联系我: 63 | - 博客: http://yuqiangcoder.com/ 64 | - 邮箱: yuqiang.coder@gmail.com 65 | 66 | 67 | --------------------------------------------------------------------------------