Alfred是個好東西,不過檢索程式的時候不支援拼音搜尋;我在論壇看到有人給作者反饋過,無奈作者說支援中文,他不知道拼音是什麼,於是就不了了之了。舉個例子:我想開啟網易雲音樂
,可是當我輸入wangyiyunyinyue
的時候卻是這樣的結果:
要麼我知道這個App的名字叫做NeteaseMusic
,要麼我就需要用中文輸入網易雲音樂
開啟了;如果恰巧輸入法是英文輸入狀態,那麼就會遇到上圖的情況;這時候再把已經輸入的刪除然後切換輸入法開啟,效率無疑大大折扣。
就算這裡搜尋這個App可以使用英文名字解決,可是對於某些系統程式比如郵件可能還知道是Mail
,那麼備忘錄呢?便籤呢?還有一些別的中文程式沒有英文名的比如馬克飛象?如果Alfred能支援拼音搜尋,這些問題全部都沒了!而且,Alfred可以強制使用英文輸入,直接使用字母檢索,不用切換輸入法了。
原理
經過簡單的觀察之後,發現Alfred檢索程式不僅僅是檢索名字,還收集了一些額外的資訊;在Alfred作者的幫助下,知道它利用了Mac檔案系統的一個擴充資訊的欄位;如果你發現某些目錄後面有@
那麼就是有擴充資訊了:
1 2 |
drwxr-xr-x+ 3 root wheel 102 9 10 2014 Stickies.app/ drwxr-xr-x@ 3 weishu admin 102 3 26 2015 Sublime Text.app/ |
可以藉助命令列工具xattr
進行操作;具體使用可以man xattr
.
所以,我們可以通過把拼音資訊新增到檔案的擴充資訊裡面去,這樣Alfred就能借助這些資訊幫助拼音檢索了。
實現
獲取程式名
程式名不僅僅是一個檔名這麼簡單,Mac軟體有一個叫做localization的概念,大致就是國際化吧;程式結構把國際化的欄位存放在不同的檔案裡面,在程式本地化之後自動load.
我們要使用的那個欄位是CFBundleName
;存放在/<App>/Contents/Resources/<language>/InfoPlist.strings
這個檔案裡面;我們把這個名字讀取出來即可。
嘗試過使用objc
的介面NSBundle.localizedInfoDiction
來獲取本地化的欄位,無奈拿到的永遠是英文欄位;只好手工解析中文欄位了(不會Objc );使用的命令列工具plutil
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def _get_localized_name(abs_path): '''get the localized name of given app''' bundle = NSBundle.new() bundle.initWithPath_(abs_path) localizations = bundle.localizations() chinese = ('zh_CN', 'zh_Hans') b = any(map(lambda x: x in localizations, chinese)) if not b: return for ch in chinese: path = bundle.pathForResource_ofType_inDirectory_forLanguage_('InfoPlist', 'strings', None, ch) if not path: continue # the path must surround with "", there may be space characters json_str = subprocess.check_output(u'plutil -convert json -o - "%s"' % path, shell=True) # print json_str json_res = json.loads(json_str, encoding='utf8') name = json_res.get('CFBundleName') if name: return name |
轉換為拼音
可以直接使用python的拼音轉換庫pypinyin,藉助這個工具,一行程式碼搞定:
1 2 |
def _get_app_pinyin_name(app_name): reduce(lambda x, y: x + y, lazy_pinyin(app_name, errors='ignore')) |
新增拼音資訊
拼音資訊被新增到檔案的擴充資訊裡面,直接使用xattr
新增即可:
1 2 3 |
def _add_meta_data(app_pinyin_name, app_path): ''' add meta data(comments) to the app, which can help Alfred or SpotLight find it''' subprocess.check_call('xattr -w com.apple.metadata:kMDItemFinderComment %s %s' % (app_pinyin_name, app_path), shell=True) |
好了,把這些程式碼整合起來,就能得到最終的結果了,完整的程式碼在這裡。
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 |
def main(): pattern = re.compile(r'^[\w\s.]+$') workspace = NSWorkspace.sharedWorkspace() for app_dir in APP_DIRECTORYS: if not os.path.exists(app_dir): continue for root, dirs, files in os.walk(app_dir, topdown=True): remove_list = [] for directory in dirs: # print type(directory), root, directory full_path = os.path.join(root, directory) if not _is_application(workspace, full_path): continue remove_list.append(directory) localized_name = _get_localized_name(full_path) app_name = localized_name if localized_name else directory.rsplit(r'.')[0] if pattern.match(app_name): continue _add_meta_data(_get_app_pinyin_name(app_name), full_path) # if this directory is already a Application # do not traverse this; some app may be very large # and there won't be any other app inside it dirs[:] = [d for d in dirs if d not in remove_list] |
最後,我們執行這一段指令碼即可sudo python main.py
。之所以需要sudo是因為某些系統程式(比如家計算器),直接使用是沒有許可權的。
完整程式碼見原文:http://www.tianweishu.com/2015/12/07/make-alfred-support-pinyinyin-search
最後看效果: