""" * Version: 0.1.0.0.0 * Purposes: * This is a result-driven style English to Japanese translation tool which is better than static dictionary style, it blocks the wrong translation while still allows the translation flexibility for auto translator. * This is an internal utilities specifically for processing English to Japanese translation for pingcap.pe.flash module. * This tool prefers "ですます体", it's WIP. """ import re, csv, importlib_resources import pingcap.pe.flash.util as util import pingcap.pe.flash.util_translator as translator import pingcap.pe.flash.ai_editor as editor _linematch_replace_resultmatch_ci = [ e[1:] for e in [ *csv.reader( importlib_resources.open_text( "pingcap.pe.flash", "flash-en2ja-blocklist-dictionary-ci.csv" ) ) ] ] _linematch_replace_resultmatch_cs = [ e[1:] for e in [ *csv.reader( importlib_resources.open_text( "pingcap.pe.flash", "flash-en2ja-blocklist-dictionary-cs.csv" ) ) ] ] _errata_cs = [ e[1:] for e in [ *csv.reader( importlib_resources.open_text( "pingcap.pe.flash", "flash-en2ja-static-errata-cs.csv" ) ) ] ] def _line_lower_result_replace( line, result, line_match: str, result_match: str, result_replacement: str ) -> str: if line_match in line.lower() and result_match in result: return result.replace(result_match, result_replacement) else: return result def _line_lower_result_replace_multi( line, result, line_match_lower: str, results_match: list, result_replacement: str ) -> str: for r in results_match: result = _line_lower_result_replace( line, result, line_match_lower, r, result_replacement ) additional_result_match = [] additional_lower_result_match = line_match_lower.split(" ") # All lower case: additional_result_match += [" ".join(additional_lower_result_match)] # All title case: additional_result_match += [ " ".join(e.title() for e in additional_lower_result_match) ] if " " in line_match_lower: # Dash connected case: additional_line_match_lower = line_match_lower.replace(" ", "-") result = _line_lower_result_replace_multi( line, result, additional_line_match_lower, results_match, result_replacement ) # First word title case: additional_result_match += [ additional_lower_result_match[0].title() + " " + " ".join(additional_lower_result_match[1:]) ] for x in additional_result_match: result = _line_lower_result_replace( line, result, line_match_lower, x, result_replacement ) return result def _line_result_replace( line, result, line_match: str, result_match: str, result_replacement: str ) -> str: if line_match in line and result_match in result: return result.replace(result_match, result_replacement) else: return result def _result_replace(result, result_match: str, result_replacement: str) -> str: if result_match in result: return result.replace(result_match, result_replacement) else: return result def _translate_cache_en2ja(line: str) -> str: """Return the cached Markdown translated result for English Markdown to Japanese Markdown. Args: line (str): The English source Markdown line. Returns: str: The cached Markdown translation result. """ payload = line.strip("\n").strip() if payload in ["No speaker notes.", "[no notes]"]: return "スピーカーのノートはありません。" elif payload in ["## Assumptions", "## Assumption"]: return "## 前提条件" elif payload in ["### Assumptions", "### Assumption"]: return "### 前提条件" elif payload in ["## Overview", "## Introduction"]: return "## 概要" elif payload in ["### Overview", "### Introduction"]: return "### 概要" elif payload in ["## Notes", "## Note"]: return "## 注意" elif payload in ["### Notes", "### Note"]: return "### 注意" elif payload == "## Duration": return "## 所要時間" elif payload == "### Duration": return "### 所要時間" elif payload == "### Tasks": return "### タスク" elif payload in [ "## Solution Steps", "## Solution Step", "## Steps", "## Solutions", "## Solution", "## Step", ]: return "## 手順" elif payload in [ "### Solution Steps", "### Solution Step", "### Steps", "### Solutions", "### Solution", "### Step", ]: return "### 手順" elif payload in ["## Conclusion", "## Summary"]: return "## まとめ" elif re.match( "\+ This exercise should take you approximately [0-9]{1,} minutes to complete.", payload, ): payload = payload pattern = re.findall("[0-9]{1,}", payload) return "+ この演習は約" + pattern[0] + "分程度かかります。" elif ( payload == "+ Some of the steps require that you type commands in two different terminal windows. You might find it convenient to launch two terminals and keep them open for the duration of the exercises." ): return "+ いくつかのステップでは、2つの異なるターミナルウィンドウでコマンドを入力する必要があります。2つのターミナルを起動し、演習の間ずっと開いておくと便利でしょう。" elif ( payload == "+ Your environment may be different with these exercises, including but not limited to `IP`, `port`, `username`, `password` and `directory`." ): return "+ この演習で利用する、`IP`、`port`、`username`、`password`、`directory`などは、あなたの環境と異なる場合があります。" elif payload == "+ The `$` in your terminal window is an OS prompt.": return '+ ターミナルウィンドウの"`$`"とはOSプロンプトです。' elif payload == "+ The `$` in your terminal window is OS prompt.": return '+ ターミナルウィンドウの"`$`"とはOSプロンプトです。' elif payload == '+ The "`$`" in your terminal window is an OS prompt.': return '+ ターミナルウィンドウの"`$`"とはOSプロンプトです。' elif ( payload == '+ The "`tidb>`" or "`mysql>`" in your terminal window is SQL command prompt.' ): return '+ ターミナルウィンドウ内の "`tidb>`" または "`mysql>`" は、SQL コマンドプロンプトです。' elif ( payload == "+ These exercises can be run on a single machine, such as your laptop." ): return "+ これらの演習は、ノートパソコンなどの単一のマシン上で実行することができます。" elif payload == "+ You are using macOS or Linux.": return "+ macOSまたはLinuxを使用していること。" elif ( payload == "+ You have cloned the [lab repository](https://github.com/pingcap/tidb-course-201-lab.git)." ): return "+ [lab repository](https://github.com/pingcap/tidb-course-201-lab.git)をクローン済みであること。" elif ( payload == "+ You have [Git command line client](https://git-scm.com/) installed." ): return "+ [Gitコマンドラインクライアント](https://git-scm.com/)がインストールされていること。" elif ( payload == "+ You have [MySQL command line client](https://www.google.com/search?q=how+to+install+mysql+command+line+client) installed." ): return "+ [MySQLコマンドラインクライアント](https://www.google.com/search?q=how+to+install+mysql+command+line+client)をインストールされていること。" elif ( payload == "+ You have completed the Exercises - Creating a TiDB Cluster for Testing Purposes." ): return "+ 演習「テスト用にTiDBクラスタを作成する」を完了していること。" elif payload == "## Outline": return "## アウトライン" elif payload == "### Outline": return "### アウトライン" elif payload == "## Domain Details": return "## 詳細" elif payload == "## Response Types": return "## 回答タイプ" elif payload == "# Objectives": return "# 目標" elif payload == "# Introduction to TiDB": return "# TiDBの概要" elif payload == "# Introduction to TiKV": return "# TiKVの概要" elif payload == "# Introduction to Placement Driver": return "# Placement Driverの概要" elif payload == "# Introduction to TiFlash": return "# TiFlashの概要" elif payload == "# Introduction to Raft": return "# Raftの概要" elif payload == "## End of Exercises": return "## 演習終わり" return None def pre_en2ja_auto_translation_md_prefix_aware(line_in: str) -> str: """ This routine currently is NOT in-use. Args: line_in (str): The English line to be translated. Returns: str: The English line refined for translating into Japanese. """ processed_line = None processed_line = line_in return processed_line def post_en2ja_auto_translation( line: str, auto_translated_line: str, source_line: str ) -> str: """ This routine will be called in a context WITHOUT the Markdown prefix such as "#, +, -, *", etc. :param line: The English source line. :param auto_translated_line: The translated line by auto translation service. :returns: The post processed result. """ result = auto_translated_line.strip("\n") print(util.speak_result("Begin Post Handling for Input: " + result)) # 1. Pre-processing before ですます processing. Refine needed, always needed. result = result.replace("この文を日本語に翻訳すると 「", "") result = re.sub("」$", "", result) result = re.sub("」。$", "", result) # してください」となります if result.strip("\n").endswith("してください」となります。"): result = result.replace("してください」となります。", "します。") if result.strip("\n").endswith("してください」となります;"): result = result.replace("してください」となります;", "します;") if result.strip("\n").endswith("してください」となります"): result = result.replace("してください」となります", "します") # 始めましょう if result.strip("\n").endswith("始めましょう。"): result = result.replace("始めましょう。", "始めます。") if result.strip("\n").endswith("始めましょう;"): result = result.replace("始めましょう;", "始めます;") if result.strip("\n").endswith("始めましょう"): result = result.replace("始めましょう", "始めます") # れてください if result.strip("\n").endswith("れてください。"): result = result.replace("れてください。", "れます。") if result.strip("\n").endswith("れてください;"): result = result.replace("れてください;", "れます;") if result.strip("\n").endswith("れてください"): result = result.replace("れてください", "れます") # してください, してください。」 if result.strip("\n").endswith("してください。"): result = result.replace("してください。", "します。") if result.strip("\n").endswith("してください。」"): result = result.replace("してください。」", "します。」") if result.strip("\n").endswith("してください;"): result = result.replace("してください;", "します;") if result.strip("\n").endswith("してください"): result = result.replace("してください", "します") # みてください if result.strip("\n").endswith("みてください。"): result = result.replace("みてください。", "みます。") if result.strip("\n").endswith("みてください;"): result = result.replace("みてください;", "みます;") if result.strip("\n").endswith("みてください"): result = result.replace("みてください", "みます") # しましょう if result.strip("\n").endswith("しましょう。"): result = result.replace("しましょう。", "します。") if result.strip("\n").endswith("しましょう;"): result = result.replace("しましょう;", "します;") if result.strip("\n").endswith("しましょう"): result = result.replace("しましょう", "します") # できない if result.strip("\n").endswith("できない。"): result = result.replace("できない。", "できません。") if result.strip("\n").endswith("できない;"): result = result.replace("できない;", "できません;") # ない if result.strip("\n").endswith("ない。"): result = result.replace("ない。", "ありません。") if result.strip("\n").endswith("ない;"): result = result.replace("ない;", "ありません;") # んだ if result.strip("\n").endswith("んだ。"): result = result.replace("んだ。", "みました。") if result.strip("\n").endswith("んだ;"): result = result.replace("んだ;", "みました;") # しかった - something passed if result.strip("\n").endswith("しかった。"): result = result.replace("しかった。", "しかったです。") if result.strip("\n").endswith("しかった;"): result = result.replace("しかった;", "しかったです;") print(util.speak_result("Post Phase 1 Completed: " + result)) # 2. The general sentence ending special handlings for "ですます体". # End of 2. The general sentence ending special handlings for "ですます体". print(util.speak_result("Post Phase 2 Completed: " + result)) # 4. Correct case sensitive translation result by a static dictionary. # Load `line_match_cs, replacement, result_match_cs` from CSV. for e in _linematch_replace_resultmatch_cs[1:]: if not type(e[2]) == str: for i in e[2]: result = _line_result_replace(line, result, e[0], i, e[1]) else: result = _line_result_replace(line, result, e[0], e[2], e[1]) print(util.speak_result("Post Phase 4 Completed: " + result)) # 5. Correct case insensitive translation result. # Load `line_match_ci, replacement, result_match_cs` from CSV. for e in _linematch_replace_resultmatch_ci[1:]: result = _line_lower_result_replace_multi( line, result, e[0], e[2].split(","), e[1] ) print(util.speak_result("Post Phase 5 Completed: " + result)) # 6. Correct complex translation result. Goal keeper solutions for those issues that can not be solved in step 5 completely. if "use" in line.lower() and result.strip("\n").endswith("使え"): result = result.replace("使え", "使う") if ( "coprocessor" in line.lower() and "コプロセッサ" in result and "コプロセッサー" not in result ): result = result.replace("コプロセッサ", "コプロセッサー") if "server" in line.lower() and "サーバ" in result and "サーバー" not in result: result = result.replace("サーバ", "サーバー") if "layer" in line.lower() and "レイヤ" in result and "レイヤー" not in result: result = result.replace("レイヤ", "レイヤー") if ( "candidates" in line.lower() and "候補" in result and "候補者" not in result ): # Following up fixing for "candidates". result = result.replace("候補", "Candidates") if ( "candidate" in line.lower() and "候補" in result and "候補者" not in result ): # Following up fixing for "candidate". result = result.replace("候補", "Candidate") if ( "role" in line.lower() and "役" in result and "役割" not in result ): # Following up fixing for "role(s)". result = result.replace("役", "ロール") if "lock" in line.lower() and (not "key" in line.lower()): result = _line_lower_result_replace_multi( line, result, "lock", ["鍵"], "ロック" ) if "row" in line.lower() and (not "raw" in line.lower()): if ("load" in line.lower() or "road" in line.lower()) and "ロード" in result: result = result.replace("ロード", "X_LROAD_PH_X") if "global" in line.lower() and "グローバル" in result: result = result.replace("グローバル", "X_GLOBAL_PH_X") if "workflow" in line.lower() and "ワークフロー" in result: result = result.replace("ワークフロー", "X_WORKFLOW_PH_X") result = ( result.replace("ロー", "行") .replace("X_LROAD_PH_X", "ロード") .replace("X_GLOBAL_PH_X", "グローバル") .replace("X_WORKFLOW_PH_X", "グローバル") ) if "tidb/tikv/pd" in line.lower(): result = re.sub("tidb/tikv/pd", "TiDB/TiKV/PD", result, flags=re.IGNORECASE) print(util.speak_result("Post Phase 6 Completed: " + result)) print(util.speak_result("Post Phase 6.1 Completed: " + line + " => " + result)) # 7. REMOVERS: # Remove "それは、" for "It is " prefixed bullet. if result.strip(" ").startswith("+") and line.strip(" ").strip("+ ").startswith( "It is " ): result = result.replace("それは、", "") # Remove the unwanted generated ** if "**" in result and "**" not in line: result = result.replace("**", "") # Remove the unwanted generated "「" and "」" if ( not line.startswith('"') and result.startswith("「") and not line.endswith('"') and result.endswith("」") ): result = result.strip("「") result = result.strip("」") # Remove the unwanted "(...)" if not "(" in source_line and not ")" in source_line: result = re.sub("\(.*\)", "", result) # Remove the unwanted generated "を日本語に翻訳します" result = result.replace("を日本語に翻訳します", "") result = result.replace(":「私は日本語を勉強しています。", "") # Remove the unwanted "です" at the end if re.match("^`[^`]+` *です[。]*$", result): result = result.replace("です", "") # Remove the unwanted "の" after `PARAMETER::` result = result.replace("`PARAMETER::` の ", "`PARAMETER::` ") print(util.speak_result("Post Phase 7 Completed: " + result)) return result def post_en2ja_auto_translation_md_prefix_aware(in_line: str, source_line: str) -> str: """ This routine will be called in a context with the Markdown prefix such as "#, +, -, *", etc. """ result = in_line # Remove あなたは result = result.replace("あなたは", "") # Remove あなたの result = result.replace("あなたの", "") if result.strip().startswith("+"): result = result.strip("。").strip("?") if result.strip().startswith("#"): result = result.strip("。").strip("?") if result.strip().startswith("["): result = result.strip("。").strip("?") if not "(" in source_line and not ")" in source_line: result = re.sub("\(.*\)", "", result) if not '"' in source_line: result = result.replace('"', "") return result def translate_en2ja_md_line( line: str, en_1gram_list: list, mode="standard", md4ppt=True, md_file_path=None, is_srt=False, desumasu=True, ) -> str: """ Translate markdown line from EN to JA. Here goes the logic: 0. Translation cache. 1. Short-cut the line in case translation is not needed. 2. Pack the prefixed whitespaces from the line, for example: `_____## Hello world` 3. Pack the prefixed `#`, `+`, `-`, `+`, `*`, `$numbering` (whitespace included) from the line. 4. Replace the inline code line - `CODE` with placeholders. Translate the processed line. 5. Put the inline code line back. 6. Post translation handling. 7-13. Unpack `#`, `+`, `-`, `+`, `*`, `$numbering` and prefixed whitespaces back to the line. """ # Source print(util.speak_result("SOURCE: [ " + line + " ]")) # Pre-process line = pre_en2ja_auto_translation_md_prefix_aware(line) print(util.speak_result("AFTER PRE-PROCESS MD-AWARE: [ " + line + " ]")) print(util.speak_result("BEFORE TRANSLATION: [ " + line + " ]")) # 0. Translation cache. cache_result = _translate_cache_en2ja(line) if cache_result != None: print(util.speak_result("Translate from cache: " + cache_result)) return cache_result # 0.1 Handle MD image. if translator.is_md_image(line): return translator.handle_image(line, "ja") # 1. Do nothing: Shortcut. if translator.is_skippable(line): for e in _errata_cs[1:]: line = _result_replace(line, e[0], e[1]) return line # Assign `line` to `compress_space_raw` to keep the line intact. compress_space_raw = line.strip("\n") # 2. Pack the prefixed whitespace prefix_whitespace_pack = translator.pack_prefix_whitespace(compress_space_raw) compress_space_raw = prefix_whitespace_pack["processed_line"] # 2.1 Markdown table processing if translator.is_table_row(line.strip("\n")): compress_space_raw = translator.translate_table_row( compress_space_raw, translate_en2ja_md_line, en_1gram_list ) print(util.speak_result("Table row translated: " + compress_space_raw)) else: # 3.1 Pack the prefixed hash prefix_hash_pack = translator.pack_prefix_hash(compress_space_raw) compress_space_raw = prefix_hash_pack["processed_line"] # 3.2 Pack the prefixed plus prefix_plus_pack = translator.pack_prefix_plus(compress_space_raw) compress_space_raw = prefix_plus_pack["processed_line"] # 3.3 Pack the prefixed minus prefix_minus_pack = translator.pack_prefix_minus(compress_space_raw) compress_space_raw = prefix_minus_pack["processed_line"] # 3.4 Pack the prefixed star prefix_star_pack = translator.pack_prefix_star(compress_space_raw) compress_space_raw = prefix_star_pack["processed_line"] # 3.5 Pack the prefixed number prefix_number_pack = translator.pack_prefix_number(compress_space_raw) compress_space_raw = prefix_number_pack["processed_line"] payload = compress_space_raw # 3.6 Do nothing: Shortcut. if not translator.is_skippable(compress_space_raw): # 4. Pack inline codeline inline_codeline_pack = translator.pack_inline_codeline(compress_space_raw) compress_space_raw = inline_codeline_pack["processed_line"] # Ready for translation before_translation = compress_space_raw.strip() # Covert to lowercase before translation as title. if ( prefix_hash_pack["count"] > 0 ): # If the line is title line, convert it to lower case. before_translation = before_translation.lower() # Translate (for SRT? should be desumasu or not?) if not is_srt: translated_line = editor.translate_en2ja_by_aws(before_translation) if desumasu: if prefix_hash_pack["count"] == 0 and not translator.is_ja_desumasu( translated_line ): print( util.speak_result( "Converting to ですます体: " + translated_line ) ) translated_line = editor.convert_ja_to_desumasu(translated_line) elif prefix_hash_pack["count"] > 0 and translator.is_ja_desumasu( translated_line ): print( util.speak_result( "Converting to 体言止め: " + translated_line ) ) translated_line = editor.convert_ja_to_taigendome( translated_line ) else: # srt uses ですます体 translated_line = editor.translate_en2ja_by_aws_for_srt( before_translation ) if desumasu: if not translator.is_ja_desumasu(translated_line): print( util.speak_result( "Converting to ですます体: " + translated_line ) ) translated_line = editor.convert_ja_to_desumasu(translated_line) # Not too formal. compress_space_raw = translated_line.strip() compress_space_raw = re.sub("いたします", "します", compress_space_raw) print( util.speak_result( "BEFORE OPENAI TRANSLATION: [ " + before_translation + " ]" ) ) print( util.speak_result( "AFTER OPENAI TRANSLATION: [ " + translated_line.strip() + " ]" ) ) compress_space_raw = translated_line.strip() # 5. Inline code handling compress_space_raw = translator.unpack_inline_codeline( compress_space_raw, inline_codeline_pack ) # Cut-off the whitespaces if any compress_space_raw = compress_space_raw.strip() # 6. Post translate print( util.speak_result( "BEFORE POST-TRANSLATION: [ " + compress_space_raw + " ]" ) ) compress_space_raw = post_en2ja_auto_translation( before_translation, compress_space_raw, payload ) print( util.speak_result( "AFTER POST-TRANSLATION: [ " + compress_space_raw + " ]" ) ) # 7. Adding Hash title prefix back compress_space_raw = translator.unpack_prefix_hash( compress_space_raw, prefix_hash_pack, en_1gram_list, md4ppt ) # 8. Adding plus prefix back compress_space_raw = translator.unpack_prefix_common( compress_space_raw, prefix_plus_pack ) # 9. Adding minus prefix back compress_space_raw = translator.unpack_prefix_common( compress_space_raw, prefix_minus_pack ) # 10. Adding star prefix back compress_space_raw = translator.unpack_prefix_common( compress_space_raw, prefix_star_pack ) # 11. Adding number prefix back compress_space_raw = translator.unpack_prefix_common( compress_space_raw, prefix_number_pack ) # 12. Give back the prefix whitespace compress_space_raw = translator.unpack_prefix_common( compress_space_raw, prefix_whitespace_pack ) # 13. Correct the general results in all cases. for e in _errata_cs[1:]: compress_space_raw = _result_replace(compress_space_raw, e[0], e[1]) # Finally print(util.speak_result("AFTER TRANSLATION: [ " + compress_space_raw + " ]")) if md4ppt: compress_space_raw = post_en2ja_auto_translation_md_prefix_aware( compress_space_raw, line ) print( util.speak_result( "AFTER TRANSLATION MD-AWARE: [ " + compress_space_raw + " ]" ) ) return compress_space_raw