Index: trunk/l10n-support/zh_TW/README.txt =================================================================== --- trunk/l10n-support/zh_TW/README.txt (revision 1538625) +++ trunk/l10n-support/zh_TW/README.txt (revision 1538626) @@ -1,94 +1,94 @@ zh_TW 翻譯相關準則與建議: (1) 請盡量遵循該指引翻譯: https://docs.google.com/document/d/1Zs4CS_ZjN-imnImq4aEsiVYih8zkIkVZTSQim13_kYg (2) 您可使用放置於此處的 sync_po_file.py 以保證範本的 PO 檔案數與字串與 zh_TW 語言相同。 (使用方法請參閱 == PO Sync 工具使用 == ) (3) 若有時間,可使用上層目錄中的 pology 工具標準化翻譯檔。 (使用方法請參閱 == POLOGY 使用 == ) (4) 你能參考 == 同步 trunk 與 stable 翻譯檔案 == 了解如何在翻譯 trunk 的字串時就能同步 stable 的字串。 (5) 有任何問題請參閱「== 聯絡團隊 ==」與團隊聯絡。 == PO Sync 工具使用 == 1. 確保你已經安裝 Python 3.7 以上。 2. 第一項為你想翻譯分支的範本資料夾。 第二項為你想翻譯分支的要翻譯資料夾。 (ex. zh_TW zh_CN) 3. 可輸入 --help 得知此工具的其他使用資訊。 == POLOGY 使用 == -確保您已經安裝 Python 2。 +最新的 Pology 存放在:https://websvn.kde.org/trunk/l10n-support/pology/ +請使用 svn 或 git-svn 將 svn://anonsvn.kde.org/home/kde/trunk/l10n-support/pology +clone 下來,未來如果 pology 有更新只需 checkout (svn) rebase (git-svn) 即可。 -(A) 編譯使用 -1. 請先安裝 cmake -2. 建立 (pology 存放目錄)/build +在使用前,請確保您已經安裝 Python 2。 -請進入目標語言目錄,輸入 `python2 (pology 存放目錄)/scripts/posieve.py [動作] [-s 參數]` 來對 PO 檔案做 +請進入翻譯檔目錄,輸入 `python2 (pology 存放目錄)/scripts/posieve.py [動作] [-s 參數]` 來對 PO 檔案做 指定動作。 接下來講建議做的一些動作。 - 動作列表 - [動作] remove-obsolete [用意] 移除已經過時的字串。除非過時字串真的太多,否則沒什麼必要執行。 [參數] 無 [動作] remove-previous [用意] 移除無 fuzzy 標記之先前訊息。 [參數] all: 移除包括 fuzzy 標記的先前訊息。 [動作] normalize-header [用意] 標準化每個 PO 檔案的檔頭,讓 PO 的檔頭一致化。 [參數] 無 [動作] check-kde4 [用意] 檢查每個 PO 檔案的字串,看 HTML Tag 是否對稱、內容是否有效等等。 [參數] strict: 嚴格檢查模式。建議不加。 lokalize: 使用 lokalize 開啟有問題的 PO 檔,讓你比較好修正。 注意:lokalize 必須要是開著的。 建議傳入。 [動作] unfuzzy-context-only [用意] 將只更改翻譯備註 (context) 的被標 fuzzy 翻譯取消 fuzzy 標記。 [參數] 無 [動作] unfuzzy-inplace-only [用意] 修正 HTML 標籤,並將因標籤問題而標為 fuzzy 的字串取消 fuzzy 標記。 [參數] 無 == 同步 trunk 與 stable 翻譯檔案 == 這可以解決要重複翻譯 trunk 和 stable 中同一個軟體 po 檔的問題。 此使用 Lokalize 18.12.0 示範,若未來方法有所變更,請幫忙提交修改。 I. 建立專案 打開 Lokalize -> 專案(P) -> 建立軟體翻譯專案 接著把 index.lokalize 取名為你想取的名字,並放在你能找得到的地方。 II. 設定專案 接著會跳出一堆東西,你只需要改以下幾個: - 一般 - ID: 把他改成你能識別的名字,例如「KDEtrunk」「KDEstable」 - 郵件論壇: 把他改成「l10n-tw@linux.org.tw」或其他郵件論壇,保留原本的亦可。 - 根目錄: 改成你想翻譯的分支,例如 trunk 或 stable 分支。 - 進階 - 分支目錄: 改成你想要同步的 PO 翻譯檔分支,例如 stable 或 trunk 分支。 - 其他翻譯目錄: 改成你目前的翻譯分支,例如 trunk 或 stable 分支。 未來只需開啟該專案檔,即可同步兩側的 PO 檔案。 == 聯絡團隊 == #l10n-tw IRC: #l10n-tw Telegram: @l10n-tw 郵件論壇: zh-l10n@linux.org.tw Index: trunk/l10n-support/zh_TW/sync_po_file.py =================================================================== --- trunk/l10n-support/zh_TW/sync_po_file.py (revision 1538625) +++ trunk/l10n-support/zh_TW/sync_po_file.py (revision 1538626) @@ -1,205 +1,216 @@ #!/usr/bin/python3 ''' -一個可以同步您語言目錄和 template 的應用程式。 +可以同步您語言目錄和範本的應用程式。 [注意] - 確保 template 與語言目錄是最新的 - 確保已經安裝 gettext-tools - 確保您系統有安裝 python3.7 或更新版本 ''' import sys, shutil, os +import argparse +import subprocess +import shlex # for subprocess + +argp = argparse.ArgumentParser( + description="同步 KDE SVN 檔案庫中範本與語言中的翻譯檔案", + epilog="請將問題回報至 ", + formatter_class=argparse.RawTextHelpFormatter +) + +argp.add_argument( + "template_path", + action="store", + type=str, + help="""\ +範本檔存放位置。 + +以 KDE 5 的應用程式翻譯檔範本為例,它通常會像這樣: +https://websvn.kde.org/trunk/l10n-kf5/templates/messages/ +""" +) +argp.add_argument( + "translation_path", + action="store", + type=str, + help="""\ +翻譯檔存放位置。 + +以 KDE 5 的應用程式翻譯檔 (zh_TW) 為例,它通常會像這樣: +https://websvn.kde.org/trunk/l10n-kf5/zh_TW/messages/ +""" +) +argp.add_argument( + "--merge", + action="store_true", + help="是否完整合併所有翻譯檔(使用範本檔作來源)?\n除非翻譯檔存放位置與範本脫節非常嚴重,否則不建議使用此功能。", + dest="mergeSwitch" +) -usage = ''' 同步範本與語言中的 PO 檔案 [for KDE] +argv = argp.parse_args() -作者:pan93412, 2019. -您可自由修改此處軟體,但請留下作者資訊。 - -用法:python3 {program_name} (範本位置) (目標語言位置) [--help] [--merge] -(註)範本與目標語言位置尾端請加 - 「/」(macOS / Linux) - 或「\」(Windows) - -[--help]: 顯示說明 -[--merge]: 自範本位置合併 po 檔案 -''' - -def string_replacer(string): - ''' - 將傳入的 string 進行修改, - 後回傳修改完的 string。 - ''' - strtmp = string - # 設定單複數變化 - strtmp = strtmp.replace("nplurals=INTEGER; plural=EXPRESSION;", "nplurals=1; plural=0;") - # 設定語言團隊語言 - strtmp = strtmp.replace("Language-Team: LANGUAGE", "Language-Team: Chinese") - # 設定語言 - strtmp = strtmp.replace(r"Language: \n", r"Language: zh_TW\n") - return strtmp - -wannaMerge = False def merge_translations(template, old): ''' 使用 msgmerge 工具合併翻譯。 template: 範本檔 old: 原翻譯檔 + 回傳值: msgmerge 執行的回傳值,預設為 0 ''' # -q 必須,否則會導致進度列無法正常運作。 - oldContent = "" - os.system(f"msgmerge -U --backup=none --previous -q {old} {template}") - with open(old, "r") as tmp: - oldContent = tmp.read() - newContent = string_replacer(oldContent) - with open(old, "w") as tmp: - tmp.write(newContent) - return + merge = subprocess.Popen(shlex.split(f"msgmerge -U --backup=none --previous -q {old} {template}")) + merge.wait() + + return merge.returncode def copy(source, target): ''' 將 source 複製到 target。 source: 來源檔案 target: 目標位置 + 回傳值: 一律為 None。 ''' shutil.copyfile(source, target) - return - -def check_argv(argv): - ''' - 檢查引數。 - - argv: 直接傳入 sys.argv 即可。 - argvlist: 引數動作列表。 - ''' - global wannaMerge - - theUsage = usage.format(program_name = argv[0]) - if len(argv) < 3 or len(argv) > 4: - print(theUsage) - exit(512) - for arg in argv: - if arg == "--help": - print(theUsage) - exit(0) - if arg == "--merge": - wannaMerge = True - return True, argv[1], argv[2] + return None def recursive(path): ''' 遞迴目錄,並回傳檔案列表。 傳入: path: 要遞迴的目錄 回傳:檔案列表與所有目錄列表。 ''' - fnlist = [] + filelist = [] dirlist = [] - - for dirbase, dirname, filename in os.walk(path): - for dn in dirname: - # 我知道這方法很爛,但最簡單呀( - # 假如 dirlist 沒有過這資料夾,且不在 .git 中 - if dirlist.count(dn) == 0 and dirbase.find(".git") == -1 and dn != ".git": - dirlist.append(os.path.join(dirbase, dn)) - - for fn in filename: - # 我知道這方法很爛,但最簡單呀( - # 假如檔案名稱有 ".po" 且不在 .git 目錄中 - if fn.find(".po") != -1 and dirbase.find(".git") == -1: - fnlist.append(os.path.join(dirbase, fn)) - - return fnlist, dirlist + # 要排除的檔案 / 目錄名稱 + excludes = [".git", ".svn"] + for dirbase, dirnames, filenames in os.walk(path): + for dirname in dirnames: + # 這迴圈將遞迴檢查以下項目: + # - 曾未出現在 dirlist 中。 + # + # 並將「dirbase/dirname」加入 dirlist 中。 + if dirlist.count(dirname) == 0: + dirlist.append(os.path.join(dirbase, dirname)) + + for filename in filenames: + # 此迴圈將遞迴檢查以下項目: + # - 曾未出現在 filelist 中。 + # - 檔名結尾必須是 ".po" 或 ".pot" + # + # 並將「dirbase/filename」加入 filelist 中。 + if ".po" in filename or ".pot" in filename and filelist.count(filename) == 0: + filelist.append(os.path.join(dirbase, filename)) + + for filename in filelist: + for exclude in excludes: + if exclude in filename: + filelist.remove(filename) + continue + + for dirname in dirlist: + for exclude in excludes: + if exclude in dirname: + dirlist.remove(dirname) + continue + + return filelist, dirlist symbol = "^" # 一開始的動畫 def symbolChanger(symbol): ''' 毫無意義的概念www symbol: 傳入 "." "-" "=" "^" 任一個,將會不斷重複這幾個動畫 ''' if symbol == "^": return "." elif symbol == ".": return "-" elif symbol == "-": return "=" elif symbol == "=": return "^" else: raise Exception("動畫好像未初始成功……") -def check_translate_file(templatedir, languagedir): +def determine_translationfile(templatedir, translationdir): ''' 檢查目前的翻譯檔案是要合併還是要複製範本檔 - templatedir: 範本目錄 - languagedir: 語言目錄 + templatedir: 範本目錄 + translationdir: 翻譯檔目錄 ''' global symbol # 把這裡的 symbol 提升到全域。 - # filenamelist_template - fnl_t, folders_t = recursive(templatedir) - # 如 Array 的起始數以 0 開始一樣,我打算也讓以下兩者以 0 開始。 - # 因此減 1。 - fnl_t_count = len(fnl_t) - 1 - folder_t_count = len(folders_t) - 1 - # filenamelist_language - fnl_l, folders_l = recursive(languagedir) - - print("\n[->] 開始檢查範本區的資料夾結構是否與目標語言資料夾架構相同……", flush=True) - # 檢查範本區的資料夾是否與目標語言區的資料夾相同。 - for t_folder in folders_t: - symbol = symbolChanger(symbol) - print("\r{1} 剩餘 {0} 個未處理目錄… ".format(folder_t_count, symbol, t_folder), end="", flush=True) - l_should_folder = t_folder.replace(templatedir, languagedir) - if os.path.exists(l_should_folder) == False: - os.mkdir(l_should_folder) # 建立資料夾 - print("\n[!] 已建立資料夾:" + l_should_folder) - folder_t_count -= 1 - + templateFileList, templateFolderList = recursive(templatedir) + # 處理最後一個檔案時,「剩餘 xx 數:[y]」的 [y] 我希望是 0,所以皆 -1。 + templateFileCount, templateFolderCount = len(templateFileList)-1, len(templateFolderList)-1 - print("\n[->] 開始更新目標語言資料夾的 PO 檔案……") + translationFileList, translationFolderList = recursive(translationdir) + + print("""\ +[->] 開始檢查範本資料夾數是否與翻譯檔的資料夾數相同…… + + 假如有一個資料夾範本有,但翻譯檔資料夾沒有,則會在翻譯檔資料夾建立同名資料夾。 + 假如有一個資料夾範本沒有,但翻譯檔資料夾有,則不進行任何動作。 +""") - for t_file in fnl_t: + # 開始檢查範本資料夾數是否與翻譯檔的資料夾數相同…… + for templateFolder in templateFolderList: symbol = symbolChanger(symbol) - t_currentfn = t_file.replace(".pot", ".po").replace(templatedir, languagedir) - print("\r{1} 剩餘 {0} 個未處理檔案… ".format(fnl_t_count, symbol), end="", flush=True) - if fnl_l.count(t_currentfn) == 0: - # 先複製檔案 - copy(t_file, t_currentfn) + print("\r{0} 剩餘 {1} 個未複製目錄…… ".format(symbol, templateFolderCount), end="", flush=True) + copyToDir = templateFolder.replace(templatedir, translationdir) + + if os.path.exists(copyToDir) == False: + os.mkdir(copyToDir) + print(f"\n[!] 已建立資料夾:{copyToDir}") - # 開始修正標頭 - oldContent = "" - with open(t_currentfn, "r") as tmp: - oldContent = tmp.read() - newContent = string_replacer(oldContent) - with open(t_currentfn, "w") as tmp: - tmp.write(newContent) - - print("\n[!] 已複製檔案:" + t_currentfn) - else: - if wannaMerge: - merge_translations(t_file, t_currentfn) - fnl_t_count -= 1 # 從列表中移除已經處理過的檔案 + templateFolderCount -= 1 + + print("""\ +\n[->] 開始更新翻譯檔資料夾中的翻譯檔案…… + + 假如有一個 PO 檔,翻譯檔資料夾沒有但範本資料夾卻有,則會使用 + msginit 來初始化範本檔中的 POT 檔,並複製到 + 翻譯檔資料夾。 + 假如有一個 PO 檔,範本和翻譯檔資料夾都有,且有傳入 --merge + 參數,則會使用 msgmerge 合併。 +""") - # 請比上一段文字長~空白得保留。 + + # 開始更新翻譯檔資料夾中的翻譯檔案…… + for templateFile in templateFileList: + symbol = symbolChanger(symbol) + print("\r{0} 剩餘 {1} 個未處理檔案…… ".format(symbol, templateFileCount), end="", flush=True) + + toProcessFile = templateFile.replace(templatedir, translationdir).replace(".pot", ".po") + + if os.path.exists(toProcessFile): + if argv.mergeSwitch == True: + merge_translations(templateFile, toProcessFile) + else: + copyPot = subprocess.Popen( + shlex.split(f"msginit --no-translator -o {toProcessFile} -i {templateFile}"), + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL + ) + copyPot.wait() + + if copyPot.returncode != 0: + print(f"\n[*] 警告:初始化 {templateFile} 至 {toProcessFile} 時發生錯誤……") + else: + print(f"\n[!] 已初始化 PO 檔:{toProcessFile}") + + templateFileCount -= 1 + + # 空白得保留。 print("\r處理完成。請再次檢查結果,以確保合併沒有任何錯誤! ") return -def main(): - isCorrect, template, language = check_argv(sys.argv) - - if check_argv(sys.argv): - check_translate_file(template, language) - else: - exit(2) - -if __name__ == "__main__": - main() -else: - print(f"此程式不建議當作模組使用,匯入的套件名稱:{__name__}") +determine_translationfile(argv.template_path, argv.translation_path)