diff --git a/ShadowsocksX-NG.xcodeproj/project.pbxproj b/ShadowsocksX-NG.xcodeproj/project.pbxproj index f4f1ca4..937e6ec 100644 --- a/ShadowsocksX-NG.xcodeproj/project.pbxproj +++ b/ShadowsocksX-NG.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 9B0BFFEB1D0460A70040E62B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9B0BFFEA1D0460A70040E62B /* Assets.xcassets */; }; 9B0BFFEE1D0460A70040E62B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9B0BFFEC1D0460A70040E62B /* MainMenu.xib */; }; 9B0BFFF91D0460A70040E62B /* ShadowsocksX_NGTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B0BFFF81D0460A70040E62B /* ShadowsocksX_NGTests.swift */; }; + 9B172A6A1D0ADDDD00B87B9A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9B172A6C1D0ADDDD00B87B9A /* Localizable.strings */; }; 9B2491B41D0ACC3A003BBECC /* PreferencesWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9B2491B61D0ACC3A003BBECC /* PreferencesWindowController.xib */; }; 9B2491B71D0ACC3E003BBECC /* AdvPreferencesWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9B2491B91D0ACC3E003BBECC /* AdvPreferencesWindowController.xib */; }; 9B3FFF0D1D05FEB30019A709 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B3FFF0C1D05FEB30019A709 /* Utils.swift */; }; @@ -115,6 +116,9 @@ 9B0BFFFA1D0460A70040E62B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9B0BFFFF1D0460A70040E62B /* ShadowsocksX-NGUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "ShadowsocksX-NGUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 9B172A5D1D0ACEA100B87B9A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/AdvPreferencesWindowController.strings"; sourceTree = ""; }; + 9B172A601D0ADA5B00B87B9A /* genstrings.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = genstrings.py; sourceTree = ""; }; + 9B172A6B1D0ADDDD00B87B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = ""; }; + 9B172A6D1D0ADDE800B87B9A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 9B2491B51D0ACC3A003BBECC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/PreferencesWindowController.xib; sourceTree = ""; }; 9B2491B81D0ACC3E003BBECC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/AdvPreferencesWindowController.xib; sourceTree = ""; }; 9B2491BA1D0ACC49003BBECC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/PreferencesWindowController.strings"; sourceTree = ""; }; @@ -213,6 +217,7 @@ 9B07EFB01D048E900052D9DF /* Support Files */ = { isa = PBXGroup; children = ( + 9B172A6C1D0ADDDD00B87B9A /* Localizable.strings */, 9B3FFF4B1D09D8F70019A709 /* install_helper.sh */, 9B3FFF351D08CF110019A709 /* jquery.min.js */, 9B3FFF361D08CF110019A709 /* qrcode.min.js */, @@ -245,6 +250,7 @@ 9B0BFFDC1D0460A70040E62B = { isa = PBXGroup; children = ( + 9B172A601D0ADA5B00B87B9A /* genstrings.py */, 9B0BFFE71D0460A70040E62B /* ShadowsocksX-NG */, 9B0BFFF71D0460A70040E62B /* ShadowsocksX-NGTests */, 9B0B00021D0460A70040E62B /* ShadowsocksX-NGUITests */, @@ -454,6 +460,7 @@ knownRegions = ( en, Base, + "zh-Hans", ); mainGroup = 9B0BFFDC1D0460A70040E62B; productRefGroup = 9B0BFFE61D0460A70040E62B /* Products */; @@ -475,6 +482,7 @@ files = ( 9B3FFF541D09E2D10019A709 /* proxy_conf_helper in Resources */, 9BEEF0691D04D4D500FC52B3 /* install_ss_local.sh in Resources */, + 9B172A6A1D0ADDDD00B87B9A /* Localizable.strings in Resources */, 9B2491B41D0ACC3A003BBECC /* PreferencesWindowController.xib in Resources */, 9B3FFF291D08A1DF0019A709 /* user-rule.txt in Resources */, 9BEEF06A1D04D4D500FC52B3 /* start_ss_local.sh in Resources */, @@ -742,6 +750,15 @@ name = MainMenu.xib; sourceTree = ""; }; + 9B172A6C1D0ADDDD00B87B9A /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 9B172A6B1D0ADDDD00B87B9A /* Base */, + 9B172A6D1D0ADDE800B87B9A /* zh-Hans */, + ); + name = Localizable.strings; + sourceTree = ""; + }; 9B2491B61D0ACC3A003BBECC /* PreferencesWindowController.xib */ = { isa = PBXVariantGroup; children = ( diff --git a/ShadowsocksX-NG/Base.lproj/Localizable.strings b/ShadowsocksX-NG/Base.lproj/Localizable.strings new file mode 100644 index 0000000..ce26059 --- /dev/null +++ b/ShadowsocksX-NG/Base.lproj/Localizable.strings @@ -0,0 +1,46 @@ + + + + + + +/* + * SHARED STRINGS + */ + +/* + * ./PACUtils.swift + */ + +"PAC has been updated by latest GFW List." = "PAC has been updated by latest GFW List."; + +"Failed to download latest GFW List." = "Failed to download latest GFW List."; + +/* + * ./PreferencesWindowController.swift + */ + +"New Server" = "New Server"; + +/* + * ./AppDelegate.swift + */ + +"Add Shadowsocks Server Profile" = "Add Shadowsocks Server Profile"; + +"By scan QR Code" = "By scan QR Code"; + +" Encription Method: \(profile.method)" = " Encription Method: \(profile.method)"; + +"Current server profile is not valid." = "Current server profile is not valid."; + +"No current server profile." = "No current server profile."; + +"Shadowsocks: On" = "Shadowsocks: On"; + +"Close Shadowsocks" = "Close Shadowsocks"; + +"Shadowsocks: Off" = "Shadowsocks: Off"; + +"Open Shadowsocks" = "Open Shadowsocks"; + diff --git a/ShadowsocksX-NG/en.lproj/MainMenu.strings b/ShadowsocksX-NG/en.lproj/MainMenu.strings new file mode 100644 index 0000000..754d532 --- /dev/null +++ b/ShadowsocksX-NG/en.lproj/MainMenu.strings @@ -0,0 +1,42 @@ + +/* Class = "NSMenuItem"; title = "Global Mode"; ObjectID = "3Sa-e9-VXX"; */ +"3Sa-e9-VXX.title" = "Global Mode"; + +/* Class = "NSMenu"; title = "Servers"; ObjectID = "9Y1-db-3HK"; */ +"9Y1-db-3HK.title" = "Servers"; + +/* Class = "NSMenuItem"; title = "Open ShadowsocksX"; ObjectID = "GSu-Tf-StS"; */ +"GSu-Tf-StS.title" = "Open ShadowsocksX"; + +/* Class = "NSMenu"; title = "ShadowsocksX-NE"; ObjectID = "Hob-KD-bx9"; */ +"Hob-KD-bx9.title" = "ShadowsocksX-NE"; + +/* Class = "NSMenuItem"; title = "Server Preferences..."; ObjectID = "M5r-E7-44f"; */ +"M5r-E7-44f.title" = "Server Preferences..."; + +/* Class = "NSMenuItem"; title = "Scan QR Code From Screen ..."; ObjectID = "Qe6-bF-paT"; */ +"Qe6-bF-paT.title" = "Scan QR Code From Screen ..."; + +/* Class = "NSMenuItem"; title = "Show QR Code For Current Server..."; ObjectID = "R6A-96-Zcb"; */ +"R6A-96-Zcb.title" = "Show QR Code For Current Server..."; + +/* Class = "NSMenuItem"; title = "Update PAC from GFW List"; ObjectID = "TFc-Ec-duM"; */ +"TFc-Ec-duM.title" = "Update PAC from GFW List"; + +/* Class = "NSMenuItem"; title = "Advance Preference ..."; ObjectID = "bZ3-fy-34d"; */ +"bZ3-fy-34d.title" = "Advance Preference ..."; + +/* Class = "NSMenuItem"; title = "Launch At Login"; ObjectID = "eUq-p7-ICK"; */ +"eUq-p7-ICK.title" = "Launch At Login"; + +/* Class = "NSMenuItem"; title = "Showsocks: On"; ObjectID = "fzk-mE-CEV"; */ +"fzk-mE-CEV.title" = "Showsocks: On"; + +/* Class = "NSMenuItem"; title = "Auto Mode By PAC"; ObjectID = "hOa-5N-3ik"; */ +"hOa-5N-3ik.title" = "Auto Mode By PAC"; + +/* Class = "NSMenuItem"; title = "Servers"; ObjectID = "u5M-hQ-VSc"; */ +"u5M-hQ-VSc.title" = "Servers"; + +/* Class = "NSMenuItem"; title = "Quit"; ObjectID = "vJS-JW-byz"; */ +"vJS-JW-byz.title" = "Quit"; diff --git a/ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings b/ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings new file mode 100644 index 0000000..ce743aa --- /dev/null +++ b/ShadowsocksX-NG/zh-Hans.lproj/Localizable.strings @@ -0,0 +1,46 @@ + + + + + + +/* + * SHARED STRINGS + */ + +/* + * ./PACUtils.swift + */ + +"PAC has been updated by latest GFW List." = "已经使用最新的 GFW List 更新PAC"; + +"Failed to download latest GFW List." = "下载 GFW List 失败"; + +/* + * ./PreferencesWindowController.swift + */ + +"New Server" = "新服务器"; + +/* + * ./AppDelegate.swift + */ + +"Add Shadowsocks Server Profile" = "已添加新Shaodwsocks服务器配置"; + +"By scan QR Code" = "通过扫描二维码"; + +" Encription Method: \(profile.method)" = " 加密方法: \(profile.method)"; + +"Current server profile is not valid." = "当前服务器配置无效"; + +"No current server profile." = "未设置当前服务器配置"; + +"Shadowsocks: On" = "Shadowsocks: On"; + +"Close Shadowsocks" = "关闭 Shadowsocks"; + +"Shadowsocks: Off" = "Shadowsocks: Off"; + +"Open Shadowsocks" = "打开 Shadowsocks"; + diff --git a/genstrings.py b/genstrings.py new file mode 100644 index 0000000..338a604 --- /dev/null +++ b/genstrings.py @@ -0,0 +1,155 @@ +# Created by Johannes Schriewer on 2011-11-30. Modified by Roy Marmelstein 2015-08-05 +# Copyright (c) 2011 planetmutlu. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER +# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# This script is heavily copied from: https://github.com/dunkelstern/Cocoa-Localisation-Helper +# Copied from https://github.com/marmelroy/Localize-Swift By QiuYuzhou + +import os, re, subprocess +import fnmatch + +def fetch_files_recursive(directory, extension): + matches = [] + for root, dirnames, filenames in os.walk(directory): + for filename in fnmatch.filter(filenames, '*' + extension): + matches.append(os.path.join(root, filename)) + return matches + + +# prepare regexes +localizedStringComment = re.compile('NSLocalizedString\("([^"]*)",\s*"([^"]*)"\s*\)', re.DOTALL) +localizedStringNil = re.compile('NSLocalizedString\("([^"]*)",\s*nil\s*\)', re.DOTALL) +localized = re.compile('Localized\("([^"]*)"[^\n\r]*\)', re.DOTALL) +localizedProperty = re.compile('"([^"]*)".localized', re.DOTALL)# Add By QiuYuzhou +localizedSwift2 = re.compile('"([^"]*)".localized\(\)', re.DOTALL) +localizedSwift2WithFormat = re.compile('"([^"]*)".localizedFormat\([^\n\r]*\)', re.DOTALL) + +# get string list +uid = 0 +strings = [] +for file in fetch_files_recursive('.', '.swift'): + with open(file, 'r') as f: + content = f.read() + for result in localizedStringComment.finditer(content): + uid += 1 + strings.append((result.group(1), result.group(2), file, uid)) + for result in localizedStringNil.finditer(content): + uid += 1 + strings.append((result.group(1), '', file, uid)) + for result in localized.finditer(content): + uid += 1 + strings.append((result.group(1), '', file, uid)) + # Add By QiuYuzhou, Begin + for result in localizedProperty.finditer(content): + uid += 1 + strings.append((result.group(1), '', file, uid)) + # Add By QiuYuzhou, End + for result in localizedSwift2.finditer(content): + uid += 1 + strings.append((result.group(1), '', file, uid)) + for result in localizedSwift2WithFormat.finditer(content): + uid += 1 + strings.append((result.group(1), '', file, uid)) + +# prepare regexes +localizedString = re.compile('"[^=]*=\s*"([^"]*)";') + +# Changed By QiuYuzhou, disable for *.xib +# fetch files +#for file in fetch_files_recursive('.', '.xib'): +# tempFile = file + '.strings' +# utf8tempFile = file + '.strings.utf8' +# subprocess.call('ibtool --export-strings-file "' + tempFile + '" "' + file + '" 2>/dev/null', shell=True) +# subprocess.call('iconv -s -f UTF-16 -t UTF-8 "' + tempFile + '" >"'+utf8tempFile+'" 2>/dev/null', shell=True) +# +# f = open(utf8tempFile, 'r') +# for line in f: +# result = localizedString.match(line) +# if result: +# uid += 1 +# strings.append((result.group(1), '', file, uid)) +# f.close() +# +# os.remove(utf8tempFile) +# os.remove(tempFile) + +# find duplicates +duplicated = [] +filestrings = {} +for string1 in strings: + dupmatch = 0 + for string2 in strings: + if string1[3] == string2[3]: + continue + if string1[0] == string2[0]: + if string1[2] != string2[2]: + dupmatch = 1 + break + if dupmatch == 1: + dupmatch = 0 + for string2 in duplicated: + if string1[0] == string2[0]: + dupmatch = 1 + break + if dupmatch == 0: + duplicated.append(string1) + else: + dupmatch = 0 + if string1[2] in filestrings: + for fs in filestrings[string1[2]]: + if fs[0] == string1[0]: + dupmatch = 1 + break + else: + filestrings[string1[2]] = [] + if dupmatch == 0: + filestrings[string1[2]].append(string1) + +print '\n\n\n\n\n' +print '/*\n * SHARED STRINGS\n */\n' + +# output filewise +for key in filestrings.keys(): + print '/*\n * ' + key + '\n */\n' + + strings = filestrings[key] + for string in strings: + if string[1] == '': + print '"' + string[0] + '" = "' + string[0] + '";' + print + else: + print '/* ' + string[1] + ' */' + print '"' + string[0] + '" = "' + string[0] + '";' + print + +# output duplicates +for string in duplicated: + if string[1] == '': + print '"' + string[0] + '" = "' + string[0] + '";' + print + else: + print '/* ' + string[1] + ' */' + print '"' + string[0] + '" = "' + string[0] + '";' + print \ No newline at end of file