From ae5803ed8ca3395760f580f638c169ef74215471 Mon Sep 17 00:00:00 2001 From: josix Date: Sun, 25 Oct 2026 00:14:01 +0800 Subject: [PATCH 1/6] Resolve fuzzy entries in library/dataclasses.po and faq/library.po MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-translate all fuzzy entries (94 in library/dataclasses.po, 51 in faq/library.po) against the current msgids and drop the fuzzy flags. Many of them were outdated machine translations that no longer matched the source, had broken reST roles, or only covered part of the msgid. Terminology was checked against existing translated entries with the translation-memory lookup, e.g. 僅限關鍵字 (keyword-only), 哨兵 (sentinel), 淺複製 (shallow copy), 弱參照 (weak reference) and 互斥鎖 (mutex). "generate" is rendered as 產生 throughout dataclasses.po, matching the dominant usage in the repo. Only msgstr blocks changed; msgid lines are untouched. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01H2AUU3pZYEkfs9BY3mm7bn --- faq/library.po | 346 +++++++++++------------- library/dataclasses.po | 584 ++++++++++++++++++----------------------- 2 files changed, 400 insertions(+), 530 deletions(-) diff --git a/faq/library.po b/faq/library.po index a69d64d7287..1148a6225c4 100644 --- a/faq/library.po +++ b/faq/library.po @@ -61,7 +61,6 @@ msgid "Where is the math.py (socket.py, regex.py, etc.) source file?" msgstr "哪裡可以找到 math.py (socket.py, regex.py, 等...) 來源檔案?" #: ../../faq/library.rst:30 -#, fuzzy msgid "" "If you can't find a source file for a module it may be a built-in or " "dynamically loaded module implemented in C, C++ or other compiled language. " @@ -69,9 +68,9 @@ msgid "" "like :file:`mathmodule.c`, somewhere in a C source directory (not on the " "Python Path)." msgstr "" -"如果找不到模組的源檔案,它可能是用 C、C++ 或其他編譯語言實作的內置或動態載入" -"的模組。在這種情況下,你可能沒有源檔案,或者它可能類似" -"於 :file:`mathmodule.c`,位於 C 源目錄中(不在 Python 路徑中)。" +"如果找不到模組的原始檔,它可能是以 C、C++ 或其他編譯式語言實作的內建模組或動" +"態載入模組。在這種情況下,你可能沒有原始檔,或者它可能是類似 " +":file:`mathmodule.c` 的檔案,位於某個 C 原始碼目錄中(不在 Python 路徑上)。" #: ../../faq/library.rst:35 msgid "There are (at least) three kinds of modules in Python:" @@ -105,14 +104,13 @@ msgid "How do I make a Python script executable on Unix?" msgstr "我如何使 Python script 執行在 Unix?" #: ../../faq/library.rst:49 -#, fuzzy msgid "" "You need to do two things: the script file's mode must be executable and the " "first line must begin with ``#!`` followed by the path of the Python " "interpreter." msgstr "" -"你需要作兩件事:腳本程式必須可以被執行而且第一行必須 ``#!`` 開頭後面接上 " -"Python 直譯器的路徑。" +"你需要做兩件事:腳本檔案的模式必須是可執行的,而且第一行必須以 ``#!`` 開頭," +"後面接著 Python 直譯器的路徑。" #: ../../faq/library.rst:53 msgid "" @@ -139,16 +137,15 @@ msgid "" msgstr "作為檔案的第一行,使用 Python 直譯器安裝在你的平台上的路徑名稱。" #: ../../faq/library.rst:64 -#, fuzzy msgid "" "If you would like the script to be independent of where the Python " "interpreter lives, you can use the :program:`env` program. Almost all Unix " "variants support the following, assuming the Python interpreter is in a " "directory on the user's :envvar:`PATH`::" msgstr "" -"如果你希望腳本獨立於 Python 直譯器所在的位置,你可以使用 :program:`env` 程" -"式。幾乎所有 Unix 變體都支援以下內容,假設 Python 直譯器位於使用者" -"的 :envvar:`PATH` 上的目錄中: ::" +"如果你希望腳本不受 Python 直譯器所在位置的影響,可以使用 :program:`env` 程式" +"。假設 Python 直譯器位於使用者 :envvar:`PATH` 中的某個目錄,幾乎所有 Unix 變" +"體都支援以下寫法: ::" #: ../../faq/library.rst:69 msgid "#!/usr/bin/env python" @@ -164,14 +161,13 @@ msgstr "" "此你需要使用直譯器的實際絕對路徑名稱。" #: ../../faq/library.rst:75 -#, fuzzy msgid "" "Occasionally, a user's environment is so full that the :program:`/usr/bin/" "env` program fails; or there's no env program at all. In that case, you can " "try the following hack (due to Alex Rezinsky):" msgstr "" -"有時,使用者的環境太滿以至於:program:`/usr/bin/env` 程式失敗;或者根本就沒有 " -"env 程式。在這種情況下,你可以嘗試以下 hack(由於 Alex Rezinsky):" +"偶爾,使用者的環境變數多到讓 :program:`/usr/bin/env` 程式執行失敗,或者根本沒" +"有 env 程式。在這種情況下,你可以試試以下技巧(出自 Alex Rezinsky):" #: ../../faq/library.rst:79 msgid "" @@ -202,19 +198,17 @@ msgid "Is there a curses/termcap package for Python?" msgstr "是否有適用於 Python 的 curses/termcap 套件?" #: ../../faq/library.rst:98 -#, fuzzy msgid "" "For Unix variants: The standard Python source distribution comes with a " "curses module in the :source:`Modules` subdirectory, though it's not " "compiled by default. (Note that this is not available in the Windows " "distribution -- there is no curses module for Windows.)" msgstr "" -"對於 Unix 變體:標準 Python 源程式碼分發版在 :source:`Modules` 子目錄中附帶一" -"個 curses 模組,但預設情況下未編譯它。(請注意,這在 Windows 發行版中不可用——" -"沒有適用於 Windows 的 curses 模組。)" +"對於 Unix 變體:標準的 Python 原始碼發行版在 :source:`Modules` 子目錄中附有 " +"curses 模組,但預設不會編譯它。(請注意,Windows 發行版中沒有這個模組 —— " +"Windows 上沒有 curses 模組。)" #: ../../faq/library.rst:103 -#, fuzzy msgid "" "The :mod:`curses` module supports basic curses features as well as many " "additional functions from ncurses and SYSV curses such as colour, " @@ -223,9 +217,9 @@ msgid "" "but there don't seem to be any currently maintained OSes that fall into this " "category." msgstr "" -":mod:`curses` 模組支援基本的 curses 功能以及 ncurses 和 SYSV curses 的許多附" -"加功能,例如顏色、替代字元集支援、鍵盤和鼠標支援。這意味著該模組與僅具有 BSD " -"curses 的作業系統不相容,但似乎沒有任何目前維護的作業系統屬於此類型。" +":mod:`curses` 模組支援基本的 curses 功能,以及許多來自 ncurses 和 SYSV " +"curses 的額外功能,例如顏色、替代字元集支援、pad 和滑鼠支援。這表示該模組與只" +"有 BSD curses 的作業系統不相容,但目前似乎沒有仍在維護的作業系統屬於這一類。" #: ../../faq/library.rst:111 msgid "Is there an equivalent to C's onexit() in Python?" @@ -272,14 +266,13 @@ msgid "How do I test a Python program or component?" msgstr "如何測試 Python 程式或元件?" #: ../../faq/library.rst:137 -#, fuzzy msgid "" "Python comes with two testing frameworks. The :mod:`doctest` module finds " "examples in the docstrings for a module and runs them, comparing the output " "with the expected output given in the docstring." msgstr "" -"Python 帶有兩個測試框架。 :mod:`doctest` 模組在模組的文件字串中查詢示例並運行" -"它們,將輸出與文件字串中給出的預期輸出進行比較。" +"Python 附帶兩個測試框架。:mod:`doctest` 模組會在模組的說明字串中尋找範例並執" +"行它們,再將輸出與說明字串中給出的預期輸出進行比較。" #: ../../faq/library.rst:141 msgid "" @@ -290,7 +283,6 @@ msgstr "" "模型。" #: ../../faq/library.rst:144 -#, fuzzy msgid "" "To make testing easier, you should use good modular design in your program. " "Your program should have almost all functionality encapsulated in either " @@ -300,15 +292,14 @@ msgid "" "avoid depending on mutating global variables, since this makes testing much " "more difficult to do." msgstr "" -"為了使測試更容易,你應該在程式中使用良好的模組化設計。你的程式應該將幾乎所有" -"功能都封裝在函式或類別方法中——這有時會產生使程式運行得更快的令人驚訝和令人愉" -"快的效果(因為局部變數存取比全域存取更快)。此外,該程式應避免依賴於可變的全" -"局變數,因為這會使測試變得更加困難。" +"為了讓測試更容易,你應該在程式中採用良好的模組化設計。你的程式應該把幾乎所有" +"功能都封裝在函式或類別方法中 —— 這有時會帶來令人驚喜的效果:讓程式執行得更快" +"(因為存取區域變數比存取全域變數更快)。此外,程式應避免依賴對全域變數的修改" +",因為這會讓測試變得困難許多。" #: ../../faq/library.rst:152 -#, fuzzy msgid "The \"global main logic\" of your program may be as simple as ::" -msgstr "你程式的「全域主邏輯」可能像一樣簡單: ::" +msgstr "你程式的「全域主邏輯」可能簡單到只是 ::" #: ../../faq/library.rst:154 msgid "" @@ -319,12 +310,10 @@ msgstr "" " main_logic()" #: ../../faq/library.rst:157 -#, fuzzy msgid "at the bottom of the main module of your program." -msgstr "在你的程式主模組的底端。" +msgstr "寫在你程式主模組的最底部。" #: ../../faq/library.rst:159 -#, fuzzy msgid "" "Once your program is organized as a tractable collection of function and " "class behaviours, you should write test functions that exercise the " @@ -335,18 +324,17 @@ msgid "" "the \"production code\", since this makes it easy to find bugs and even " "design flaws earlier." msgstr "" -"一旦你的程式被組織為函式和類別行為的易於處理的集合,你就應該編寫測試函式來執" -"行這些行為。可將一系列測試自動化的測試套件與每個模組相關聯。這聽起來像是很多" -"工作,但由於 Python 如此簡潔和靈活,所以它非常容易。透過與 \"生產程式碼\" 並" -"行編寫測試函式,你可以使編碼變得更加愉快和有趣,因為這使得更早地發現錯誤甚至" -"設計缺陷變得容易。" +"當你的程式被組織成一組易於處理的函式與類別行為之後,就應該撰寫測試函式來檢驗" +"這些行為。每個模組都可以搭配一個能自動執行一系列測試的測試套件。這聽起來工作" +"量很大,但由於 Python 非常簡潔且靈活,實際上出乎意料地容易。如果你在撰寫「正" +"式程式碼」的同時也撰寫測試函式,就能更早發現錯誤甚至設計缺陷,讓寫程式變得更" +"愉快、更有趣。" #: ../../faq/library.rst:167 -#, fuzzy msgid "" "\"Support modules\" that are not intended to be the main module of a program " "may include a self-test of the module. ::" -msgstr "不打算成為程式主要模組的 \"支援模組\" 可能包括模組的自檢: ::" +msgstr "不打算作為程式主模組的「支援模組」可以包含該模組的自我測試。 ::" #: ../../faq/library.rst:170 msgid "" @@ -357,35 +345,32 @@ msgstr "" " self_test()" #: ../../faq/library.rst:173 -#, fuzzy msgid "" "Even programs that interact with complex external interfaces may be tested " "when the external interfaces are unavailable by using \"fake\" interfaces " "implemented in Python." msgstr "" -"即使是與復雜外部介面交互的程式也可以在外部介面不可用時透過使用 Python 中實作" -"的 \"假\" 介面進行測試。" +"即使是與複雜外部介面互動的程式,在外部介面無法使用時,也可以透過以 Python 實" +"作的「假」介面來進行測試。" #: ../../faq/library.rst:179 msgid "How do I create documentation from doc strings?" msgstr "如何從文件字串建立文件?" #: ../../faq/library.rst:181 -#, fuzzy msgid "" "The :mod:`pydoc` module can create HTML from the doc strings in your Python " "source code. An alternative for creating API documentation purely from " "docstrings is `epydoc `_. `Sphinx `_ can also include docstring content." msgstr "" -":mod:`pydoc` 模組可以從 Python 源程式碼中的文件字串建立 HTML。純粹從文件字串" -"建立 API 文件的另一種方法是 `epydoc `_。 " -"`Sphinx `_ 也可以包含文件字串內容。" +":mod:`pydoc` 模組可以從你的 Python 原始碼中的說明字串產生 HTML。另一個純粹從" +"說明字串產生 API 文件的工具是 `epydoc `_。" +"`Sphinx `_ 也可以納入說明字串的內容。" #: ../../faq/library.rst:188 -#, fuzzy msgid "How do I get a single keypress at a time?" -msgstr "我如何一次獲得一個按鍵?" +msgstr "如何一次取得一個按鍵?" #: ../../faq/library.rst:190 msgid "" @@ -404,28 +389,26 @@ msgid "How do I program using threads?" msgstr "如何使用執行緒編寫程式?" #: ../../faq/library.rst:239 -#, fuzzy msgid "" "Be sure to use the :mod:`threading` module and not the :mod:`_thread` " "module. The :mod:`threading` module builds convenient abstractions on top of " "the low-level primitives provided by the :mod:`_thread` module." msgstr "" -"請務必使用 :mod:`threading` 模組而不是 :mod:`_thread` 模" -"組。 :mod:`threading` 模組在 :mod:`_thread` 模組提供的低階原語之上構建方便的" -"抽象。" +"請務必使用 :mod:`threading` 模組,而不是 :mod:`_thread` 模組。" +":mod:`threading` 模組在 :mod:`_thread` 模組所提供的低階基本操作之上,建構了方" +"便使用的抽象層。" #: ../../faq/library.rst:245 msgid "None of my threads seem to run: why?" msgstr "我的執行緒似乎都沒有運行:為什麼?" #: ../../faq/library.rst:247 -#, fuzzy msgid "" "As soon as the main thread exits, all threads are killed. Your main thread " "is running too quickly, giving the threads no time to do any work." msgstr "" -"一旦主執行緒退出,所有執行緒都會被殺死。你的主執行緒運行得太快,執行緒沒有時" -"間做任何工作。" +"主執行緒一結束,所有執行緒都會被終止。你的主執行緒執行得太快,導致其他執行緒" +"沒有時間做任何工作。" #: ../../faq/library.rst:250 msgid "" @@ -461,15 +444,14 @@ msgstr "" "time.sleep(10) # <---------------------------!" #: ../../faq/library.rst:265 -#, fuzzy msgid "" "But now (on many platforms) the threads don't run in parallel, but appear to " "run sequentially, one at a time! The reason is that the OS thread scheduler " "doesn't start a new thread until the previous thread is blocked." msgstr "" -"但是現在(在許多平台上)執行緒不是並行運行的,而是看起來是順序運行的,一次一" -"個!原因是作業系統執行緒調度程式在前一個執行緒被阻塞之前不會啟動一個新執行" -"緒。" +"但現在(在許多平台上)這些執行緒並不是平行執行,而是看起來依序執行,一次一個" +"!原因是作業系統的執行緒排程器要等到前一個執行緒被阻塞後,才會啟動新的執行緒" +"。" #: ../../faq/library.rst:269 msgid "A simple fix is to add a tiny sleep to the start of the run function::" @@ -500,7 +482,6 @@ msgstr "" "time.sleep(10)" #: ../../faq/library.rst:282 -#, fuzzy msgid "" "Instead of trying to guess a good delay value for :func:`time.sleep`, it's " "better to use some kind of semaphore mechanism. One idea is to use " @@ -508,26 +489,24 @@ msgid "" "token to the queue when it finishes, and let the main thread read as many " "tokens from the queue as there are threads." msgstr "" -"與其嘗試為 :func:`time.sleep` 猜測一個好的延遲值,不如使用某種訊號量機制。一" -"種想法是使用 :mod:`queue` 模組建立一個隊列物件,讓每個執行緒在完成時向隊列新" -"增一個權杖,並讓主執行緒從隊列中讀取與執行緒數一樣多的權杖。" +"與其嘗試為 :func:`time.sleep` 猜一個合適的延遲值,不如使用某種號誌(" +"semaphore)機制。一個做法是使用 :mod:`queue` 模組建立一個佇列物件,讓每個執行" +"緒在完成時向佇列加入一個記號,然後讓主執行緒從佇列中讀取與執行緒數量相同的記" +"號。" #: ../../faq/library.rst:290 -#, fuzzy msgid "How do I parcel out work among a bunch of worker threads?" -msgstr "我如何在一堆工作執行緒中分配工作?" +msgstr "如何將工作分配給一群工作執行緒?" #: ../../faq/library.rst:292 -#, fuzzy msgid "" "The easiest way is to use the :mod:`concurrent.futures` module, especially " "the :mod:`~concurrent.futures.ThreadPoolExecutor` class." msgstr "" -"最簡單的方法是使用 :mod:`concurrent.futures` 模組,尤其" -"是 :mod:`~concurrent.futures.ThreadPoolExecutor` 類別。" +"最簡單的方法是使用 :mod:`concurrent.futures` 模組,尤其是 " +":mod:`~concurrent.futures.ThreadPoolExecutor` 類別。" #: ../../faq/library.rst:295 -#, fuzzy msgid "" "Or, if you want fine control over the dispatching algorithm, you can write " "your own logic manually. Use the :mod:`queue` module to create a queue " @@ -536,10 +515,10 @@ msgid "" "``.get()`` method to return them. The class will take care of the locking " "necessary to ensure that each job is handed out exactly once." msgstr "" -"或者,如果你想對調度演算法進行精細控制,你可以手動編寫自己的邏輯。使" -"用 :mod:`queue` 模組建立一個包含作業列表的隊列。 :class:`~queue.Queue` 類別維" -"護一個物件列表,並有一個 `.put(obj)`` 方法將項目新增到隊列和一個 ``.get()`` " -"方法回傳它們。該類別將負責必要的鎖定,以確保每個作業都恰好分發一次。" +"或者,如果你想精細控制分派演算法,也可以自己手動撰寫邏輯。使用 :mod:`queue` " +"模組建立一個存放工作清單的佇列。:class:`~queue.Queue` 類別維護一個物件串列," +"並提供將項目加入佇列的 ``.put(obj)`` 方法,以及取出項目的 ``.get()`` 方法。這" +"個類別會處理必要的鎖定,確保每項工作都恰好只被分派一次。" #: ../../faq/library.rst:302 msgid "Here's a trivial example::" @@ -619,20 +598,17 @@ msgstr "" "..." #: ../../faq/library.rst:358 -#, fuzzy msgid "" "Consult the module's documentation for more details; " "the :class:`~queue.Queue` class provides a featureful interface." msgstr "" -"有關更多詳細資訊,請參閱模組的文件; :class:`~queue.Queue` 類別提供了一個功能" -"強大的介面。" +"更多細節請參閱該模組的文件;:class:`~queue.Queue` 類別提供了功能豐富的介面。" #: ../../faq/library.rst:363 msgid "What kinds of global value mutation are thread-safe?" msgstr "什麼樣的全域值變化是執行緒安全的?" #: ../../faq/library.rst:365 -#, fuzzy msgid "" "A :term:`global interpreter lock` (GIL) is used internally to ensure that " "only one thread runs in the Python VM at a time. In general, Python offers " @@ -641,32 +617,30 @@ msgid "" "instruction and therefore all the C implementation code reached from each " "instruction is therefore atomic from the point of view of a Python program." msgstr "" -"內部使用 :term:`全域直譯器鎖 (GIL, global interpreter lock)`\\ 來確保一次只有" -"一個執行緒在 Python VM 中運行。通常,Python 僅提供位元組碼指令之間的執行緒切" -"換;可以透過 :func:`sys.setswitchinterval` 設定它切換的頻率。因此,從 Python " -"程式的角度來看,每條位元組碼指令以及從每條指令到達的所有 C 實作程式碼都是原子" -"的。" +"Python 內部使用\\ :term:`全域直譯器鎖 `\\ (GIL) 來確" +"保同一時間只有一個執行緒在 Python VM 中執行。一般來說,Python 只會在位元組碼" +"指令之間切換執行緒;切換的頻率可以透過 :func:`sys.setswitchinterval` 設定。因" +"此,從 Python 程式的角度來看,每一條位元組碼指令,以及每條指令所執行到的所有 " +"C 實作程式碼,都是不可分割的(atomic)。" #: ../../faq/library.rst:372 -#, fuzzy msgid "" "In theory, this means an exact accounting requires an exact understanding of " "the PVM bytecode implementation. In practice, it means that operations on " "shared variables of built-in data types (ints, lists, dicts, etc) that " "\"look atomic\" really are." msgstr "" -"從理論上講,這意味著準確的記賬需要對 PVM 位元組碼實作有準確的理解。實際上,這" -"意味著對 \"看起來原子\" 的內置資料型別(整數、列表、字典等)的共享變數的操作" -"確實是原子的。" +"理論上,這表示要精確判斷就必須確切了解 PVM 位元組碼的實作。實務上,這表示對內" +"建資料型別(int、list、dict 等)的共享變數所做的那些「看起來不可分割」的操作" +",確實是不可分割的。" #: ../../faq/library.rst:377 -#, fuzzy msgid "" "For example, the following operations are all atomic (L, L1, L2 are lists, " "D, D1, D2 are dicts, x, y are objects, i, j are ints)::" msgstr "" -"例如,以下操作都是原子的(L、L1、L2 是列表,D、D1、D2 是字典,x、y 是物件," -"i、j 是整數): ::" +"例如,以下操作都是不可分割的(L、L1、L2 為 list,D、D1、D2 為 dict,x、y 為物" +"件,i、j 為 int): ::" #: ../../faq/library.rst:380 msgid "" @@ -711,32 +685,30 @@ msgstr "" "D[x] = D[x] + 1" #: ../../faq/library.rst:399 -#, fuzzy msgid "" "Operations that replace other objects may invoke those other " "objects' :meth:`~object.__del__` method when their reference count reaches " "zero, and that can affect things. This is especially true for the mass " "updates to dictionaries and lists. When in doubt, use a mutex!" msgstr "" -"替換其他物件的操作可能會在引用計數達到零時叫用其他物件的 :meth:`__del__` 方" -"法,這可能會影響事情。對於字典和列表的大量更新尤其如此。如有疑問,請使用互斥" -"體!" +"會替換其他物件的操作,可能會在那些物件的參照計數歸零時呼叫它們的 " +":meth:`~object.__del__` 方法,而這可能會造成影響。對 dict 和 list 進行大量更" +"新時尤其如此。有疑慮時,請使用互斥鎖(mutex)!" #: ../../faq/library.rst:406 msgid "Can't we get rid of the Global Interpreter Lock?" msgstr "不能擺脫全域直譯器鎖嗎?" #: ../../faq/library.rst:408 -#, fuzzy msgid "" "The :term:`global interpreter lock` (GIL) is often seen as a hindrance to " "Python's deployment on high-end multiprocessor server machines, because a " "multi-threaded Python program effectively only uses one CPU, due to the " "insistence that (almost) all Python code can only run while the GIL is held." msgstr "" -":term:`global interpreter lock` (GIL) 通常被視為 Python 在高階多處理器伺服器" -"機器上部署的障礙,因為多執行緒 Python 程式實際上只使用一個 CPU,因為堅持(幾" -"乎)所有 Python 程式碼只能在持有 GIL 的情況下運行。" +":term:`全域直譯器鎖 `\\ (GIL) 常被視為在高階多處理器" +"伺服器上部署 Python 的阻礙,因為(幾乎)所有 Python 程式碼都只能在持有 GIL 時" +"執行,導致多執行緒的 Python 程式實際上只會使用一顆 CPU。" #: ../../faq/library.rst:413 msgid "" @@ -751,7 +723,6 @@ msgid "" msgstr "" #: ../../faq/library.rst:422 -#, fuzzy msgid "" "The current work to remove the GIL is based on a `fork of Python 3.9 with " "the GIL removed `_ by Sam Gross. Prior " @@ -765,14 +736,16 @@ msgid "" "compensate for the removal of the GIL. The Python 3.9 fork is the first " "attempt at removing the GIL with an acceptable performance impact." msgstr "" -"回到 Python 1.5 時代,Greg Stein 實際上實作了一個全面的補丁集( \"自由執行緒" -"\" 補丁),刪除了 GIL 並用細粒度鎖定取而代之。 Adam Olsen 最近在他的 `python-" -"safethread `_ 項目中做了" -"一個類似的實驗。不幸的是,這兩個實驗都表現出單執行緒性能的急劇下降(至少慢了 " -"30%),這是由於需要大量的細粒度鎖定來補償 GIL 的移除。" +"目前移除 GIL 的工作是以 Sam Gross 的 `移除了 GIL 的 Python 3.9 分支 " +"`_\\ 為基礎。在此之前,早在 Python 1.5 時" +"代,Greg Stein 就曾實作一套完整的修補程式(即「自由執行緒」修補程式),移除 " +"GIL 並改以細粒度鎖定取代。Adam Olsen 也在他的 `python-safethread " +"`_ 專案中做過類似的實驗" +"。遺憾的是,這兩個早期實驗的單執行緒效能都大幅下降(至少慢了 30%),原因是需" +"要大量的細粒度鎖定來彌補 GIL 的移除。這個 Python 3.9 分支是第一個在可接受的效" +"能影響下移除 GIL 的嘗試。" #: ../../faq/library.rst:437 -#, fuzzy msgid "" "The presence of the GIL in current Python releases doesn't mean that you " "can't make good use of Python on multi-CPU machines! You just have to be " @@ -782,14 +755,13 @@ msgid "" "doing so; the :mod:`multiprocessing` module provides a lower-level API in " "case you want more control over dispatching of tasks." msgstr "" -"這並不意味著你不能在多 CPU 機器上用好 Python!你只需要創造性地將工作分配給多" -"個*行程*而​​不是多個*執行緒*。新的 :mod:`concurrent.futures` 模組中" -"的 :class:`~concurrent.futures.ProcessPoolExecutor` 類別提供了一種簡單的方" -"法; :mod:`multiprocessing` 模組提供了一個較低階別的 API,以防你希望更好地控" -"制任務的調度。" +"目前的 Python 版本中存在 GIL,並不表示你無法在多 CPU 機器上善用 Python!你只" +"需要發揮創意,把工作分配給多個\\ *行程*\\ 而不是多個\\ *執行緒*。新的 " +":mod:`concurrent.futures` 模組中的 " +":class:`~concurrent.futures.ProcessPoolExecutor` 類別提供了簡便的做法;如果你" +"想對任務分派有更多控制,:mod:`multiprocessing` 模組則提供了較低階的 API。" #: ../../faq/library.rst:446 -#, fuzzy msgid "" "Judicious use of C extensions will also help; if you use a C extension to " "perform a time-consuming task, the extension can release the GIL while the " @@ -797,9 +769,9 @@ msgid "" "work done. Some standard library modules such as :mod:`zlib` " "and :mod:`hashlib` already do this." msgstr "" -"明智地使用 C 擴充也會有所幫助;如果你使用 C 擴充來執行耗時任務,則該擴充可以" -"在執行執行緒在 C 程式碼中時釋放 GIL,並允許其他執行緒完成一些工作。一些標準函" -"式庫模組,例如 :mod:`zlib` 和 :mod:`hashlib` 已經這樣做了。" +"審慎地使用 C 擴充也有幫助;如果你使用 C 擴充來執行耗時的任務,該擴充可以在執" +"行緒執行 C 程式碼期間釋放 GIL,讓其他執行緒得以完成一些工作。一些標準函式庫模" +"組,例如 :mod:`zlib` 和 :mod:`hashlib`,已經這麼做了。" #: ../../faq/library.rst:452 msgid "" @@ -826,19 +798,17 @@ msgid "How do I delete a file? (And other file questions...)" msgstr "如何刪除檔案?(以及其他檔案問題...)" #: ../../faq/library.rst:467 -#, fuzzy msgid "" "Use ``os.remove(filename)`` or ``os.unlink(filename)``; for documentation, " "see the :mod:`os` module. The two functions are " "identical; :func:`~os.unlink` is simply the name of the Unix system call for " "this function." msgstr "" -"使用 ``os.remove(filename)`` 或 ``os.unlink(filename)``;有關文件,請參" -"閱 :mod:`os` 模組。這兩個功能是相同的; :func:`~os.unlink` 只是這個函式的 " -"Unix 系統呼叫的名稱。" +"使用 ``os.remove(filename)`` 或 ``os.unlink(filename)``;相關文件請參閱 " +":mod:`os` 模組。這兩個函式完全相同;:func:`~os.unlink` 只是這個功能在 Unix 系" +"統呼叫中的名稱。" #: ../../faq/library.rst:471 -#, fuzzy msgid "" "To remove a directory, use :func:`os.rmdir`; use :func:`os.mkdir` to create " "one. ``os.makedirs(path)`` will create any intermediate directories in " @@ -846,27 +816,26 @@ msgid "" "directories as long as they're empty; if you want to delete an entire " "directory tree and its contents, use :func:`shutil.rmtree`." msgstr "" -"要刪除目錄,請使用 :func:`os.rmdir`;使用 :func:`os.mkdir` 建立一個。 " -"``os.makedirs(path)`` 將在 ``path`` 中建立任何不存在的中間目錄。 " -"``os.removedirs(path)`` 將刪除中間目錄,只要它們是空的;如果要刪除整個目錄樹" -"及其內容,請使用 :func:`shutil.rmtree`。" +"要刪除目錄,請使用 :func:`os.rmdir`;要建立目錄則使用 :func:`os.mkdir`。" +"``os.makedirs(path)`` 會建立 ``path`` 中所有不存在的中間目錄。" +"``os.removedirs(path)`` 會在中間目錄為空的情況下將它們刪除;如果你想刪除整個" +"目錄樹及其內容,請使用 :func:`shutil.rmtree`。" #: ../../faq/library.rst:477 msgid "To rename a file, use ``os.rename(old_path, new_path)``." msgstr "要重新命名檔案,請使用 ``os.rename(old_path, new_path)``。" #: ../../faq/library.rst:479 -#, fuzzy msgid "" "To truncate a file, open it using ``f = open(filename, \"rb+\")``, and use " "``f.truncate(offset)``; offset defaults to the current seek position. " "There's also ``os.ftruncate(fd, offset)`` for files opened " "with :func:`os.open`, where *fd* is the file descriptor (a small integer)." msgstr "" -"要截斷一個檔案,使用``f = open(filename, \"rb+\")``打開它,然後使用" -"``f.truncate(offset)``;偏移量預設為目前搜尋位置。對於使用 :func:`os.open` 打" -"開的檔案,還有 ``os.ftruncate(fd, offset)``,其中 *fd* 是檔案描述器(一個小整" -"數)。" +"要截斷檔案,請用 ``f = open(filename, \"rb+\")`` 開啟它,然後使用 " +"``f.truncate(offset)``;offset 預設為目前的搜尋位置。對於以 :func:`os.open` " +"開啟的檔案,也可以使用 ``os.ftruncate(fd, offset)``,其中 *fd* 是檔案描述器(" +"一個小整數)。" #: ../../faq/library.rst:484 msgid "" @@ -883,7 +852,6 @@ msgid "How do I copy a file?" msgstr "如何複製檔案?" #: ../../faq/library.rst:492 -#, fuzzy msgid "" "The :mod:`shutil` module contains a :func:`~shutil.copyfile` function. Note " "that on Windows NTFS volumes, it does not copy `alternate data streams " @@ -893,35 +861,34 @@ msgid "" "permissions and metadata, though using :func:`shutil.copy2` instead will " "preserve most (though not all) of it." msgstr "" -":mod:`shutil` 模組包含一個 :func:`~shutil.copyfile` 函式。請注意,在 Windows " -"NTFS 卷上,它不會複製 `alternate data streams `_ 也不會複製 `resource forks `__ 在 macOS HFS+ 卷上,儘管現在兩者都很" -"少使用。它也不會複製檔案權限和元資料,儘管使用 :func:`shutil.copy2` 會保留其" -"中的大部分(儘管不是全部)。" +":mod:`shutil` 模組包含 :func:`~shutil.copyfile` 函式。請注意,它不會複製 " +"Windows NTFS 磁碟區上的 `替代資料串流 " +"`_,也不會複" +"製 macOS HFS+ 磁碟區上的 `資源分支 " +"`__,不過這兩者現在都很少使用。" +"它也不會複製檔案權限和詮釋資料,但改用 :func:`shutil.copy2` 可以保留其中大部" +"分(但不是全部)。" #: ../../faq/library.rst:503 msgid "How do I read (or write) binary data?" msgstr "如何讀取(或寫入)二進位制資料?" #: ../../faq/library.rst:505 -#, fuzzy msgid "" "To read or write complex binary data formats, it's best to use " "the :mod:`struct` module. It allows you to take a string containing binary " "data (usually numbers) and convert it to Python objects; and vice versa." msgstr "" -"要讀取或寫入複雜的二進制資料格式,最好使用 :mod:`struct` 模組。它允許你取得包" -"含二進制資料(通常是數字)的字串並將其轉換為 Python 物件;反之亦然。" +"要讀寫複雜的二進位資料格式,最好使用 :mod:`struct` 模組。它可以讓你將包含二進" +"位資料(通常是數字)的字串轉換成 Python 物件,反之亦然。" #: ../../faq/library.rst:509 -#, fuzzy msgid "" "For example, the following code reads two 2-byte integers and one 4-byte " "integer in big-endian format from a file::" msgstr "" -"例如,以下程式碼從一個檔案中以大端格式讀取兩個 2 位元組整數和一個 4 位元組整" -"數: ::" +"例如,以下程式碼從檔案中以大端序(big-endian)格式讀取兩個 2 位元組整數和一個" +" 4 位元組整數: ::" #: ../../faq/library.rst:512 msgid "" @@ -938,42 +905,38 @@ msgstr "" " x, y, z = struct.unpack(\">hhl\", s)" #: ../../faq/library.rst:518 -#, fuzzy msgid "" "The '>' in the format string forces big-endian data; the letter 'h' reads " "one \"short integer\" (2 bytes), and 'l' reads one \"long integer\" (4 " "bytes) from the string." msgstr "" -"格式字串中的 \">\" 強制使用大端資料;字母 'h' 讀取一個 \"短整數\" (2 位元" -"組), 'l' 從字串中讀取一個 \"長整數\" (4 位元組)。" +"格式字串中的 '>' 強制使用大端序資料;字母 'h' 從字串中讀取一個「短整數」(2 " +"位元組),'l' 則讀取一個「長整數」(4 位元組)。" #: ../../faq/library.rst:522 -#, fuzzy msgid "" "For data that is more regular (e.g. a homogeneous list of ints or floats), " "you can also use the :mod:`array` module." msgstr "" -"對於更規則的資料(例如,整數或浮點數的同類列表),你還可以使用 :mod:`array` " -"模組。" +"對於更規律的資料(例如由 int 或 float 組成的同質串列),也可以使用 " +":mod:`array` 模組。" #: ../../faq/library.rst:527 -#, fuzzy msgid "" "To read and write binary data, it is mandatory to open the file in binary " "mode (here, passing ``\"rb\"`` to :func:`open`). If you use ``\"r\"`` " "instead (the default), the file will be open in text mode and ``f.read()`` " "will return :class:`str` objects rather than :class:`bytes` objects." msgstr "" -"要讀取和寫入二進位資料,必須以二進位模式打開檔案(這裡,將 ``\"rb\"`` 傳遞" -"給 :func:`open`)。如果你改用 ``\"r\"``\\ (預設設定),檔案將以文本模式打開,並" -"且 ``f.read()`` 將回傳 :class:`str` 物件而不是 :class:`bytes` 物件。" +"要讀寫二進位資料,必須以二進位模式開啟檔案(此處是將 ``\"rb\"`` 傳給 " +":func:`open`)。如果你改用 ``\"r\"``\\ (預設值),檔案會以文字模式開啟,而 " +"``f.read()`` 會回傳 :class:`str` 物件而不是 :class:`bytes` 物件。" #: ../../faq/library.rst:535 msgid "I can't seem to use os.read() on a pipe created with os.popen(); why?" msgstr "我似乎無法在用 os.popen() 建立的 pipe 上使用 os.read();為什麼?" #: ../../faq/library.rst:537 -#, fuzzy msgid "" ":func:`os.read` is a low-level function which takes a file descriptor, a " "small integer representing the opened file. :func:`os.popen` creates a high-" @@ -981,10 +944,10 @@ msgid "" "function. Thus, to read *n* bytes from a pipe *p* created " "with :func:`os.popen`, you need to use ``p.read(n)``." msgstr "" -":func:`os.read` 是一個低階函式,它接受一個檔案描述器,一個代表打開檔案的小整" -"數。 :func:`os.popen` 建立一個高階檔案物件,與內置的 :func:`open` 函式回傳的" -"型別相同。因此,要從使用 :func:`os.popen` 建立的管道 *p* 中讀取 *n* 個位元" -"組,你需要使用 ``p.read(n)``。" +":func:`os.read` 是一個低階函式,它接受一個檔案描述器,也就是代表已開啟檔案的" +"小整數。:func:`os.popen` 會建立一個高階檔案物件,其型別與內建 :func:`open` 函" +"式回傳的相同。因此,要從以 :func:`os.popen` 建立的管道 *p* 讀取 *n* 個位元組" +",你需要使用 ``p.read(n)``。" #: ../../faq/library.rst:545 msgid "How do I access the serial (RS232) port?" @@ -1018,7 +981,6 @@ msgstr "" "Python :term:`檔案物件 `\\ 是低階 C 檔案描述器的高階抽象層。" #: ../../faq/library.rst:562 -#, fuzzy msgid "" "For most file objects you create in Python via the built-in :func:`open` " "function, ``f.close()`` marks the Python file object as being closed from " @@ -1026,31 +988,31 @@ msgid "" "descriptor. This also happens automatically in ``f``'s destructor, when " "``f`` becomes garbage." msgstr "" -"對於你透過內置的 :func:`open` 函式在 Python 中建立的大多數檔案物件," -"``f.close()`` 從 Python 的角度將 Python 檔案物件標記為已關閉,並安排關閉底層 " -"C 檔案描述器。當 ``f`` 變成垃圾時,這也會自動發生在 ``f`` 的析構函式中。" +"對於你在 Python 中透過內建 :func:`open` 函式建立的大多數檔案物件," +"``f.close()`` 會從 Python 的角度將該 Python 檔案物件標記為已關閉,並安排關閉" +"底層的 C 檔案描述器。當 ``f`` 變成垃圾時,``f`` 的解構函式也會自動執行這件事" +"。" #: ../../faq/library.rst:568 -#, fuzzy msgid "" "But stdin, stdout and stderr are treated specially by Python, because of the " "special status also given to them by C. Running ``sys.stdout.close()`` " "marks the Python-level file object as being closed, but does *not* close the " "associated C file descriptor." msgstr "" -"但是 stdin、stdout 和 stderr 被 Python 特殊對待,因為 C 也賦予它們特殊的狀" -"態。運行 ``sys.stdout.close()`` 將 Python 級檔案物件標記為已關閉,但是 * " -"not* 關閉關聯的 C 檔案描述器。" +"但 stdin、stdout 和 stderr 會被 Python 特別處理,因為 C 也賦予了它們特殊的地" +"位。執行 ``sys.stdout.close()`` 會將 Python 層級的檔案物件標記為已關閉,但\\ " +"*不會*\\ 關閉對應的 C 檔案描述器。" #: ../../faq/library.rst:573 -#, fuzzy msgid "" "To close the underlying C file descriptor for one of these three, you should " "first be sure that's what you really want to do (e.g., you may confuse " "extension modules trying to do I/O). If it is, use :func:`os.close`::" msgstr "" -"要關閉這三個之一的底層 C 檔案描述器,你應該首先確定這是你真正想要做的(例如," -"你可能會混淆試圖執行 I/O 的擴充模組)。如果是,使用 :func:`os.close`: ::" +"要關閉這三者之一的底層 C 檔案描述器,你應該先確定這真的是你想做的事(例如,你" +"可能會讓嘗試執行 I/O 的擴充模組出錯)。如果確定要這麼做,請使用 " +":func:`os.close`: ::" #: ../../faq/library.rst:577 msgid "" @@ -1075,14 +1037,13 @@ msgid "What WWW tools are there for Python?" msgstr "Python 有哪些 WWW 工具?" #: ../../faq/library.rst:590 -#, fuzzy msgid "" "See the chapters titled :ref:`internet` and :ref:`netdata` in the Library " "Reference Manual. Python has many modules that will help you build server-" "side and client-side web systems." msgstr "" -"請參閱函式庫參考手冊中標題為 :ref:`internet` 和 :ref:`netdata` 的章節。 " -"Python 有許多模組可以幫助你構建伺服器端和用戶端 Web 系統。" +"請參閱函式庫參考手冊中標題為 :ref:`internet` 和 :ref:`netdata` 的章節。" +"Python 有許多模組可以幫助你建構伺服器端和用戶端的 Web 系統。" #: ../../faq/library.rst:596 msgid "" @@ -1113,13 +1074,12 @@ msgid "Use the standard library module :mod:`smtplib`." msgstr "使用標準函式庫模組 :mod:`smtplib`。" #: ../../faq/library.rst:614 -#, fuzzy msgid "" "Here's a very simple interactive mail sender that uses it. This method will " "work on any host that supports an SMTP listener. ::" msgstr "" -"這是一個使用它的非常簡單的交互式郵件發件人。此方法適用於任何支援 SMTP 偵聽器" -"的主機。: ::" +"以下是一個使用它的非常簡單的互動式郵件寄送程式。這個方法適用於任何支援 SMTP " +"監聽器的主機。 ::" #: ../../faq/library.rst:617 msgid "" @@ -1195,7 +1155,6 @@ msgid "" msgstr ":mod:`select` 模組通常用於幫助處理 socket 上的非同步 I/O。" #: ../../faq/library.rst:659 -#, fuzzy msgid "" "To prevent the TCP connect from blocking, you can set the socket to non-" "blocking mode. Then when you do the :meth:`~socket.socket.connect`, you " @@ -1205,13 +1164,13 @@ msgid "" "return different values, so you're going to have to check what's returned on " "your system." msgstr "" -"為防止 TCP 連接阻塞,可以將 socket 設定為非阻塞模式。然後當你執" -"行 :meth:`socket.connect` 時,你要麼立即連接(不太可能),要麼得到一個例外," -"其中包含錯誤號 ``.errno``。 ``errno.EINPROGRESS`` 表示連接正在進行中,但尚未" -"完成。不同的作業系統將回傳不同的值,因此你將不得不檢查系統回傳的內容。" +"為了避免 TCP 連線阻塞,你可以將 socket 設為非阻塞模式。這樣當你呼叫 " +":meth:`~socket.socket.connect` 時,要不是立即連線成功(不太可能),就是會得到" +"一個例外,其中的錯誤代碼存放在 ``.errno``。``errno.EINPROGRESS`` 表示連線正在" +"進行中,但尚未完成。不同的作業系統會回傳不同的值,所以你必須檢查你的系統會回" +"傳什麼。" #: ../../faq/library.rst:667 -#, fuzzy msgid "" "You can use the :meth:`~socket.socket.connect_ex` method to avoid creating " "an exception. It will just return the errno value. To poll, you can " @@ -1219,10 +1178,10 @@ msgid "" "``errno.EISCONN`` indicate that you're connected -- or you can pass this " "socket to :meth:`select.select` to check if it's writable." msgstr "" -"你可以使用 :meth:`socket.connect_ex` 方法來避免建立例外。它只會回傳 errno " -"值。要輪詢,你可以稍後再次呼叫 :meth:`socket.connect_ex` - ``0`` 或 " -"``errno.EISCONN`` 表示你已連接 - 或者你可以將此 socket 傳遞" -"給 :meth:`select.select` 檢查它是否可寫。" +"你可以使用 :meth:`~socket.socket.connect_ex` 方法來避免產生例外,它只會回傳 " +"errno 值。若要輪詢,你可以稍後再次呼叫 :meth:`~socket.socket.connect_ex` —— " +"回傳 ``0`` 或 ``errno.EISCONN`` 表示你已經連線 —— 或者你可以將這個 socket 傳" +"給 :meth:`select.select` 來檢查它是否可寫入。" #: ../../faq/library.rst:675 msgid "" @@ -1248,25 +1207,23 @@ msgid "Yes." msgstr "有的。" #: ../../faq/library.rst:690 -#, fuzzy msgid "" "Interfaces to disk-based hashes such as :mod:`DBM ` and :mod:`GDBM " "` are also included with standard Python. There is also " "the :mod:`sqlite3` module, which provides a lightweight disk-based " "relational database." msgstr "" -"基於磁碟的雜湊介面,例如 :mod:`DBM ` 和 :mod:`GDBM ` 也包" -"含在標準 Python 中。還有 :mod:`sqlite3` 模組,它提供了一個輕量級的基於磁碟的" -"關係資料庫。" +"標準 Python 也內含以磁碟為基礎的雜湊介面,例如 :mod:`DBM ` 和 " +":mod:`GDBM `。另外還有 :mod:`sqlite3` 模組,它提供了一個輕量級、以磁" +"碟為基礎的關聯式資料庫。" #: ../../faq/library.rst:695 -#, fuzzy msgid "" "Support for most relational databases is available. See the " "`DatabaseProgramming wiki page `_ for details." msgstr "" -"支援大多數關係資料庫。有關詳細資訊,請參閱 `DatabaseProgramming 維基頁面 " +"大多數關聯式資料庫都有支援。詳情請參閱 `DatabaseProgramming wiki 頁面 " "`_。" #: ../../faq/library.rst:701 @@ -1274,16 +1231,15 @@ msgid "How do you implement persistent objects in Python?" msgstr "你如何在 Python 中實作持久性物件?" #: ../../faq/library.rst:703 -#, fuzzy msgid "" "The :mod:`pickle` library module solves this in a very general way (though " "you still can't store things like open files, sockets or windows), and " "the :mod:`shelve` library module uses pickle and (g)dbm to create persistent " "mappings containing arbitrary Python objects." msgstr "" -":mod:`pickle` 函式庫模組以一種非常通用的方式解決了這個問題(儘管你仍然不能存" -"儲諸如打開的檔案、socket 或窗口之類的東西),而 :mod:`shelve` 函式庫模組使用 " -"pickle 和 (g) dbm 建立包含任意 Python 物件的持久對映。" +":mod:`pickle` 函式庫模組以非常通用的方式解決了這個問題(不過你仍然無法儲存像" +"已開啟的檔案、socket 或視窗這類東西),而 :mod:`shelve` 函式庫模組則使用 " +"pickle 和 (g)dbm 來建立包含任意 Python 物件的持久性對映。" #: ../../faq/library.rst:710 msgid "Mathematics and Numerics" diff --git a/library/dataclasses.po b/library/dataclasses.po index dcfc903aa6f..d62113f6e5c 100644 --- a/library/dataclasses.po +++ b/library/dataclasses.po @@ -24,24 +24,22 @@ msgid "**Source code:** :source:`Lib/dataclasses.py`" msgstr "**原始碼:**\\ :source:`Lib/dataclasses.py`" #: ../../library/dataclasses.rst:14 -#, fuzzy msgid "" "This module provides a decorator and functions for automatically adding " "generated :term:`special methods ` such as :meth:`~object." "__init__` and :meth:`~object.__repr__` to user-defined classes. It was " "originally described in :pep:`557`." msgstr "" -"該模組提供了一個裝飾器和函式,用於自動新增生成的\\ :term:`特殊方法 `,例如 :meth:`~object.__init__` 和 :meth:`~object.__repr__` 到使用者" -"定義的類別。它最初在 :pep:`557` 中描述。" +"此模組提供了一個裝飾器和一些函式,用來自動為使用者定義的類別加入產生的\\ " +":term:`特殊方法 `,例如 :meth:`~object.__init__` 和 " +":meth:`~object.__repr__`。它最初在 :pep:`557` 中提出。" #: ../../library/dataclasses.rst:19 -#, fuzzy msgid "" "The member variables to use in these generated methods are defined using :" "pep:`526` type annotations. For example, this code::" msgstr "" -"在這些生成的方法中使用的成員變數是使用 :pep:`526` 型別註釋定義的。例如,這段" +"這些產生的方法所使用的成員變數,是透過 :pep:`526` 型別註釋來定義的。例如以下" "程式碼: ::" #: ../../library/dataclasses.rst:22 @@ -60,9 +58,8 @@ msgid "" msgstr "" #: ../../library/dataclasses.rst:34 -#, fuzzy msgid "will add, among other things, a :meth:`!__init__` that looks like::" -msgstr "將新增,除其他事項外,一個 :meth:`!__init__` 看起來像: ::" +msgstr "除了其他東西之外,還會加入一個如下所示的 :meth:`!__init__`: ::" #: ../../library/dataclasses.rst:36 msgid "" @@ -79,45 +76,41 @@ msgstr "" " self.quantity_on_hand = quantity_on_hand" #: ../../library/dataclasses.rst:41 -#, fuzzy msgid "" "Note that this method is automatically added to the class: it is not " "directly specified in the :class:`!InventoryItem` definition shown above." msgstr "" -"請注意,此方法會自動新增到類別中:它不會在上面顯示的 :class:`!InventoryItem` " -"定義中直接指定。" +"請注意,這個方法是自動加入類別的:它並沒有直接寫在上面的 " +":class:`!InventoryItem` 定義中。" #: ../../library/dataclasses.rst:47 msgid "Module contents" msgstr "模組內容" #: ../../library/dataclasses.rst:51 -#, fuzzy msgid "" "This function is a :term:`decorator` that is used to add generated :term:" "`special methods ` to classes, as described below." msgstr "" -"此函式是一個 :term:`decorator`,用於將生成的\\ :term:`特殊方法 `\\新增到類別中,如下所述。" +"此函式是一個\\ :term:`裝飾器 `,用來將產生的\\ :term:`特殊方法 " +"`\\ 加入類別中,詳見下文。" #: ../../library/dataclasses.rst:54 -#, fuzzy msgid "" "The ``@dataclass`` decorator examines the class to find ``field``\\s. A " "``field`` is defined as a class variable that has a :term:`type annotation " "`. With two exceptions described below, nothing in " "``@dataclass`` examines the type specified in the variable annotation." msgstr "" -"``@dataclass`` 裝飾器檢查類別以找到 ``field``\\s。 ``field`` 被定義為具有 :" -"term:`type annotation ` 的類別變數。除了下面描述的兩個例" -"外,``@dataclass`` 中沒有任何內容檢查變數註釋中指定的型別。" +"``@dataclass`` 裝飾器會檢查類別以找出 ``field``\\ 。``field`` 的定義是具有\\ " +":term:`型別註釋 `\\ 的類別變數。除了下面描述的兩個例外," +"``@dataclass`` 完全不會檢查變數註釋中指定的型別。" #: ../../library/dataclasses.rst:60 -#, fuzzy msgid "" "The order of the fields in all of the generated methods is the order in " "which they appear in the class definition." -msgstr "所有生成的方法中欄位的順序是它們在類別定義中出現的順序。" +msgstr "在所有產生的方法中,欄位的順序就是它們在類別定義中出現的順序。" #: ../../library/dataclasses.rst:63 msgid "" @@ -132,14 +125,13 @@ msgstr "" "同一個類別;不會建立新類別。" #: ../../library/dataclasses.rst:69 -#, fuzzy msgid "" "If ``@dataclass`` is used just as a simple decorator with no parameters, it " "acts as if it has the default values documented in this signature. That is, " "these three uses of ``@dataclass`` are equivalent::" msgstr "" -"如果 ``@dataclass`` 僅用作不帶參數的簡單裝飾器,它的行為就好像它具有此簽名中" -"記錄的預設值一樣。也就是說,``@dataclass`` 的這三種用法是等價的: ::" +"如果 ``@dataclass`` 只是當作不帶參數的簡單裝飾器使用,它的行為就如同使用此簽" +"名中記載的預設值。也就是說,以下三種 ``@dataclass`` 的用法是等價的: ::" #: ../../library/dataclasses.rst:74 msgid "" @@ -179,7 +171,7 @@ msgstr "``@dataclass`` 的參數是:" msgid "" "*init*: If true (the default), a :meth:`~object.__init__` method will be " "generated." -msgstr "*init*:如果為真(預設值),將生成一個 :meth:`~object.__init__` 方法。" +msgstr "*init*:如果為真(預設值),將產生一個 :meth:`~object.__init__` 方法。" #: ../../library/dataclasses.rst:92 msgid "" @@ -187,7 +179,6 @@ msgid "" msgstr "如果該類別已經定義了 :meth:`!__init__`,則此參數會被忽略。" #: ../../library/dataclasses.rst:95 -#, fuzzy msgid "" "*repr*: If true (the default), a :meth:`~object.__repr__` method will be " "generated. The generated repr string will have the class name and the name " @@ -196,10 +187,10 @@ msgid "" "example: ``InventoryItem(name='widget', unit_price=3.0, " "quantity_on_hand=10)``." msgstr "" -"*repr*:如果為真(預設值),將生成一個 :meth:`__repr__` 方法。生成的 repr 字" -"串將包含類別名稱以及每個欄位的名稱和 repr,按照它們在類別中定義的順序排列。不" -"包括標記為從 repr 中排除的欄位。例如:``InventoryItem(name='widget', " -"unit_price=3.0, quantity_on_hand=10)``。" +"*repr*:如果為真(預設值),將會產生 :meth:`~object.__repr__` 方法。產生的 " +"repr 字串會包含類別名稱,以及每個欄位的名稱和 repr,順序與它們在類別中定義的" +"順序相同。被標記為不納入 repr 的欄位不會包含在內。例如:" +"``InventoryItem(name='widget', unit_price=3.0, quantity_on_hand=10)``。" #: ../../library/dataclasses.rst:102 msgid "" @@ -210,7 +201,7 @@ msgstr "如果該類別已經定義了 :meth:`!__repr__`,則此參數會被忽 msgid "" "*eq*: If true (the default), an :meth:`~object.__eq__` method will be " "generated." -msgstr "*eq*:如果為真(預設值),將生成一個 :meth:`~object.__eq__` 方法。" +msgstr "*eq*:如果為真(預設值),將產生一個 :meth:`~object.__eq__` 方法。" #: ../../library/dataclasses.rst:108 msgid "" @@ -245,7 +236,6 @@ msgid "" msgstr "" #: ../../library/dataclasses.rst:126 -#, fuzzy msgid "" "*order*: If true (the default is ``False``), :meth:`~object.__lt__`, :meth:" "`~object.__le__`, :meth:`~object.__gt__`, and :meth:`~object.__ge__` methods " @@ -253,10 +243,10 @@ msgid "" "fields, in order. Both instances in the comparison must be of the identical " "type. If *order* is true and *eq* is false, a :exc:`ValueError` is raised." msgstr "" -"*order*:如果為真(預設為 ``False``),:meth:`~object.__lt__`、:meth:" -"`~object.__le__`、:meth:`~object.__gt__` 和 :meth:`~object.__ge__` 方法將是產" -"生。它們按順序比較類別,就好像它是其欄位的元組一樣。比較中的兩個實例必須屬於" -"同一型別。如果 *order* 為真且 *eq* 為假,則會引發 :exc:`ValueError`。" +"*order*:如果為真(預設為 ``False``),將會產生 :meth:`~object.__lt__`、" +":meth:`~object.__le__`、:meth:`~object.__gt__` 和 :meth:`~object.__ge__` 方法" +"。這些方法會把類別當成由其欄位依序組成的 tuple 來比較。比較的兩個實例必須是完" +"全相同的型別。如果 *order* 為真而 *eq* 為假,則會引發 :exc:`ValueError`。" #: ../../library/dataclasses.rst:133 msgid "" @@ -267,18 +257,17 @@ msgstr "" "meth:`!__ge__` 中的任何一個,則引發 :exc:`TypeError`。" #: ../../library/dataclasses.rst:137 -#, fuzzy msgid "" "*unsafe_hash*: If true, force ``dataclasses`` to create a :meth:`~object." "__hash__` method, even though it may not be safe to do so. Otherwise, " "generate a :meth:`~object.__hash__` method according to how *eq* and " "*frozen* are set. The default value is ``False``." msgstr "" -"*unsafe_hash*:如果 ``False``\\ (預設值),將根據 *eq* 和 *frozen* 的設定生" -"成一個 :meth:`~object.__hash__` 方法。" +"*unsafe_hash*:如果為真,會強制 ``dataclasses`` 建立 " +":meth:`~object.__hash__` 方法,即使這樣做可能並不安全。否則會依據 *eq* 和 " +"*frozen* 的設定來產生 :meth:`~object.__hash__` 方法。預設值為 ``False``。" #: ../../library/dataclasses.rst:143 -#, fuzzy msgid "" ":meth:`!__hash__` is used by built-in :meth:`hash`, and when objects are " "added to hashed collections such as dictionaries and sets. Having a :meth:`!" @@ -287,13 +276,12 @@ msgid "" "and behavior of :meth:`!__eq__`, and the values of the *eq* and *frozen* " "flags in the ``@dataclass`` decorator." msgstr "" -":meth:`!__hash__` 由內建的 :meth:`hash` 使用,當物件被新增到雜湊集合(如字典" -"和集合)時。擁有 :meth:`!__hash__` 意味著該類別的實例是不可變的。可變性是一個" -"複雜的屬性,它取決於程序設計師的意圖 :meth:`!__eq__` 的存在和行為,以及 " -"dataclass 裝飾器中的 *eq* 和 *frozen* 旗標的值." +"內建的 :meth:`hash` 會使用 :meth:`!__hash__`,物件被加入 dict 和 set 等雜湊集" +"合時也會用到它。擁有 :meth:`!__hash__` 意味著該類別的實例是不可變的。可變性是" +"一個複雜的特性,取決於程式設計師的意圖、:meth:`!__eq__` 是否存在及其行為,以" +"及 ``@dataclass`` 裝飾器中 *eq* 和 *frozen* 旗標的值。" #: ../../library/dataclasses.rst:150 -#, fuzzy msgid "" "By default, ``@dataclass`` will not implicitly add a :meth:`~object." "__hash__` method unless it is safe to do so. Neither will it add or change " @@ -301,13 +289,12 @@ msgid "" "attribute ``__hash__ = None`` has a specific meaning to Python, as described " "in the :meth:`!__hash__` documentation." msgstr "" -"預設情況下,``@dataclass`` 不會隱式新增 :meth:`~object.__hash__` 方法,除非這" -"樣做是安全的。它也不會新增或更改現有的明確定義的 :meth:`!__hash__` 方法。設定" -"類別屬性 ``__hash__ = None`` 對 Python 具有特定含義,如 :meth:`!__hash__` 文" -"件中所述。" +"預設情況下,除非這樣做是安全的,否則 ``@dataclass`` 不會隱式加入 " +":meth:`~object.__hash__` 方法。它也不會加入或更改既有的、明確定義的 " +":meth:`!__hash__` 方法。設定類別屬性 ``__hash__ = None`` 對 Python 有特定的意" +"義,如 :meth:`!__hash__` 文件中所述。" #: ../../library/dataclasses.rst:156 -#, fuzzy msgid "" "If :meth:`!__hash__` is not explicitly defined, or if it is set to ``None``, " "then ``@dataclass`` *may* add an implicit :meth:`!__hash__` method. Although " @@ -316,23 +303,22 @@ msgid "" "logically immutable but can still be mutated. This is a specialized use case " "and should be considered carefully." msgstr "" -"如果 :meth:`!__hash__` 沒有明確定義,或者如果它被設定為 ``None``,那麼 " -"``@dataclass`` *可能* 新增一個隱式的 :meth:`!__hash__` 方法。雖然不推薦,但你" -"可以強制 ``@dataclass`` 使用 ``unsafe_hash=True`` 建立一個 :meth:`!__hash__` " -"方法。如果你的類別在邏輯上是不可變的但仍然可以改變,則可能是這種情況。這是一" -"個特殊的用例,應該仔細考慮。" +"如果 :meth:`!__hash__` 沒有被明確定義,或被設為 ``None``,那麼 " +"``@dataclass`` *可能*\\ 會加入一個隱式的 :meth:`!__hash__` 方法。雖然不建議," +"但你可以用 ``unsafe_hash=True`` 強制 ``@dataclass`` 建立 :meth:`!__hash__` 方" +"法。當你的類別在邏輯上不可變、但實際上仍然可以被修改時,可能就會這樣做。這是" +"一個特殊的使用情境,應該謹慎考慮。" #: ../../library/dataclasses.rst:163 -#, fuzzy msgid "" "Here are the rules governing implicit creation of a :meth:`!__hash__` " "method. Note that you cannot both have an explicit :meth:`!__hash__` method " "in your dataclass and set ``unsafe_hash=True``; this will result in a :exc:" "`TypeError`." msgstr "" -"以下是管理隱式建立 :meth:`!__hash__` 方法的規則。請注意,你不能在資料類別中既" -"有顯式的 :meth:`!__hash__` 方法又設定 ``unsafe_hash=True``;這將導致 :exc:" -"`TypeError`。" +"以下是隱式建立 :meth:`!__hash__` 方法的規則。請注意,你不能在資料類別中既明確" +"定義 :meth:`!__hash__` 方法、又設定 ``unsafe_hash=True``;這會導致 " +":exc:`TypeError`。" #: ../../library/dataclasses.rst:168 msgid "" @@ -344,22 +330,20 @@ msgid "" "used (if the superclass is :class:`object`, this means it will fall back to " "id-based hashing)." msgstr "" -"如果 *eq* 和 *frozen* 都為真,預設情況下 ``@dataclass`` 會為你生成一個 :meth:" +"如果 *eq* 和 *frozen* 都為真,預設情況下 ``@dataclass`` 會為你產生一個 :meth:" "`!__hash__` 方法。如果 *eq* 為真且 *frozen* 為假,:meth:`!__hash__` 將被設定" "為 ``None``,並將其標記為不可雜湊(因為它是可變的)。如果 *eq* 為假,:meth:`!" "__hash__` 將保持不變,這意味著將使用超類別的 :meth:`!__hash__` 方法(如果超類" "別是 :class:`object`,這意味著它將回退到基於 id 的雜湊)。" #: ../../library/dataclasses.rst:176 -#, fuzzy msgid "" "*frozen*: If true (the default is ``False``), assigning to fields will " "generate an exception. This emulates read-only frozen instances. See the :" "ref:`discussion ` below." msgstr "" -"*frozen*:如果為真(預設為 ``False``),分配給欄位將產生例外。這模擬了只讀的" -"凍結實例。如果 :meth:`~object.__setattr__` 或 :meth:`~object.__delattr__` 在" -"類別中定義,則 :exc:`TypeError` 被引發。請參閱下面的討論。" +"*frozen*:如果為真(預設為 ``False``),對欄位賦值將會產生例外。這模擬了唯讀" +"的凍結實例。請參閱下方的\\ :ref:`討論 `。" #: ../../library/dataclasses.rst:180 msgid "" @@ -370,7 +354,6 @@ msgstr "" "且 *frozen* 為真,則會引發 :exc:`TypeError`。" #: ../../library/dataclasses.rst:183 -#, fuzzy msgid "" "*match_args*: If true (the default is ``True``), the :attr:`~object." "__match_args__` tuple will be created from the list of non keyword-only " @@ -379,13 +362,13 @@ msgid "" "__match_args__` is already defined in the class, then :attr:`!" "__match_args__` will not be generated." msgstr "" -"*match_args*:如果為真(預設為 ``True``),``__match_args__`` 元組將從參數列" -"表建立到生成的 :meth:`~object.__init__` 方法(即使 :meth: `!__init__` 未生" -"成,見上文)。如果為 false,或者類別中已經定義了 :attr:`!__match_args__`,則" -"不會生成 :attr:`!__match_args__`。" +"*match_args*:如果為真(預設為 ``True``),將會以傳給所產生的 " +":meth:`~object.__init__` 方法中、非僅限關鍵字參數的串列,建立 " +":attr:`~object.__match_args__` tuple(即使沒有產生 :meth:`!__init__` 也一樣," +"見上文)。如果為假,或是類別中已經定義了 :attr:`!__match_args__`,則不會產生 " +":attr:`!__match_args__`。" #: ../../library/dataclasses.rst:192 -#, fuzzy msgid "" "*kw_only*: If true (the default value is ``False``), then all fields will be " "marked as keyword-only. If a field is marked as keyword-only, then the only " @@ -394,27 +377,26 @@ msgid "" "is called. See the :term:`parameter` glossary entry for details. Also see " "the :const:`KW_ONLY` section." msgstr "" -"``kw_only``:如果為 true(預設值為 ``False``),則所有欄位將被標記為僅限關鍵" -"字。如果一個欄位被標記為僅限關鍵字,那麼唯一的影響是從僅限關鍵字欄位生成的 :" -"meth:`~object.__init__` 參數必須在呼叫 :meth:`!__init__` 時指定關鍵字。對資料" -"類別的任何其他方面都沒有影響。有關詳細資訊,請參閱 :term:`parameter` 詞彙表條" -"目。另請參閱 :const:`KW_ONLY` 部分。" +"*kw_only*:如果為真(預設值為 ``False``),則所有欄位都會被標記為僅限關鍵字。" +"如果一個欄位被標記為僅限關鍵字,唯一的影響是:由僅限關鍵字欄位產生的 " +":meth:`~object.__init__` 參數,在呼叫 :meth:`!__init__` 時必須以關鍵字指定。" +"詳情請參閱\\ :term:`參數 `\\ 詞彙表條目,另請參閱 " +":const:`KW_ONLY` 一節。" #: ../../library/dataclasses.rst:200 msgid "Keyword-only fields are not included in :attr:`!__match_args__`." msgstr "" #: ../../library/dataclasses.rst:204 -#, fuzzy msgid "" "*slots*: If true (the default is ``False``), :attr:`~object.__slots__` " "attribute will be generated and new class will be returned instead of the " "original one. If :attr:`!__slots__` is already defined in the class, then :" "exc:`TypeError` is raised." msgstr "" -"``slots``:如果為 true(預設為 ``False``),將生成 :attr:`~object.__slots__` " -"屬性並回傳新類別而不是原始類別。如果 :attr:`!__slots__` 已經在類別中定義," -"則 :exc:`TypeError` 被引發。" +"*slots*:如果為真(預設為 ``False``),將會產生 :attr:`~object.__slots__` 屬" +"性,並回傳新的類別而非原本的類別。如果類別中已經定義了 :attr:`!__slots__`,則" +"會引發 :exc:`TypeError`。" #: ../../library/dataclasses.rst:210 msgid "" @@ -425,7 +407,6 @@ msgid "" msgstr "" #: ../../library/dataclasses.rst:218 -#, fuzzy msgid "" "If a field name is already included in the :attr:`!__slots__` of a base " "class, it will not be included in the generated :attr:`!__slots__` to " @@ -434,30 +415,28 @@ msgid "" "`fields` instead. To be able to determine inherited slots, base class :attr:" "`!__slots__` may be any iterable, but *not* an iterator." msgstr "" -"如果欄位名稱已經包含在基底類別的 :attr:`!__slots__` 中,它將不會包含在生成" -"的 :attr:`!__slots__` 中以防止 :ref:`覆蓋它們 `。因此," -"不要使用 __slots__ 來檢索資料類別的欄位名稱。使用 :func:`fields` 代替。為了能" -"夠確定繼承的插槽,基底類別 :attr:`!__slots__` 可以是任何可疊代的,但*不是*疊" -"代器。" +"如果某個欄位名稱已經包含在基底類別的 :attr:`!__slots__` 中,它就不會被包含在" +"產生的 :attr:`!__slots__` 裡,以避免\\ :ref:`覆寫它們 " +"`。因此,請不要使用 :attr:`!__slots__` 來取得資料類別的" +"欄位名稱,而應改用 :func:`fields`。為了能判斷繼承而來的 slot,基底類別的 " +":attr:`!__slots__` 可以是任何可疊代物件,但\\ *不能*\\ 是疊代器。" #: ../../library/dataclasses.rst:228 -#, fuzzy msgid "" "*weakref_slot*: If true (the default is ``False``), add a slot named " "\"__weakref__\", which is required to make an instance :func:`weakref-able " "`. It is an error to specify ``weakref_slot=True`` without also " "specifying ``slots=True``." msgstr "" -"*weakref_slot*:如果為真(預設為 ``False``),新增一個名為 \"__weakref__\" 的" -"插槽,這是使實例可弱引用所必需的。在沒有指定 ``slots=True`` 的情況下指定 " -"``weakref_slot=True`` 是錯誤的。" +"*weakref_slot*:如果為真(預設為 ``False``),會加入一個名為 \"__weakref__\" " +"的 slot,這是讓實例\\ :func:`可被弱參照 `\\ 所必需的。指定 " +"``weakref_slot=True`` 卻沒有同時指定 ``slots=True`` 是錯誤的。" #: ../../library/dataclasses.rst:236 -#, fuzzy msgid "" "``field``\\s may optionally specify a default value, using normal Python " "syntax::" -msgstr "``field``\\s 可以選擇指定一個預設值,使用普通的 Python 語法: ::" +msgstr "``field``\\ 可以選擇性地使用一般的 Python 語法來指定預設值: ::" #: ../../library/dataclasses.rst:239 msgid "" @@ -468,30 +447,27 @@ msgid "" msgstr "" #: ../../library/dataclasses.rst:244 -#, fuzzy msgid "" "In this example, both :attr:`!a` and :attr:`!b` will be included in the " "added :meth:`~object.__init__` method, which will be defined as::" msgstr "" -"在此示例中,:attr:`!a` 和 :attr:`!b` 都將包含在新增的 :meth:`~object." -"__init__` 方法中,該方法將定義為: ::" +"在這個範例中,:attr:`!a` 和 :attr:`!b` 都會被包含在加入的 " +":meth:`~object.__init__` 方法中,該方法的定義如下: ::" #: ../../library/dataclasses.rst:247 msgid "def __init__(self, a: int, b: int = 0):" msgstr "def __init__(self, a: int, b: int = 0):" #: ../../library/dataclasses.rst:249 -#, fuzzy msgid "" ":exc:`TypeError` will be raised if a field without a default value follows a " "field with a default value. This is true whether this occurs in a single " "class, or as a result of class inheritance." msgstr "" -":exc:`TypeError` 如果沒有預設值的欄位跟在具有預設值的欄位之後,將引發。無論這" -"發生在單個類別中還是作為類別繼承的結果,都是如此。" +"如果沒有預設值的欄位出現在有預設值的欄位之後,將會引發 :exc:`TypeError`。無論" +"是發生在單一類別中,還是因類別繼承而造成的,都是如此。" #: ../../library/dataclasses.rst:255 -#, fuzzy msgid "" "For common and simple use cases, no other functionality is required. There " "are, however, some dataclass features that require additional per-field " @@ -499,9 +475,9 @@ msgid "" "replace the default field value with a call to the provided :func:`!field` " "function. For example::" msgstr "" -"對於常見和簡單的用例,不需要其他功能。但是,有些資料類別功能需要額外的每個欄" -"位資訊。為了滿足這種對附加資訊的需求,你可以透過呼叫提供的 :func:`!field` 函" -"式來替換預設欄位值。例如: ::" +"對於常見且簡單的使用情境,不需要其他功能。不過,有些資料類別的功能需要每個欄" +"位額外的資訊。為了提供這些額外資訊,你可以把預設的欄位值替換成對所提供的 " +":func:`!field` 函式的呼叫。例如: ::" #: ../../library/dataclasses.rst:261 msgid "" @@ -520,16 +496,15 @@ msgstr "" "c.mylist += [1, 2, 3]" #: ../../library/dataclasses.rst:268 -#, fuzzy msgid "" "As shown above, the :const:`MISSING` value is a sentinel object used to " "detect if some parameters are provided by the user. This sentinel is used " "because ``None`` is a valid value for some parameters with a distinct " "meaning. No code should directly use the :const:`MISSING` value." msgstr "" -"如上所示,:const:`MISSING` 值是一個哨兵物件,用於檢測某些參數是否由使用者提" -"供。使用此標記是因為 ``None`` 對於某些具有不同含義的參數是有效值。任何程式碼" -"都不應直接使用 :const:`MISSING` 值。" +"如上所示,:const:`MISSING` 值是一個哨兵物件,用來偵測使用者是否提供了某些參數" +"。之所以使用這個哨兵,是因為對某些參數來說,``None`` 是一個具有不同意義的有效" +"值。任何程式碼都不應直接使用 :const:`MISSING` 值。" #: ../../library/dataclasses.rst:273 msgid "The parameters to :func:`!field` are:" @@ -545,7 +520,6 @@ msgstr "" "呼叫本身會替換預設值的正常位置。" #: ../../library/dataclasses.rst:279 -#, fuzzy msgid "" "*default_factory*: If provided, it must be a zero-argument callable that " "will be called when a default value is needed for this field. Among other " @@ -553,30 +527,27 @@ msgid "" "discussed below. It is an error to specify both *default* and " "*default_factory*." msgstr "" -"*default_factory*:如果提供,它必須是一個零引數可呼叫函式,當此欄位需要預設值" -"時將被呼叫。除其他用途外,這可用於指定具有可變預設值的欄位,如下所述。同時指" -"定 *default* 和 *default_factory* 是錯誤的。" +"*default_factory*:如果有提供,它必須是一個不接受引數的可呼叫物件,在此欄位需" +"要預設值時會被呼叫。除了其他用途之外,這可以用來指定具有可變預設值的欄位,如" +"下所述。同時指定 *default* 和 *default_factory* 是錯誤的。" #: ../../library/dataclasses.rst:285 -#, fuzzy msgid "" "*init*: If true (the default), this field is included as a parameter to the " "generated :meth:`~object.__init__` method." msgstr "" -"*init*:如果為 true(預設值),則此欄位將作為生成的 :meth:`~object.__init__` " -"方法的參數包含在內。" +"*init*:如果為真(預設值),此欄位會作為參數包含在產生的 " +":meth:`~object.__init__` 方法中。" #: ../../library/dataclasses.rst:288 -#, fuzzy msgid "" "*repr*: If true (the default), this field is included in the string returned " "by the generated :meth:`~object.__repr__` method." msgstr "" -"*repr*:如果為真(預設值),則此欄位包含在生成的 :meth:`~object.__repr__` 方" -"法回傳的字串中。" +"*repr*:如果為真(預設值),此欄位會包含在產生的 :meth:`~object.__repr__` 方" +"法所回傳的字串中。" #: ../../library/dataclasses.rst:291 -#, fuzzy msgid "" "*hash*: This can be a bool or ``None``. If true, this field is included in " "the generated :meth:`~object.__hash__` method. If false, this field is " @@ -586,13 +557,13 @@ msgid "" "comparisons. Setting this value to anything other than ``None`` is " "discouraged." msgstr "" -"*hash*:這可以是 bool 或 ``None``。如果為真,則此欄位包含在生成的 :meth:" -"`__hash__` 方法中。如果 ``None``\\ (預設值),則使用\\ *比較*\\ 的值:這通常" -"是預期的行為。如果一個欄位用於比較,則應在雜湊中考慮該欄位。不鼓勵將此值設定" -"為 ``None`` 以外的任何值。" +"*hash*:可以是 bool 或 ``None``。如果為真,此欄位會包含在產生的 " +":meth:`~object.__hash__` 方法中。如果為假,此欄位會被排除在產生的 " +":meth:`~object.__hash__` 之外。如果為 ``None``\\ (預設值),則使用 " +"*compare* 的值:這通常就是預期的行為,因為如果一個欄位會用於比較,它就應該被" +"納入雜湊。不建議將此值設為 ``None`` 以外的值。" #: ../../library/dataclasses.rst:299 -#, fuzzy msgid "" "One possible reason to set ``hash=False`` but ``compare=True`` would be if a " "field is expensive to compute a hash value for, that field is needed for " @@ -600,22 +571,20 @@ msgid "" "hash value. Even if a field is excluded from the hash, it will still be " "used for comparisons." msgstr "" -"設定 ``hash=False`` 但 ``compare=True`` 的一個可能原因是,如果一個欄位計算雜" -"湊值的成本很高,則需要該欄位進行相等性測試,並且還有其他欄位有助於型別的雜湊" -"值。即使一個欄位被排除在雜湊之外,它仍然會被用於比較。" +"設定 ``hash=False`` 但 ``compare=True`` 的一個可能原因是:某個欄位計算雜湊值" +"的成本很高、該欄位又是相等性測試所必需的,而且還有其他欄位會影響該型別的雜湊" +"值。即使某個欄位被排除在雜湊之外,它仍然會被用於比較。" #: ../../library/dataclasses.rst:305 -#, fuzzy msgid "" "*compare*: If true (the default), this field is included in the generated " "equality and comparison methods (:meth:`~object.__eq__`, :meth:`~object." "__gt__`, et al.)." msgstr "" -"*compare*:如果為真(預設值),則此欄位包含在生成的相等和比較方法中(:meth:" -"`~object.__eq__`、:meth:`~object.__gt__` 等)。" +"*compare*:如果為真(預設值),此欄位會包含在產生的相等與比較方法中(" +":meth:`~object.__eq__`、:meth:`~object.__gt__` 等)。" #: ../../library/dataclasses.rst:309 -#, fuzzy msgid "" "*metadata*: This can be a mapping or ``None``. ``None`` is treated as an " "empty dict. This value is wrapped in :func:`~types.MappingProxyType` to " @@ -624,19 +593,18 @@ msgid "" "mechanism. Multiple third-parties can each have their own key, to use as a " "namespace in the metadata." msgstr "" -"*metadata*:這可以是對映或無。 None 被視為空字典。此值包含在 :func:`~types." -"MappingProxyType` 中以使其成為只讀的,並暴露在 :class:`Field` 物件上。它根本" -"不被資料類別使用,而是作為第三方擴充機制提供的。多個第三方可以各自擁有自己的" -"密鑰,用作元資料中的命名空間。" +"*metadata*:可以是一個對映或 ``None``。``None`` 會被視為空的 dict。此值會被包" +"裝在 :func:`~types.MappingProxyType` 中使其成為唯讀,並公開在 :class:`Field` " +"物件上。資料類別完全不會使用它,它是作為第三方擴充機制而提供的。多個第三方可" +"以各自擁有自己的鍵,作為 metadata 中的命名空間。" #: ../../library/dataclasses.rst:317 -#, fuzzy msgid "" "*kw_only*: If true, this field will be marked as keyword-only. This is used " "when the generated :meth:`~object.__init__` method's parameters are computed." msgstr "" -"*kw_only*:如果為真,該欄位將被標記為僅限關鍵字。這在計算生成的 :meth:" -"`~object.__init__` 方法的參數時使用。" +"*kw_only*:如果為真,此欄位會被標記為僅限關鍵字。這會在計算所產生的 " +":meth:`~object.__init__` 方法的參數時使用。" #: ../../library/dataclasses.rst:321 msgid "Keyword-only fields are also not included in :attr:`!__match_args__`." @@ -647,7 +615,6 @@ msgid "*doc*: optional docstring for this field." msgstr "" #: ../../library/dataclasses.rst:329 -#, fuzzy msgid "" "If the default value of a field is specified by a call to :func:`!field`, " "then the class attribute for this field will be replaced by the specified " @@ -657,10 +624,10 @@ msgid "" "fields, just as if the default value itself were specified. For example, " "after::" msgstr "" -"如果欄位的預設值是透過呼叫 :func:`!field` 指定的,那麼該欄位的類別屬性將被指" -"定的 *default* 值替換。如果沒有提供 *default*,那麼類別屬性將被刪除。目的是" -"在 :func:`@dataclass ` 裝飾器運行後,類別屬性將全部包含欄位的預設" -"值,就像預設值本身已指定一樣。例如,在: ::" +"如果欄位的預設值是透過呼叫 :func:`!field` 來指定的,那麼該欄位的類別屬性將會" +"被替換為所指定的 *default* 值。如果沒有提供 *default*,該類別屬性就會被刪除。" +"這樣做的目的是,在 :deco:`dataclass` 裝飾器執行之後,類別屬性都會包含欄位的預" +"設值,就如同直接指定了預設值本身一樣。例如,在以下程式碼之後: ::" #: ../../library/dataclasses.rst:338 msgid "" @@ -679,26 +646,24 @@ msgstr "" " t: int = 20" #: ../../library/dataclasses.rst:345 -#, fuzzy msgid "" "The class attribute :attr:`!C.z` will be ``10``, the class attribute :attr:`!" "C.t` will be ``20``, and the class attributes :attr:`!C.x` and :attr:`!C.y` " "will not be set." msgstr "" -"類別屬性 :attr:`!C.z` 將為 ``10``,類別屬性 :attr:`!C.t` 將為 ``20``,類別屬" -"性 :attr:`!C.x` 和 :attr:`!C.y` 將不會放。" +"類別屬性 :attr:`!C.z` 會是 ``10``,類別屬性 :attr:`!C.t` 會是 ``20``,而類別" +"屬性 :attr:`!C.x` 和 :attr:`!C.y` 不會被設定。" #: ../../library/dataclasses.rst:351 -#, fuzzy msgid "" ":class:`!Field` objects describe each defined field. These objects are " "created internally, and are returned by the :func:`fields` module-level " "method (see below). Users should never instantiate a :class:`!Field` object " "directly. Its documented attributes are:" msgstr "" -":class:`!Field` 物件描述每個定義的欄位。這些物件在內部建立,並由 :func:" -"`fields` 模組級方法回傳(見下文)。使用者不應該直接實例化 :class:`!Field` 物" -"件。它記錄的屬性是:" +":class:`!Field` 物件描述每個已定義的欄位。這些物件是在內部建立的,並由模組層" +"級的 :func:`fields` 方法回傳(見下文)。使用者絕不應直接實例化 " +":class:`!Field` 物件。它有文件記載的屬性如下:" #: ../../library/dataclasses.rst:356 msgid ":attr:`!name`: The name of the field." @@ -709,22 +674,20 @@ msgid ":attr:`!type`: The type of the field." msgstr ":attr:`!type`:欄位的型別。" #: ../../library/dataclasses.rst:358 -#, fuzzy msgid "" ":attr:`!default`, :attr:`!default_factory`, :attr:`!init`, :attr:`!repr`, :" "attr:`!hash`, :attr:`!compare`, :attr:`!metadata`, and :attr:`!kw_only` have " "the identical meaning and values as they do in the :func:`field` function." msgstr "" -":attr:`!default`、:attr:`!default_factory`、:attr:`!init`、:attr:`!repr`、:" -"attr:`!hash`、:attr:`!compare`, :attr:`!metadata` 和 :attr:`!kw_only` 有與它" -"們在 :func:`field` 函式中的含義和值相同。" +":attr:`!default`、:attr:`!default_factory`、:attr:`!init`、:attr:`!repr`、" +":attr:`!hash`、:attr:`!compare`、:attr:`!metadata` 和 :attr:`!kw_only` 的意義" +"與值,都與它們在 :func:`field` 函式中的相同。" #: ../../library/dataclasses.rst:362 -#, fuzzy msgid "" "Other attributes may exist, but they are private and must not be inspected " "or relied on." -msgstr "可能存在其他屬性,但它們是私有的,不得檢查或依賴。" +msgstr "可能還存在其他屬性,但它們是私有的,不得檢查或依賴。" #: ../../library/dataclasses.rst:367 msgid "" @@ -736,33 +699,30 @@ msgid "" msgstr "" #: ../../library/dataclasses.rst:376 -#, fuzzy msgid "" "Returns a tuple of :class:`Field` objects that define the fields for this " "dataclass. Accepts either a dataclass, or an instance of a dataclass. " "Raises :exc:`TypeError` if not passed a dataclass or instance of one. Does " "not return pseudo-fields which are ``ClassVar`` or ``InitVar``." msgstr "" -"回傳定義此資料類別欄位的 :class:`Field` 物件的元組。接受資料類別或資料類別的" -"實例。如果未傳遞資料類別或其中一個實例,則引發 :exc:`TypeError`。不回傳 " -"``ClassVar`` 或 ``InitVar`` 的偽欄位。" +"回傳一個由 :class:`Field` 物件組成的 tuple,這些物件定義了此資料類別的欄位。" +"接受資料類別或資料類別的實例。如果傳入的不是資料類別或其實例,則會引發 " +":exc:`TypeError`。不會回傳屬於 ``ClassVar`` 或 ``InitVar`` 的偽欄位。" #: ../../library/dataclasses.rst:383 -#, fuzzy msgid "" "Converts the dataclass *obj* to a dict (by using the factory function " "*dict_factory*). Each dataclass is converted to a dict of its fields, as " "``name: value`` pairs. dataclasses, dicts, lists, and tuples are recursed " "into. Other objects are copied with :func:`copy.deepcopy`." msgstr "" -"將資料類別 *obj* 轉換為字典(透過使用工廠函式 *dict_factory*)。每個資料類別" -"都被轉換為其欄位的字典,作為 ``name: value`` 對。資料類別、字典、列表和元組被" -"遞迴到。其他物件使用 :func:`copy.deepcopy` 複製。" +"將資料類別 *obj* 轉換為 dict(使用工廠函式 *dict_factory*)。每個資料類別都會" +"被轉換成由其欄位組成、以 ``name: value`` 配對表示的 dict。資料類別、dict、" +"list 和 tuple 會被遞迴處理,其他物件則以 :func:`copy.deepcopy` 複製。" #: ../../library/dataclasses.rst:389 -#, fuzzy msgid "Example of using :func:`!asdict` on nested dataclasses::" -msgstr "在嵌套資料類別上使用 :func:`!asdict` 的範例: ::" +msgstr "在巢狀資料類別上使用 :func:`!asdict` 的範例: ::" #: ../../library/dataclasses.rst:391 msgid "" @@ -797,31 +757,28 @@ msgstr "" "assert asdict(c) == {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}" #: ../../library/dataclasses.rst:406 ../../library/dataclasses.rst:426 -#, fuzzy msgid "To create a shallow copy, the following workaround may be used::" -msgstr "要建立淺複製,可以使用以下解決方法:" +msgstr "若要建立淺複製,可以使用以下替代做法: ::" #: ../../library/dataclasses.rst:408 msgid "{field.name: getattr(obj, field.name) for field in fields(obj)}" msgstr "{field.name: getattr(obj, field.name) for field in fields(obj)}" #: ../../library/dataclasses.rst:410 -#, fuzzy msgid "" ":func:`!asdict` raises :exc:`TypeError` if *obj* is not a dataclass instance." -msgstr ":func:`!asdict` 如果 *obj* 不是資料類別實例,則引發 :exc:`TypeError`。" +msgstr "如果 *obj* 不是資料類別實例,:func:`!asdict` 會引發 :exc:`TypeError`。" #: ../../library/dataclasses.rst:415 -#, fuzzy msgid "" "Converts the dataclass *obj* to a tuple (by using the factory function " "*tuple_factory*). Each dataclass is converted to a tuple of its field " "values. dataclasses, dicts, lists, and tuples are recursed into. Other " "objects are copied with :func:`copy.deepcopy`." msgstr "" -"將資料類別 *obj* 轉換為元組(透過使用工廠函式 *tuple_factory*)。每個資料類別" -"都被轉換為其欄位值的元組。資料類別、字典、列表和元組被遞迴到。其他物件使用 :" -"func:`copy.deepcopy` 複製。" +"將資料類別 *obj* 轉換為 tuple(使用工廠函式 *tuple_factory*)。每個資料類別都" +"會被轉換成由其欄位值組成的 tuple。資料類別、dict、list 和 tuple 會被遞迴處理" +",其他物件則以 :func:`copy.deepcopy` 複製。" #: ../../library/dataclasses.rst:421 msgid "Continuing from the previous example::" @@ -840,15 +797,13 @@ msgid "tuple(getattr(obj, field.name) for field in dataclasses.fields(obj))" msgstr "tuple(getattr(obj, field.name) for field in dataclasses.fields(obj))" #: ../../library/dataclasses.rst:430 -#, fuzzy msgid "" ":func:`!astuple` raises :exc:`TypeError` if *obj* is not a dataclass " "instance." msgstr "" -":func:`!astuple` 如果 *obj* 不是資料類別實例,則引發 :exc:`TypeError`。" +"如果 *obj* 不是資料類別實例,:func:`!astuple` 會引發 :exc:`TypeError`。" #: ../../library/dataclasses.rst:435 -#, fuzzy msgid "" "Creates a new dataclass with name *cls_name*, fields as defined in *fields*, " "base classes as given in *bases*, and initialized with a namespace as given " @@ -859,13 +814,13 @@ msgid "" "*slots*, and *weakref_slot* have the same meaning as they do in :deco:" "`dataclass`." msgstr "" -"建立一個名為 *cls_name* 的新資料類別,欄位在 *fields* 中定義,基底類別在 " -"*bases* 中給出,並使用 *namespace* 中給出的命名空間進行初始化。 ``fields`` 是" -"一個疊代器,其元素分別是 ``name``、``(name, type)`` 或 ``(name, type, " -"Field)``。如果只提供 ``name``,則 ``typing.Any`` 用於 ``type``。 ``init``、" -"``repr``、``eq``、``order``、``unsafe_hash``、``frozen``、``match_args``、" -"``kw_only`` 的值, ``slots`` 和 ``weakref_slot`` 與它們在 :func:`dataclass` 中" -"的含義相同。" +"建立一個名為 *cls_name* 的新資料類別,其欄位由 *fields* 定義、基底類別由 " +"*bases* 給定,並以 *namespace* 給定的命名空間進行初始化。*fields* 是一個可疊" +"代物件,其中每個元素是 ``name``、``(name, type)`` 或 ``(name, type, Field)`` " +"其中之一。如果只提供 ``name``,則 ``type`` 會使用 :data:`typing.Any`。" +"*init*、*repr*、*eq*、*order*、*unsafe_hash*、*frozen*、*match_args*、" +"*kw_only*、*slots* 和 *weakref_slot* 的值,與它們在 :deco:`dataclass` 中的意" +"義相同。" #: ../../library/dataclasses.rst:445 msgid "" @@ -882,16 +837,15 @@ msgid "" msgstr "" #: ../../library/dataclasses.rst:454 -#, fuzzy msgid "" "This function is not strictly required, because any Python mechanism for " "creating a new class with :attr:`~object.__annotations__` can then apply " "the :deco:`dataclass` function to convert that class to a dataclass. This " "function is provided as a convenience. For example::" msgstr "" -"這個函式不是嚴格要求的,因為任何使用 :attr:`~object.__annotations__` 建立新類" -"別的 Python 機制都可以應用 :func:`dataclass` 函式將該類別轉換為資料類別。提供" -"此功能是為了方便。例如: ::" +"這個函式並非絕對必要,因為任何以 :attr:`~object.__annotations__` 建立新類別的" +" Python 機制,都可以接著套用 :deco:`dataclass` 函式,把該類別轉換為資料類別。" +"提供這個函式只是為了方便。例如: ::" #: ../../library/dataclasses.rst:460 msgid "" @@ -947,36 +901,32 @@ msgstr "" "dataclass 的欄位則引發 :exc:`TypeError`。" #: ../../library/dataclasses.rst:487 -#, fuzzy msgid "" "The newly returned object is created by calling the :meth:`~object.__init__` " "method of the dataclass. This ensures that :meth:`__post_init__`, if " "present, is also called." msgstr "" -"新回傳的物件是透過呼叫資料類別的 :meth:`~object.__init__` 方法建立的。這確" -"保 :meth:`__post_init__`\\ (如果存在)也被呼叫。" +"新回傳的物件是透過呼叫資料類別的 :meth:`~object.__init__` 方法來建立的。這確" +"保了 :meth:`__post_init__`\\ (如果有的話)也會被呼叫。" #: ../../library/dataclasses.rst:491 -#, fuzzy msgid "" "Init-only variables without default values, if any exist, must be specified " "on the call to :func:`!replace` so that they can be passed to :meth:`!" "__init__` and :meth:`__post_init__`." msgstr "" -"沒有預設值的僅初始化變數(如果存在)必須在呼叫 :func:`replace` 時指定,以便它" -"們可以傳遞給 :meth:`__init__` 和 :meth:`__post_init__`。" +"如果存在沒有預設值的僅初始化變數,就必須在呼叫 :func:`!replace` 時指定它們," +"以便將它們傳給 :meth:`!__init__` 和 :meth:`__post_init__`。" #: ../../library/dataclasses.rst:495 -#, fuzzy msgid "" "It is an error for *changes* to contain any fields that are defined as " "having ``init=False``. A :exc:`ValueError` will be raised in this case." msgstr "" -"*changes* 包含任何定義為具有 ``init=False`` 的欄位是錯誤的。在這種情況下將引" -"發 :exc:`ValueError`。" +"*changes* 中若包含任何定義為 ``init=False`` 的欄位,就是錯誤的。在這種情況下" +"會引發 :exc:`ValueError`。" #: ../../library/dataclasses.rst:499 -#, fuzzy msgid "" "Be forewarned about how ``init=False`` fields work during a call to :func:`!" "replace`. They are not copied from the source object, but rather are " @@ -986,11 +936,11 @@ msgid "" "perhaps a custom :func:`!replace` (or similarly named) method which handles " "instance copying." msgstr "" -"預先警告 ``init=False`` 欄位在呼叫 :func:`!replace` 期間是如何工作的。它們不" -"是從源物件複製的,而是在 :meth:`__post_init__` 中初始化的,如果它們被初始化的" -"話。預計 ``init=False`` 欄位將很少被明智地使用。如果使用它們,使用替代的類別" -"建構函式可能是明智的,或者可能是處理實例複製的自訂 :func:`!replace` (或類似" -"命名的)方法。" +"請事先留意 ``init=False`` 欄位在呼叫 :func:`!replace` 時的行為。它們不會從來" +"源物件複製,而是在 :meth:`__post_init__` 中初始化(如果有初始化的話)。" +"``init=False`` 欄位預期會很少用到,並且應審慎使用。如果確實要使用,比較明智的" +"做法可能是提供替代的類別建構函式,或是自訂一個處理實例複製的 :func:`!replace`" +"\\ (或名稱類似的)方法。" #: ../../library/dataclasses.rst:508 msgid "" @@ -1008,14 +958,13 @@ msgstr "" "名 `)或一個實例則回傳 ``True``,否則回傳 ``False``。" #: ../../library/dataclasses.rst:516 -#, fuzzy msgid "" "If you need to know if a class is an instance of a dataclass (and not a " "dataclass itself), then add a further check for ``not isinstance(obj, " "type)``::" msgstr "" -"如果你需要知道一個類別是否是資料類別的實例(而不是資料類別本身),那麼新增一" -"個進一步的檢查 ``not isinstance(obj, type)``: ::" +"如果你需要知道一個類別是否為資料類別的實例(而不是資料類別本身),請再加上 " +"``not isinstance(obj, type)`` 的檢查: ::" #: ../../library/dataclasses.rst:520 msgid "" @@ -1026,12 +975,10 @@ msgstr "" " return is_dataclass(obj) and not isinstance(obj, type)" #: ../../library/dataclasses.rst:525 -#, fuzzy msgid "A sentinel value signifying a missing default or default_factory." -msgstr "表示缺少 default 或 default_factory 的標記值。" +msgstr "一個哨兵值,表示缺少 default 或 default_factory。" #: ../../library/dataclasses.rst:529 -#, fuzzy msgid "" "A sentinel value used as a type annotation. Any fields after a pseudo-field " "with the type of :const:`!KW_ONLY` are marked as keyword-only fields. Note " @@ -1041,17 +988,17 @@ msgid "" "meth:`~object.__init__` parameters that must be specified as keywords when " "the class is instantiated." msgstr "" -"用作型別註釋的標記值。型別為 :const:`!KW_ONLY` 的偽欄位之後的任何欄位都被標記" -"為僅關鍵字欄位。請注意,:const:`!KW_ONLY` 型別的偽欄位將被完全忽略。這包括此" -"類欄位的名稱。按照慣例,名稱 ``_`` 用於 :const:`!KW_ONLY` 欄位。僅關鍵字欄位" -"表示 :meth:`~object.__init__` 參數,在實例化類別時必須將其指定為關鍵字。" +"一個用作型別註釋的哨兵值。在型別為 :const:`!KW_ONLY` 的偽欄位之後的所有欄位," +"都會被標記為僅限關鍵字欄位。請注意,型別為 :const:`!KW_ONLY` 的偽欄位除此之外" +"會被完全忽略,包括這種欄位的名稱。依照慣例,:const:`!KW_ONLY` 欄位會使用 " +"``_`` 作為名稱。僅限關鍵字欄位代表在實例化類別時,必須以關鍵字指定的 " +":meth:`~object.__init__` 參數。" #: ../../library/dataclasses.rst:538 -#, fuzzy msgid "" "In this example, the fields ``y`` and ``z`` will be marked as keyword-only " "fields::" -msgstr "在此示例中,欄位 ``y`` 和 ``z`` 將被標記為僅關鍵字欄位: ::" +msgstr "在這個範例中,欄位 ``y`` 和 ``z`` 會被標記為僅限關鍵字欄位: ::" #: ../../library/dataclasses.rst:540 msgid "" @@ -1074,29 +1021,26 @@ msgstr "" "p = Point(0, y=1.5, z=2.0)" #: ../../library/dataclasses.rst:549 -#, fuzzy msgid "" "In a single dataclass, it is an error to specify more than one field whose " "type is :const:`!KW_ONLY`." -msgstr "在單個資料類別中,指定多個型別為 :const:`!KW_ONLY` 的欄位是錯誤的。" +msgstr "在單一資料類別中,指定多個型別為 :const:`!KW_ONLY` 的欄位是錯誤的。" #: ../../library/dataclasses.rst:556 -#, fuzzy msgid "" "Raised when an implicitly defined :meth:`~object.__setattr__` or :meth:" "`~object.__delattr__` is called on a dataclass which was defined with " "``frozen=True``. It is a subclass of :exc:`AttributeError`." msgstr "" -"當在使用 frozen=True 定義的資料類別上呼叫隱式定義的 :meth:`__setattr__` 或 :" -"meth:`__delattr__` 時引發。它是 :exc:`AttributeError` 的子類別。" +"在以 ``frozen=True`` 定義的資料類別上,呼叫隱式定義的 " +":meth:`~object.__setattr__` 或 :meth:`~object.__delattr__` 時引發。它是 " +":exc:`AttributeError` 的子類別。" #: ../../library/dataclasses.rst:563 -#, fuzzy msgid "Post-init processing" -msgstr "後初始化處理" +msgstr "初始化後處理" #: ../../library/dataclasses.rst:567 -#, fuzzy msgid "" "When defined on the class, it will be called by the generated :meth:`~object." "__init__`, normally as :meth:`!self.__post_init__`. However, if any " @@ -1105,18 +1049,17 @@ msgid "" "__init__` method is generated, then :meth:`!__post_init__` will not " "automatically be called." msgstr "" -"生成的 :meth:`~object.__init__` 程式碼將呼叫一個名為 :meth:`!self." -"__post_init__` 的方法,如果 :meth:`!__post_init__` 是在類別上定義的。它通常被" -"稱為 ``self.!__post_init__()``。但是,如果定義了任何 ``InitVar`` 欄位,它們也" -"將按照它們在類別中定義的順序傳遞給 :meth:`!__post_init__` 。如果沒有生成 :" -"meth:`!__init__` 方法,那麼 :meth:`!__post_init__` 將不會被自動呼叫。" +"若在類別上定義此方法,產生的 :meth:`~object.__init__` 就會呼叫它,通常是以 " +":meth:`!self.__post_init__` 的形式呼叫。不過,如果定義了任何 ``InitVar`` 欄位" +",它們也會依照在類別中定義的順序傳給 :meth:`!__post_init__`。如果沒有產生 " +":meth:`!__init__` 方法,就不會自動呼叫 :meth:`!__post_init__`。" #: ../../library/dataclasses.rst:574 -#, fuzzy msgid "" "Among other uses, this allows for initializing field values that depend on " "one or more other fields. For example::" -msgstr "在其他用途中,這允許初始化依賴於一個或多個其他欄位的欄位值。例如: ::" +msgstr "" +"除了其他用途之外,這讓你可以初始化依賴於一個或多個其他欄位的欄位值。例如: ::" #: ../../library/dataclasses.rst:577 msgid "" @@ -1139,16 +1082,15 @@ msgstr "" " self.c = self.a + self.b" #: ../../library/dataclasses.rst:586 -#, fuzzy msgid "" "The :meth:`~object.__init__` method generated by :deco:`dataclass` does not " "call base class :meth:`!__init__` methods. If the base class has an :meth:`!" "__init__` method that has to be called, it is common to call this method in " "a :meth:`__post_init__` method::" msgstr "" -":func:`@dataclass ` 生成的 :meth:`~object.__init__` 方法不呼叫基底" -"類別 :meth:`!__init__` 方法。如果基底類別有一個必須呼叫的 :meth:`!__init__` " -"方法,通常在 :meth:`__post_init__` 方法中呼叫此方法: ::" +"由 :deco:`dataclass` 產生的 :meth:`~object.__init__` 方法不會呼叫基底類別的 " +":meth:`!__init__` 方法。如果基底類別有一個必須被呼叫的 :meth:`!__init__` 方法" +",通常會在 :meth:`__post_init__` 方法中呼叫它: ::" #: ../../library/dataclasses.rst:591 msgid "" @@ -1177,31 +1119,28 @@ msgstr "" " super().__init__(self.side, self.side)" #: ../../library/dataclasses.rst:603 -#, fuzzy msgid "" "Note, however, that in general the dataclass-generated :meth:`!__init__` " "methods don't need to be called, since the derived dataclass will take care " "of initializing all fields of any base class that is a dataclass itself." msgstr "" -"但是請注意,通常不需要呼叫資料類別生成的 :meth:`!__init__` 方法,因為衍生資料" -"類別將負責初始化作為資料類別本身的任何基底類別的所有欄位。" +"不過請注意,一般來說並不需要呼叫由資料類別產生的 :meth:`!__init__` 方法,因為" +"衍生的資料類別會負責初始化任何本身也是資料類別的基底類別的所有欄位。" #: ../../library/dataclasses.rst:607 -#, fuzzy msgid "" "See the section below on init-only variables for ways to pass parameters to :" "meth:`!__post_init__`. Also see the warning about how :func:`replace` " "handles ``init=False`` fields." msgstr "" -"請參閱下面有關僅初始化變數的部分,了解將參數傳遞給 :meth:`!__post_init__` 的" -"方法。另請參閱有關 :func:`replace` 如何處理 ``init=False`` 欄位的警告。" +"關於將參數傳給 :meth:`!__post_init__` 的方式,請參閱下方有關僅初始化變數的章" +"節。另請參閱 :func:`replace` 如何處理 ``init=False`` 欄位的警告。" #: ../../library/dataclasses.rst:614 msgid "Class variables" msgstr "類別變數" #: ../../library/dataclasses.rst:616 -#, fuzzy msgid "" "One of the few places where :deco:`dataclass` actually inspects the type of " "a field is to determine if a field is a class variable as defined in :pep:" @@ -1210,19 +1149,17 @@ msgid "" "as a field and is ignored by the dataclass mechanisms. Such ``ClassVar`` " "pseudo-fields are not returned by the module-level :func:`fields` function." msgstr "" -":func:`@dataclass ` 實際檢查欄位型別的少數地方之一是確定欄位是否" -"是 :pep:`526` 中定義的類別變數。它透過檢查欄位的型別是否為 :data:`typing." -"ClassVar` 來做到這一點。如果一個欄位是一個 ``ClassVar``,它就被排除在考慮之" -"外,並被資料類別機制忽略。模組級 :func:`fields` 函式不會回傳此類別 " -"``ClassVar`` 偽欄位。" +":deco:`dataclass` 實際會檢查欄位型別的少數地方之一,是判斷欄位是否為 " +":pep:`526` 中定義的類別變數。它的做法是檢查欄位的型別是否為 " +":data:`typing.ClassVar`。如果一個欄位是 ``ClassVar``,它就不會被視為欄位,並" +"會被資料類別的機制忽略。這種 ``ClassVar`` 偽欄位不會由模組層級的 " +":func:`fields` 函式回傳。" #: ../../library/dataclasses.rst:627 -#, fuzzy msgid "Init-only variables" msgstr "僅初始化變數" #: ../../library/dataclasses.rst:629 -#, fuzzy msgid "" "Another place where :deco:`dataclass` inspects a type annotation is to " "determine if a field is an init-only variable. It does this by seeing if " @@ -1233,19 +1170,19 @@ msgid "" "`~object.__init__` method, and are passed to the optional :meth:" "`__post_init__` method. They are not otherwise used by dataclasses." msgstr "" -":func:`dataclass` 檢查型別註解的另一個地方是確定欄位是否是僅初始化變數。它通" -"過查看欄位的型別是否為 ``dataclasses.InitVar`` 型別來執行此操作。如果一個欄位" -"是一個 ``InitVar``,它被認為是一個偽欄位,稱為 init-only 欄位。由於它不是真正" -"的欄位,因此它不會由模組級 fields 函式回傳。 Init-only 欄位作為參數新增到生成" -"的 :meth:`~object.__init__` 方法,並傳遞給可選的 :meth:`__post_init__` 方法。" -"它們不被資料類別使用。" +":deco:`dataclass` 會檢查型別註釋的另一個地方,是判斷欄位是否為僅初始化變數。" +"它的做法是查看欄位的型別是否為 :class:`InitVar`。如果一個欄位是 " +":class:`InitVar`,它會被視為一個稱為僅初始化欄位的偽欄位。由於它並不是真正的" +"欄位,因此不會由模組層級的 :func:`fields` 函式回傳。僅初始化欄位會作為參數加" +"入產生的 :meth:`~object.__init__` 方法,並傳給選擇性的 :meth:`__post_init__` " +"方法。除此之外,資料類別不會使用它們。" #: ../../library/dataclasses.rst:639 -#, fuzzy msgid "" "For example, suppose a field will be initialized from a database, if a value " "is not provided when creating the class::" -msgstr "例如,假設一個欄位將從資料庫中初始化,如果在建立類別時沒有提供值: ::" +msgstr "" +"例如,假設某個欄位在建立類別時若沒有提供值,就會從資料庫進行初始化: ::" #: ../../library/dataclasses.rst:642 msgid "" @@ -1274,20 +1211,18 @@ msgstr "" "c = C(10, database=my_database)" #: ../../library/dataclasses.rst:654 -#, fuzzy msgid "" "In this case, :func:`fields` will return :class:`Field` objects for :attr:`!" "i` and :attr:`!j`, but not for :attr:`!database`." msgstr "" -"在這種情況下,:func:`fields` 將為 :attr:`!i` 和 :attr:`!j` 回傳 :class:" -"`Field` 物件,但不會為 :attr:`!database` 回傳。" +"在這種情況下,:func:`fields` 會回傳 :attr:`!i` 和 :attr:`!j` 的 " +":class:`Field` 物件,但不會回傳 :attr:`!database` 的。" #: ../../library/dataclasses.rst:660 msgid "Frozen instances" msgstr "凍結實例" #: ../../library/dataclasses.rst:662 -#, fuzzy msgid "" "It is not possible to create truly immutable Python objects. However, by " "passing ``frozen=True`` to the :deco:`dataclass` decorator you can emulate " @@ -1295,27 +1230,25 @@ msgid "" "__setattr__` and :meth:`~object.__delattr__` methods to the class. These " "methods will raise a :exc:`FrozenInstanceError` when invoked." msgstr "" -"不可能建立真正不可變的 Python 物件。但是,透過將 ``frozen=True`` 傳遞給 :" -"func:`@dataclass ` 裝飾器,你可以模擬不變性。在這種情況下,資料類" -"別將向類別新增 :meth:`~object.__setattr__` 和 :meth:`~object.__delattr__` 方" -"法。這些方法在叫用時會引發 :exc:`FrozenInstanceError`。" +"在 Python 中無法建立真正不可變的物件。不過,透過將 ``frozen=True`` 傳給 " +":deco:`dataclass` 裝飾器,你可以模擬不可變性。在這種情況下,資料類別會在類別" +"中加入 :meth:`~object.__setattr__` 和 :meth:`~object.__delattr__` 方法。這些" +"方法在被呼叫時會引發 :exc:`FrozenInstanceError`。" #: ../../library/dataclasses.rst:668 -#, fuzzy msgid "" "There is a tiny performance penalty when using ``frozen=True``: :meth:" "`~object.__init__` cannot use simple assignment to initialize fields, and " "must use :meth:`!object.__setattr__`." msgstr "" -"使用 ``frozen=True`` 時有一個微小的性能損失::meth:`~object.__init__` 不能使" -"用簡單賦值來初始化欄位,必須使用 :meth:`!object.__setattr__`。" +"使用 ``frozen=True`` 時會有些微的效能損失::meth:`~object.__init__` 無法使用" +"簡單的賦值來初始化欄位,而必須使用 :meth:`!object.__setattr__`。" #: ../../library/dataclasses.rst:677 msgid "Inheritance" msgstr "繼承" #: ../../library/dataclasses.rst:679 -#, fuzzy msgid "" "When the dataclass is being created by the :deco:`dataclass` decorator, it " "looks through all of the class's base classes in reverse MRO (that is, " @@ -1326,11 +1259,11 @@ msgid "" "ordered mapping of fields. Because the fields are in insertion order, " "derived classes override base classes. An example::" msgstr "" -"當 :func:`@dataclass ` 裝飾器建立資料類別時,它會以反向 MRO(即" -"從 :class:`object` 開始)查看該類別的所有基底類別,並且對於它找到的每個資料類" -"別,將該基底類別中的欄位新增到欄位的有序對映中。新增所有基底類別欄位後,它會" -"將自己的欄位新增到有序對映中。所有生成的方法都將使用這種組合的、計算的有序欄" -"位對映。因為欄位是按插入順序排列的,所以衍生類別會覆蓋基底類別。一個例子: ::" +"當 :deco:`dataclass` 裝飾器在建立資料類別時,它會以反向 MRO(也就是從 " +":class:`object` 開始)走訪該類別的所有基底類別,並對找到的每個資料類別,將該" +"基底類別的欄位加入一個有序的欄位對映中。加入所有基底類別的欄位之後,它會再把" +"自己的欄位加入這個有序對映。所有產生的方法都會使用這個合併計算後的有序欄位對" +"映。由於欄位是依插入順序排列的,衍生類別會覆寫基底類別。範例: ::" #: ../../library/dataclasses.rst:689 msgid "" @@ -1355,52 +1288,47 @@ msgstr "" " x: int = 15" #: ../../library/dataclasses.rst:699 -#, fuzzy msgid "" "The final list of fields is, in order, :attr:`!x`, :attr:`!y`, :attr:`!z`. " "The final type of :attr:`!x` is :class:`int`, as specified in class :class:`!" "C`." msgstr "" -"最終的欄位列表按順序為 :attr:`!x`、:attr:`!y`、:attr:`!z`。:attr:`!x` 的最終" -"型別是 :class:`int`,如類別 :class:`!C` 中指定的那樣。" +"最終的欄位串列依序為 :attr:`!x`、:attr:`!y`、:attr:`!z`。:attr:`!x` 的最終型" +"別是 :class:`int`,如類別 :class:`!C` 中所指定。" #: ../../library/dataclasses.rst:702 -#, fuzzy msgid "" "The generated :meth:`~object.__init__` method for :class:`!C` will look " "like::" -msgstr "為 :class:`!C` 生成的 :meth:`~object.__init__` 方法將如下所示: ::" +msgstr "為 :class:`!C` 產生的 :meth:`~object.__init__` 方法會如下所示: ::" #: ../../library/dataclasses.rst:704 msgid "def __init__(self, x: int = 15, y: int = 0, z: int = 10):" msgstr "def __init__(self, x: int = 15, y: int = 0, z: int = 10):" #: ../../library/dataclasses.rst:707 -#, fuzzy msgid "Re-ordering of keyword-only parameters in :meth:`!__init__`" -msgstr ":meth:`!__init__` 中僅關鍵字參數的重新排序" +msgstr ":meth:`!__init__` 中僅限關鍵字參數的重新排序" #: ../../library/dataclasses.rst:709 -#, fuzzy msgid "" "After the parameters needed for :meth:`~object.__init__` are computed, any " "keyword-only parameters are moved to come after all regular (non-keyword-" "only) parameters. This is a requirement of how keyword-only parameters are " "implemented in Python: they must come after non-keyword-only parameters." msgstr "" -"在計算 :meth:`__init__` 所需的參數後,任何僅關鍵字參數都將移動到所有常規(非" -"僅關鍵字)參數之後。這是如何在 Python 中實作僅關鍵字參數的要求:它們必須位於" -"非僅關鍵字參數之後。" +"在計算出 :meth:`~object.__init__` 所需的參數之後,所有僅限關鍵字參數都會被移" +"到所有一般(非僅限關鍵字)參數之後。這是 Python 實作僅限關鍵字參數的方式所要" +"求的:它們必須位於非僅限關鍵字參數之後。" #: ../../library/dataclasses.rst:715 -#, fuzzy msgid "" "In this example, :attr:`!Base.y`, :attr:`!Base.w`, and :attr:`!D.t` are " "keyword-only fields, and :attr:`!Base.x` and :attr:`!D.z` are regular " "fields::" msgstr "" -"在此示例中,:attr:`!Base.y`、:attr:`!Base.w` 和 :attr:`!D.t` 是僅限關鍵字的欄" -"位,:attr:`!Base.x` 和 :attr:`!D.z` 是常規欄位: ::" +"在這個範例中,:attr:`!Base.y`、:attr:`!Base.w` 和 :attr:`!D.t` 是僅限關鍵字欄" +"位,而 :attr:`!Base.x` 和 :attr:`!D.z` 是一般欄位: ::" #: ../../library/dataclasses.rst:718 msgid "" @@ -1429,9 +1357,8 @@ msgstr "" " t: int = field(kw_only=True, default=0)" #: ../../library/dataclasses.rst:730 -#, fuzzy msgid "The generated :meth:`!__init__` method for :class:`!D` will look like::" -msgstr "為 :class:`!D` 生成的 :meth:`!__init__` 方法將如下所示: ::" +msgstr "為 :class:`!D` 產生的 :meth:`!__init__` 方法會如下所示: ::" #: ../../library/dataclasses.rst:732 msgid "" @@ -1442,42 +1369,40 @@ msgstr "" "int = 0):" #: ../../library/dataclasses.rst:734 -#, fuzzy msgid "" "Note that the parameters have been re-ordered from how they appear in the " "list of fields: parameters derived from regular fields are followed by " "parameters derived from keyword-only fields." msgstr "" -"請注意,參數已根據它們在欄位列表中的顯示方式重新排序:從常規欄位衍生的參數後" -"跟從僅關鍵字欄位衍生的參數。" +"請注意,參數已經從它們在欄位串列中出現的順序重新排序:由一般欄位衍生的參數排" +"在前面,接著才是由僅限關鍵字欄位衍生的參數。" #: ../../library/dataclasses.rst:738 -#, fuzzy msgid "" "The relative ordering of keyword-only parameters is maintained in the re-" "ordered :meth:`!__init__` parameter list." -msgstr "僅關鍵字參數的相對順序在重新排序的 :meth:`!__init__` 參數列表中維護。" +msgstr "" +"在重新排序後的 :meth:`!__init__` 參數串列中,僅限關鍵字參數之間的相對順序會維" +"持不變。" #: ../../library/dataclasses.rst:743 msgid "Default factory functions" msgstr "預設工廠函式" #: ../../library/dataclasses.rst:745 -#, fuzzy msgid "" "If a :func:`field` specifies a *default_factory*, it is called with zero " "arguments when a default value for the field is needed. For example, to " "create a new instance of a list, use::" msgstr "" -"如果 :func:`field` 指定了 *default_factory*,當需要該欄位的預設值時,它會以零" -"引數呼叫。例如,要建立列表的新實例,請使用: ::" +"如果 :func:`field` 指定了 *default_factory*,當需要該欄位的預設值時,會以零個" +"引數呼叫它。例如,若要建立一個新的 list 實例,請使用: ::" #: ../../library/dataclasses.rst:749 msgid "mylist: list = field(default_factory=list)" msgstr "mylist: list = field(default_factory=list)" #: ../../library/dataclasses.rst:751 -#, fuzzy msgid "" "If a field is excluded from :meth:`~object.__init__` (using ``init=False``) " "and the field also specifies *default_factory*, then the default factory " @@ -1485,21 +1410,21 @@ msgid "" "function. This happens because there is no other way to give the field an " "initial value." msgstr "" -"如果一個欄位從 :meth:`~object.__init__` 中排除(使用 ``init=False``)並且該欄" -"位還指定了 ``default_factory``,那麼預設工廠函式將始終從生成的 :meth:" -"`__init__ 中呼叫`功能。發生這種情況是因為沒有其他方法可以為該欄位賦予初始值。" +"如果某個欄位被排除在 :meth:`~object.__init__` 之外(使用 ``init=False``),而" +"該欄位又指定了 *default_factory*,那麼產生的 :meth:`!__init__` 函式一定會呼叫" +"這個預設工廠函式。這是因為沒有其他方式可以為該欄位提供初始值。" #: ../../library/dataclasses.rst:758 msgid "Mutable default values" msgstr "可變預設值" #: ../../library/dataclasses.rst:760 -#, fuzzy msgid "" "Python stores default member variable values in class attributes. Consider " "this example, not using dataclasses::" msgstr "" -"Python 將預設成員變數值儲存在類別屬性中。考慮這個例子,不使用資料類別: ::" +"Python 會將預設的成員變數值儲存在類別屬性中。請看這個沒有使用資料類別的範例:" +" ::" #: ../../library/dataclasses.rst:763 msgid "" @@ -1528,18 +1453,15 @@ msgstr "" "assert o1.x is o2.x" #: ../../library/dataclasses.rst:775 -#, fuzzy msgid "" "Note that the two instances of class :class:`!C` share the same class " "variable :attr:`!x`, as expected." msgstr "" -"請注意,類別 :class:`!C` 的兩個實例共享同一個類別變數 :attr:`!x`,正如預期的" -"那樣。" +"請注意,如預期般,類別 :class:`!C` 的兩個實例共用同一個類別變數 :attr:`!x`。" #: ../../library/dataclasses.rst:778 -#, fuzzy msgid "Using dataclasses, *if* this code was valid::" -msgstr "使用資料類別,*如果*\\ 此程式碼有效: ::" +msgstr "使用資料類別時,*如果*\\ 這段程式碼是有效的: ::" #: ../../library/dataclasses.rst:780 msgid "" @@ -1557,7 +1479,7 @@ msgstr "" #: ../../library/dataclasses.rst:786 msgid "it would generate code similar to::" -msgstr "它會生成類似的程式碼: ::" +msgstr "它會產生類似的程式碼: ::" #: ../../library/dataclasses.rst:788 msgid "" @@ -1580,7 +1502,6 @@ msgstr "" "assert D().x is D().x" #: ../../library/dataclasses.rst:797 -#, fuzzy msgid "" "This has the same issue as the original example using class :class:`!C`. " "That is, two instances of class :class:`!D` that do not specify a value for :" @@ -1592,19 +1513,20 @@ msgid "" "is that if a value is unhashable, it is mutable. This is a partial " "solution, but it does protect against many common errors." msgstr "" -"這與使用類別 :class:`!C` 的原始示例存在相同的問題。也就是說,類別 :class:`!" -"D` 的兩個實例在建立類別實例時沒有為 :attr:`!x` 指定值,它們將共享 :attr:`!x` " -"的同一個副本。因為資料類別只是使用普通的 Python 類別建立,所以它們也有這種行" -"為。資料類別沒有通用的方法來檢測這種情況。相反,如果 :func:`dataclass` 裝飾器" -"檢測到不可雜湊的預設參數,它將引發 :exc:`TypeError`。假設是如果一個值是不可散" -"列的,那麼它就是可變的。這是一個部分解決方案,但它確實可以防止許多常見錯誤。" +"這和最初使用類別 :class:`!C` 的範例有相同的問題。也就是說,類別 :class:`!D` " +"的兩個實例若在建立時沒有為 :attr:`!x` 指定值,就會共用同一個 :attr:`!x` 副本" +"。由於資料類別只是使用一般的 Python 類別建立方式,它們也有相同的行為。資料類" +"別沒有通用的方法可以偵測這種情況。取而代之的是,如果 :deco:`dataclass` 裝飾器" +"偵測到不可雜湊的預設參數,就會引發 :exc:`ValueError`。這背後的假設是:如果一" +"個值是不可雜湊的,它就是可變的。這只是部分的解決方案,但確實能防止許多常見的" +"錯誤。" #: ../../library/dataclasses.rst:808 -#, fuzzy msgid "" "Using default factory functions is a way to create new instances of mutable " "types as default values for fields::" -msgstr "使用預設工廠函式是一種建立可變型別的新實例作為欄位預設值的方法: ::" +msgstr "" +"使用預設工廠函式,是為欄位建立可變型別的新實例作為預設值的一種方式: ::" #: ../../library/dataclasses.rst:811 msgid "" @@ -1621,50 +1543,44 @@ msgstr "" "assert D().x is not D().x" #: ../../library/dataclasses.rst:817 -#, fuzzy msgid "" "Instead of looking for and disallowing objects of type :class:`list`, :class:" "`dict`, or :class:`set`, unhashable objects are now not allowed as default " "values. Unhashability is used to approximate mutability." msgstr "" -"不再查找和禁止型別為 :class:`list`、:class:`dict` 或 :class:`set` 的物件,現" -"在不允許使用不可雜湊的物件作為預設值。不可雜湊性用於近似可變性。" +"現在不再是尋找並禁止型別為 :class:`list`、:class:`dict` 或 :class:`set` 的物" +"件,而是不允許使用不可雜湊的物件作為預設值。不可雜湊性被用來近似判斷可變性。" #: ../../library/dataclasses.rst:824 -#, fuzzy msgid "Descriptor-typed fields" msgstr "描述器型別的欄位" #: ../../library/dataclasses.rst:826 -#, fuzzy msgid "" "Fields that are assigned :ref:`descriptor objects ` as their " "default value have the following special behaviors:" msgstr "" -"指定為\\ :ref:`描述器物件 `\\ 作為預設值的欄位具有以下特殊行為:" +"以\\ :ref:`描述器物件 `\\ 作為預設值的欄位具有以下特殊行為:" #: ../../library/dataclasses.rst:829 -#, fuzzy msgid "" "The value for the field passed to the dataclass's :meth:`~object.__init__` " "method is passed to the descriptor's :meth:`~object.__set__` method rather " "than overwriting the descriptor object." msgstr "" -"傳遞給資料類別的 :meth:`~object.__init__` 方法的欄位值被傳遞給描述器的 :meth:" -"`~object.__set__` 方法,而不是覆蓋描述器物件。" +"傳給資料類別 :meth:`~object.__init__` 方法的欄位值,會被傳給描述器的 " +":meth:`~object.__set__` 方法,而不是覆寫描述器物件。" #: ../../library/dataclasses.rst:833 -#, fuzzy msgid "" "Similarly, when getting or setting the field, the descriptor's :meth:" "`~object.__get__` or :meth:`!__set__` method is called rather than returning " "or overwriting the descriptor object." msgstr "" -"同樣,在取得或設定欄位時,將呼叫描述器的 :meth:`~object.__get__` 或 :meth:`!" -"__set__` 方法,而不是回傳或覆蓋描述器物件。" +"同樣地,在取得或設定該欄位時,會呼叫描述器的 :meth:`~object.__get__` 或 " +":meth:`!__set__` 方法,而不是回傳或覆寫描述器物件。" #: ../../library/dataclasses.rst:837 -#, fuzzy msgid "" "To determine whether a field contains a default value, :deco:`dataclass` " "will call the descriptor's :meth:`!__get__` method using its class access " @@ -1673,11 +1589,10 @@ msgid "" "hand, if the descriptor raises :exc:`AttributeError` in this situation, no " "default value will be provided for the field." msgstr "" -"為了確定一個欄位是否包含預設值,:func:`@dataclass ` 將使用其類別存" -"取形式呼叫描述器的 :meth:`!__get__` 方法(即 ``descriptor.__get__(obj=None, " -"type=cls)``。如果在這種情況下,描述器回傳一個值,它將用作欄位的預設值。另一方" -"面,如果描述器在這種情況下引發 :exc:`AttributeError`,則不會為該欄位提供預設" -"值。" +"為了判斷欄位是否包含預設值,:deco:`dataclass` 會以類別存取的形式呼叫描述器的 " +":meth:`!__get__` 方法:``descriptor.__get__(obj=None, type=cls)``。如果描述器" +"在這種情況下回傳了一個值,該值就會被用作欄位的預設值。反之,如果描述器在這種" +"情況下引發 :exc:`AttributeError`,則該欄位不會有預設值。" #: ../../library/dataclasses.rst:847 msgid "" @@ -1734,11 +1649,10 @@ msgstr "" "print(i.quantity_on_hand) # 2" #: ../../library/dataclasses.rst:872 -#, fuzzy msgid "" "Note that if a field is annotated with a descriptor type, but is not " "assigned a descriptor object as its default value, the field will act like a " "normal field." msgstr "" -"請注意,如果一個欄位用描述器型別註釋,但未分配描述器物件作為其預設值,則該欄" -"位將像普通欄位一樣工作。" +"請注意,如果一個欄位以描述器型別進行註釋,但沒有被指派描述器物件作為預設值," +"該欄位就會像一般欄位一樣運作。" From 1b4d6bf5335b5dc6983179f8871331ff7d12935d Mon Sep 17 00:00:00 2001 From: josix Date: Sun, 11 Oct 2026 10:11:24 +0800 Subject: [PATCH 2/6] Add translation-memory lookup to Claude skills via agentic-retrieval Add .scripts/translation_memory.sh and .scripts/translation_memory/main.py, a stdlib-only helper that indexes this repo's .po files with agentic-retrieval (lexical retriever, cjk tokenizer) and returns similar already-translated entries as msgid/msgstr pairs, filtering out fuzzy and untranslated ones. The engine is located via AGENTIC_RETRIEVAL_ENGINE or a `retrieval` command on PATH; the helper exits 3 with a hint otherwise. translate-po, check-terminology and validate-translation gain a lookup step that treats these hits as reference only (glossary and README rules still win) and skips silently when the helper is unavailable. README documents setup and usage; CLAUDE.md gains a pointer and a corrected line range. The index directory .agentic-retrieval/ is gitignored. Co-Authored-By: Claude Fable 5.1 --- .claude/skills/check-terminology/SKILL.md | 10 +- .claude/skills/translate-po/SKILL.md | 29 ++- .claude/skills/validate-translation/SKILL.md | 5 + .gitignore | 3 + .scripts/translation_memory.sh | 3 + .scripts/translation_memory/main.py | 219 +++++++++++++++++++ CLAUDE.md | 3 +- README.rst | 38 ++++ 8 files changed, 306 insertions(+), 4 deletions(-) create mode 100755 .scripts/translation_memory.sh create mode 100644 .scripts/translation_memory/main.py diff --git a/.claude/skills/check-terminology/SKILL.md b/.claude/skills/check-terminology/SKILL.md index 0a5bc5e0176..6a1639248bd 100644 --- a/.claude/skills/check-terminology/SKILL.md +++ b/.claude/skills/check-terminology/SKILL.md @@ -13,6 +13,7 @@ Check terminology against project glossary and zh_CN/zh_TW mappings. 1. **Primary:** `glossary.po` - Official Python glossary translations 2. **Secondary:** `focused_terminology_dictionary.csv` - 118 key terms 3. **Tertiary:** `.scripts/google_translate/utils.py` - zh_CN to zh_TW mappings +4. **Usage evidence (not authority):** `.scripts/translation_memory.sh query ""` - how existing translated .po entries render the term (skip if the command fails) ## Check Process @@ -21,12 +22,14 @@ Check terminology against project glossary and zh_CN/zh_TW mappings. 2. Check if it's in `references/keep-in-english.md` 3. If found, use the standard translation 4. If not found, check `glossary.po` in project root +5. Run `.scripts/translation_memory.sh query "" --top-k 10` to see existing renderings with `path:line`; if the glossary is silent, report the dominant rendering as a suggestion ### For a PO File 1. Extract all translated terms from msgstr 2. Cross-reference against glossary 3. Identify forbidden zh_CN terms -4. Report inconsistencies +4. For each term that looks inconsistent, run `.scripts/translation_memory.sh query "" --top-k 10` and compare renderings across files +5. Report inconsistencies, citing `path:line` from the lookup ## Error Categories @@ -41,6 +44,10 @@ Example violations: ### Category 2: Inconsistent Terms (Should Review) Same English term translated differently across files. +Gather evidence with the translation memory lookup and cite each rendering with +`path:line`. The glossary decides; majority usage only informs a suggestion +when the glossary is silent. + ### Category 3: Missing from Glossary (Informational) New terms not yet in the glossary. @@ -71,6 +78,7 @@ Decision Log: 1. Check `../translate-po/references/terminology.md` first (authoritative, 186 terms) 2. If term should stay in English, see `references/keep-in-english.md` +3. To see how a term is actually rendered across files: `.scripts/translation_memory.sh query ""` ## References diff --git a/.claude/skills/translate-po/SKILL.md b/.claude/skills/translate-po/SKILL.md index 0c8b21e000b..d25b1866354 100644 --- a/.claude/skills/translate-po/SKILL.md +++ b/.claude/skills/translate-po/SKILL.md @@ -15,6 +15,27 @@ Translate English PO msgid entries to Traditional Chinese msgstr following all p 3. Look up terminology in `references/terminology.md` 4. Check `references/forbidden-terms.md` for zh_CN terms to avoid +## Translation Memory Lookup + +Before translating an entry that contains prose, look up how similar text was +already translated in this repo: + +```bash +.scripts/translation_memory.sh query "" +``` + +- Prints up to 5 translated, non-fuzzy entries as `path:line` followed by + `msgid:` / `msgstr:` lines (use `--top-k N` for more). +- To see how one term is rendered elsewhere, query the term alone + (e.g. `"context manager"`) or a candidate Chinese rendering of 2+ characters + (e.g. `"情境管理器"`). +- Treat hits as reference for phrasing and term choice, not as authority: + `references/terminology.md`, `glossary.po`, and the rules below win when they + disagree, and existing translations may contain mistakes. +- Skip the lookup for code-only entries and very short entries. +- If the command fails or prints nothing (for example, the engine is not + installed), continue without it and do not report the failure. + ## Translation Process ### Step 1: Content Analysis @@ -50,8 +71,10 @@ See `references/rst-cheatsheet.md` for complete rules. 1. Check term in `references/terminology.md` 2. If not found, check `glossary.po` in project root -3. If uncertain, add `#, fuzzy` flag -4. Never use zh_CN terms from `references/forbidden-terms.md` +3. If still not found, run `.scripts/translation_memory.sh query ""` and + follow the dominant existing rendering +4. If uncertain, add `#, fuzzy` flag +5. Never use zh_CN terms from `references/forbidden-terms.md` ## Output Format @@ -71,6 +94,7 @@ Before outputting, verify: - [ ] reST syntax preserved - [ ] Terminology matches glossary - [ ] No forbidden zh_CN terms +- [ ] Consistent with translation memory hits, unless they conflict with the glossary ## Examples @@ -130,3 +154,4 @@ msgstr "可能需要審核的翻譯" - `references/terminology.md` - Key term translations (186 terms) - `references/forbidden-terms.md` - zh_CN to zh_TW mappings - `references/rst-cheatsheet.md` - reST syntax rules +- `.scripts/translation_memory.sh` - Translation memory lookup over this repo's .po files diff --git a/.claude/skills/validate-translation/SKILL.md b/.claude/skills/validate-translation/SKILL.md index 77b8fc19eca..ccc8cf604be 100644 --- a/.claude/skills/validate-translation/SKILL.md +++ b/.claude/skills/validate-translation/SKILL.md @@ -14,6 +14,10 @@ Validate translated PO entries against project rules and report issues. - Compare terms against `glossary.po` and terminology dictionary - Flag zh_CN terms that should be zh_TW (see `references/validation-rules.md`) - Check for inconsistent translations of the same term + - Run `.scripts/translation_memory.sh query "" --top-k 10` to + compare with how other files render it. Report a deviation from the + dominant rendering as WARNING only when `glossary.po` and the terminology + dictionary are silent. Skip silently if the command fails. ### 2. Punctuation Rules - Chinese text must use full-width: `「」()、,。:;!?` @@ -56,3 +60,4 @@ After review, run: `make lint`, `make wrap`, `make build .po` ## References - `references/validation-rules.md` - Complete validation checklist +- `.scripts/translation_memory.sh` - Translation memory lookup (existing renderings with `path:line`) diff --git a/.gitignore b/.gitignore index 948c289ec66..5397c0884b6 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ venv/ ENV/ env.bak/ venv.bak/ + +# agentic-retrieval translation memory index +.agentic-retrieval/ diff --git a/.scripts/translation_memory.sh b/.scripts/translation_memory.sh new file mode 100755 index 00000000000..11d08379852 --- /dev/null +++ b/.scripts/translation_memory.sh @@ -0,0 +1,3 @@ +#!/bin/sh +# Translation memory lookup over this repo's .po files (see translation_memory/main.py). +exec python3 "$(dirname "$0")/translation_memory/main.py" "$@" diff --git a/.scripts/translation_memory/main.py b/.scripts/translation_memory/main.py new file mode 100644 index 00000000000..64bba9c66c2 --- /dev/null +++ b/.scripts/translation_memory/main.py @@ -0,0 +1,219 @@ +"""Translation memory lookup over this repo's .po files via agentic-retrieval. + +Usage (run from anywhere; the repo root is always the index root):: + + .scripts/translation_memory.sh index [--force] + .scripts/translation_memory.sh query "" [--top-k N] [--fetch N] + [--include-fuzzy] [--include-untranslated] [--json] [--max-chars N] + +Engine resolution: + +1. ``AGENTIC_RETRIEVAL_ENGINE`` = path to an agentic-retrieval ``engine`` + directory (needs ``uv``); run with ``uv run --frozen``. +2. Otherwise a ``retrieval`` executable on ``PATH``. +3. Otherwise exit with status 3. + +The index lives in ``.agentic-retrieval/`` (gitignored). ``query`` always runs +``index`` first so the cjk tokenizer is guaranteed; a fresh index is a cheap +no-op. Exit codes: 0 ok, 2 usage error, 3 engine unavailable or failed. +Supports macOS and Linux only. +""" + +import argparse +import json +import os +import shutil +import subprocess +import sys +import time +from pathlib import Path +from typing import Any, Dict, List + +REPO_ROOT = Path(__file__).resolve().parents[2] +ENV_VAR = "AGENTIC_RETRIEVAL_ENGINE" +EXIT_UNAVAILABLE = 3 + + +def fail(message: str) -> None: + print(f"translation-memory: {message}", file=sys.stderr) + sys.exit(EXIT_UNAVAILABLE) + + +def resolve_engine_cmd() -> List[str]: + engine = os.environ.get(ENV_VAR) + if engine: + if not (Path(engine) / "pyproject.toml").is_file(): + fail( + f"{ENV_VAR}={engine} is not an agentic-retrieval engine " + "directory (no pyproject.toml)" + ) + if not shutil.which("uv"): + fail( + "'uv' not found on PATH (needed to run the engine from " + f"{ENV_VAR})" + ) + return ["uv", "run", "--frozen", "--quiet", "--directory", engine, + "retrieval"] + found = shutil.which("retrieval") + if found: + return [found] + fail( + "agentic-retrieval engine not found. Either\n" + f" export {ENV_VAR}=/path/to/agentic-retrieval/engine\n" + "or install it so that 'retrieval' is on PATH." + ) + return [] # unreachable + + +def run_engine(engine_cmd: List[str], args: List[str]) -> str: + try: + proc = subprocess.run( + engine_cmd + args, capture_output=True, encoding="utf-8" + ) + except OSError as exc: + fail(f"cannot run engine: {exc}") + if proc.returncode != 0: + sys.stderr.write(proc.stdout or "") + sys.stderr.write(proc.stderr or "") + fail(f"engine exited with status {proc.returncode}") + return proc.stdout + + +def run_index(engine_cmd: List[str], force: bool) -> None: + args = ["index", "--root", str(REPO_ROOT), "--retriever", "lexical", + "--tokenizer", "cjk"] + if force: + args.append("--force") + out = run_engine(engine_cmd, args) + if out.strip(): + sys.stderr.write(out) + + +def cmd_index(args: argparse.Namespace) -> int: + start = time.monotonic() + run_index(resolve_engine_cmd(), args.force) + print( + f"translation-memory: index ready in {time.monotonic() - start:.1f}s", + file=sys.stderr, + ) + return 0 + + +def keep_hit(meta: Dict[str, Any], args: argparse.Namespace) -> bool: + if meta.get("kind") != "po_entry": + return False + if not args.include_untranslated and not meta.get("translated"): + return False + if not args.include_fuzzy and meta.get("fuzzy"): + return False + return True + + +def collect_entries( + hits: List[Dict[str, Any]], args: argparse.Namespace +) -> List[Dict[str, Any]]: + entries: List[Dict[str, Any]] = [] + seen = set() + for hit in hits: + meta = hit.get("metadata") or {} + if not keep_hit(meta, args): + continue + key = (meta.get("msgid"), meta.get("msgstr")) + if key in seen: + continue + seen.add(key) + path = hit.get("path", "") + if os.path.isabs(path): + path = os.path.relpath(path, REPO_ROOT) + entry = { + "path": path, + "line": meta.get("line"), + "msgid": meta.get("msgid", ""), + "msgstr": meta.get("msgstr", ""), + "fuzzy": bool(meta.get("fuzzy")), + "translated": bool(meta.get("translated")), + "references": meta.get("references") or [], + } + if meta.get("msgctxt"): + entry["msgctxt"] = meta["msgctxt"] + entries.append(entry) + if len(entries) >= args.top_k: + break + return entries + + +def compact(text: str, max_chars: int) -> str: + text = " ".join(str(text).split()) + if max_chars and len(text) > max_chars: + return text[:max_chars].rstrip() + "..." + return text + + +def format_entry(entry: Dict[str, Any], max_chars: int) -> str: + header = f"{entry['path']}:{entry['line']}" + if entry["fuzzy"]: + header += " [fuzzy]" + if not entry["translated"]: + header += " [untranslated]" + lines = [header] + if "msgctxt" in entry: + lines.append(f" msgctxt: {compact(entry['msgctxt'], max_chars)}") + lines.append(f" msgid: {compact(entry['msgid'], max_chars)}") + lines.append(f" msgstr: {compact(entry['msgstr'], max_chars)}") + return "\n".join(lines) + + +def cmd_query(args: argparse.Namespace) -> int: + engine_cmd = resolve_engine_cmd() + run_index(engine_cmd, False) + fetch = args.fetch or max(3 * args.top_k, 20) + out = run_engine( + engine_cmd, + ["query", args.text, "--root", str(REPO_ROOT), "--retriever", + "lexical", "--top-k", str(fetch), "--json", "--stale-ok"], + ) + try: + hits = json.loads(out).get("results", []) + except ValueError as exc: + fail(f"engine returned invalid JSON: {exc}") + entries = collect_entries(hits, args) + if args.json: + print(json.dumps(entries, ensure_ascii=False, indent=2)) + elif entries: + print("\n\n".join(format_entry(e, args.max_chars) for e in entries)) + if not entries: + print("translation-memory: no matching translated entries", + file=sys.stderr) + return 0 + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Translation memory over this repo's .po files." + ) + sub = parser.add_subparsers(dest="command", required=True) + p_index = sub.add_parser("index", help="build or refresh the index") + p_index.add_argument("--force", action="store_true") + p_index.set_defaults(func=cmd_index) + p_query = sub.add_parser("query", help="find similar translated entries") + p_query.add_argument("text") + p_query.add_argument("--top-k", type=int, default=5) + p_query.add_argument("--fetch", type=int, default=0, + help="hits to fetch before filtering " + "(default max(3*top-k, 20))") + p_query.add_argument("--include-fuzzy", action="store_true") + p_query.add_argument("--include-untranslated", action="store_true") + p_query.add_argument("--json", action="store_true") + p_query.add_argument("--max-chars", type=int, default=300, + help="truncate each field (0 = no truncation)") + p_query.set_defaults(func=cmd_query) + return parser + + +def main() -> int: + args = build_parser().parse_args() + return args.func(args) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/CLAUDE.md b/CLAUDE.md index 198852d160d..16708b2e793 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,5 +39,6 @@ make progress # Check translation progress - **Terminology:** `focused_terminology_dictionary.csv`, `glossary.po` - **zh_CN corrections:** `.scripts/google_translate/utils.py` -- **Full guidelines:** `README.rst` (lines 240-377) +- **Full guidelines:** `README.rst` (lines 240-368) - **Skills:** `.claude/skills/` for detailed translation workflows +- **Translation memory:** `.scripts/translation_memory.sh query ""` - similar translated entries from this repo's .po files (needs `AGENTIC_RETRIEVAL_ENGINE` or `retrieval` on PATH; see README.rst) diff --git a/README.rst b/README.rst index 633546a0720..9c3d06ecc0d 100644 --- a/README.rst +++ b/README.rst @@ -450,6 +450,44 @@ Skill 會將發現的問題分類為 ERROR、WARNING、INFO 並逐項列出, Skill 產生的譯文僅供輔助,送出 Pull Request 前請務必人工確認每一條翻譯是否\ 正確、是否符合\ `翻譯守則`_\ 。 +翻譯記憶 (Translation Memory) +------------------------------ + +``translate-po``\ 、\ ``check-terminology`` 與 ``validate-translation`` 會在翻譯\ +或校對時,從本專案既有的 po 檔中查詢相似的已翻譯條目與用詞的翻譯方式作為參考。\ +此功能使用 `agentic-retrieval `_ +建立索引;未安裝時 Skill 會自動略過此步驟。查詢結果僅供參考,用詞仍以\ +`術語表 Glossary`_\ 為準。 + +事前準備(目前僅支援 macOS 與 Linux): + +- 安裝 `uv `_\ 。 +- 取得 agentic-retrieval 的原始碼,並將環境變數指向其中的 ``engine`` 目錄: + + .. code-block:: sh + + export AGENTIC_RETRIEVAL_ENGINE=/path/to/agentic-retrieval/engine + +- agentic-retrieval 正式發布後,也可以改用 ``uv tool install`` 安裝,讓\ + ``retrieval`` 指令出現在 ``PATH`` 中,就不需要設定環境變數: + + .. code-block:: sh + + uv tool install "agentic-retrieval @ git+https://github.com/josix/agentic-retrieval.git#subdirectory=engine" + +使用方式: + +.. code-block:: sh + + .scripts/translation_memory.sh index # 建立或更新索引 + .scripts/translation_memory.sh query "cache decorator" # 以英文查詢 + .scripts/translation_memory.sh query "快取 裝飾器" # 以中文查詢 + +索引存放在專案根目錄的 ``.agentic-retrieval/``\ (已列入 ``.gitignore``\ )。專案\ +檔案更新後(例如 ``git pull``\ ),下一次查詢會自動重建索引。預設只列出已翻譯且\ +未標記 fuzzy 的條目,可加上 ``--include-fuzzy`` 或 ``--include-untranslated`` +調整。 + 術語表 Glossary =============== From a051272563c323b7338af498ea962a2828bd3d38 Mon Sep 17 00:00:00 2001 From: josix Date: Sun, 25 Oct 2026 00:24:22 +0800 Subject: [PATCH 3/6] Polish dataclasses/faq-library translations after reviewing rendered HTML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix problems that only show on the built pages: - ``field``\ 可以 rendered as "field可以"; use a normal space - 全域直譯器鎖(GIL) used half-width parens and left a stray space; use \ (GIL) directly followed by the next word - link text after 的 had an extra leading space; join it with \ - 錯誤代碼 -> 錯誤碼 (zh_CN wording) - smoother wording for the *match_args* and *frozen* parameters Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01H2AUU3pZYEkfs9BY3mm7bn --- faq/library.po | 24 ++++++++++++------------ library/dataclasses.po | 10 +++++----- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/faq/library.po b/faq/library.po index 1148a6225c4..5b2c7470ffc 100644 --- a/faq/library.po +++ b/faq/library.po @@ -617,11 +617,11 @@ msgid "" "instruction and therefore all the C implementation code reached from each " "instruction is therefore atomic from the point of view of a Python program." msgstr "" -"Python 內部使用\\ :term:`全域直譯器鎖 `\\ (GIL) 來確" -"保同一時間只有一個執行緒在 Python VM 中執行。一般來說,Python 只會在位元組碼" -"指令之間切換執行緒;切換的頻率可以透過 :func:`sys.setswitchinterval` 設定。因" -"此,從 Python 程式的角度來看,每一條位元組碼指令,以及每條指令所執行到的所有 " -"C 實作程式碼,都是不可分割的(atomic)。" +"Python 內部使用\\ :term:`全域直譯器鎖 `\\ (GIL)來" +"確保同一時間只有一個執行緒在 Python VM 中執行。一般來說,Python 只會在位元組" +"碼指令之間切換執行緒;切換的頻率可以透過 :func:`sys.setswitchinterval` 設定。" +"因此,從 Python 程式的角度來看,每一條位元組碼指令,以及每條指令所執行到的所" +"有 C 實作程式碼,都是不可分割的(atomic)。" #: ../../faq/library.rst:372 msgid "" @@ -706,9 +706,9 @@ msgid "" "multi-threaded Python program effectively only uses one CPU, due to the " "insistence that (almost) all Python code can only run while the GIL is held." msgstr "" -":term:`全域直譯器鎖 `\\ (GIL) 常被視為在高階多處理器" -"伺服器上部署 Python 的阻礙,因為(幾乎)所有 Python 程式碼都只能在持有 GIL 時" -"執行,導致多執行緒的 Python 程式實際上只會使用一顆 CPU。" +":term:`全域直譯器鎖 `\\ (GIL)常被視為在高階多處理" +"器伺服器上部署 Python 的阻礙,因為(幾乎)所有 Python 程式碼都只能在持有 GIL " +"時執行,導致多執行緒的 Python 程式實際上只會使用一顆 CPU。" #: ../../faq/library.rst:413 msgid "" @@ -736,7 +736,7 @@ msgid "" "compensate for the removal of the GIL. The Python 3.9 fork is the first " "attempt at removing the GIL with an acceptable performance impact." msgstr "" -"目前移除 GIL 的工作是以 Sam Gross 的 `移除了 GIL 的 Python 3.9 分支 " +"目前移除 GIL 的工作是以 Sam Gross 的\\ `移除了 GIL 的 Python 3.9 分支 " "`_\\ 為基礎。在此之前,早在 Python 1.5 時" "代,Greg Stein 就曾實作一套完整的修補程式(即「自由執行緒」修補程式),移除 " "GIL 並改以細粒度鎖定取代。Adam Olsen 也在他的 `python-safethread " @@ -862,9 +862,9 @@ msgid "" "preserve most (though not all) of it." msgstr "" ":mod:`shutil` 模組包含 :func:`~shutil.copyfile` 函式。請注意,它不會複製 " -"Windows NTFS 磁碟區上的 `替代資料串流 " +"Windows NTFS 磁碟區上的\\ `替代資料串流 " "`_,也不會複" -"製 macOS HFS+ 磁碟區上的 `資源分支 " +"製 macOS HFS+ 磁碟區上的\\ `資源分支 " "`__,不過這兩者現在都很少使用。" "它也不會複製檔案權限和詮釋資料,但改用 :func:`shutil.copy2` 可以保留其中大部" "分(但不是全部)。" @@ -1166,7 +1166,7 @@ msgid "" msgstr "" "為了避免 TCP 連線阻塞,你可以將 socket 設為非阻塞模式。這樣當你呼叫 " ":meth:`~socket.socket.connect` 時,要不是立即連線成功(不太可能),就是會得到" -"一個例外,其中的錯誤代碼存放在 ``.errno``。``errno.EINPROGRESS`` 表示連線正在" +"一個例外,其中的錯誤碼存放在 ``.errno``。``errno.EINPROGRESS`` 表示連線正在" "進行中,但尚未完成。不同的作業系統會回傳不同的值,所以你必須檢查你的系統會回" "傳什麼。" diff --git a/library/dataclasses.po b/library/dataclasses.po index d62113f6e5c..3e1cd539eee 100644 --- a/library/dataclasses.po +++ b/library/dataclasses.po @@ -342,8 +342,8 @@ msgid "" "generate an exception. This emulates read-only frozen instances. See the :" "ref:`discussion ` below." msgstr "" -"*frozen*:如果為真(預設為 ``False``),對欄位賦值將會產生例外。這模擬了唯讀" -"的凍結實例。請參閱下方的\\ :ref:`討論 `。" +"*frozen*:如果為真(預設為 ``False``),對欄位賦值會引發例外。這模擬了唯讀的" +"凍結實例。請參閱下方的\\ :ref:`討論 `。" #: ../../library/dataclasses.rst:180 msgid "" @@ -362,8 +362,8 @@ msgid "" "__match_args__` is already defined in the class, then :attr:`!" "__match_args__` will not be generated." msgstr "" -"*match_args*:如果為真(預設為 ``True``),將會以傳給所產生的 " -":meth:`~object.__init__` 方法中、非僅限關鍵字參數的串列,建立 " +"*match_args*:如果為真(預設為 ``True``),會根據所產生的 " +":meth:`~object.__init__` 方法中非僅限關鍵字參數的串列,建立 " ":attr:`~object.__match_args__` tuple(即使沒有產生 :meth:`!__init__` 也一樣," "見上文)。如果為假,或是類別中已經定義了 :attr:`!__match_args__`,則不會產生 " ":attr:`!__match_args__`。" @@ -436,7 +436,7 @@ msgstr "" msgid "" "``field``\\s may optionally specify a default value, using normal Python " "syntax::" -msgstr "``field``\\ 可以選擇性地使用一般的 Python 語法來指定預設值: ::" +msgstr "``field`` 可以選擇性地使用一般的 Python 語法來指定預設值: ::" #: ../../library/dataclasses.rst:239 msgid "" From a2de965df1883c6e2fd6f426cc5db4fbd2c796dd Mon Sep 17 00:00:00 2001 From: josix Date: Sun, 25 Oct 2026 00:29:12 +0800 Subject: [PATCH 4/6] Apply past review conventions to dataclasses/faq-library translations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recheck the re-translated entries against the corrections reviewers most often ask for: - drop \ before 。 after a literal (no separator next to full-width punctuation) - 串列 only for a Python list; generic lists of fields/parameters/objects become 清單 - keep "token" in English Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01H2AUU3pZYEkfs9BY3mm7bn --- faq/library.po | 6 +++--- library/dataclasses.po | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/faq/library.po b/faq/library.po index 5b2c7470ffc..6fe1415a930 100644 --- a/faq/library.po +++ b/faq/library.po @@ -491,8 +491,8 @@ msgid "" msgstr "" "與其嘗試為 :func:`time.sleep` 猜一個合適的延遲值,不如使用某種號誌(" "semaphore)機制。一個做法是使用 :mod:`queue` 模組建立一個佇列物件,讓每個執行" -"緒在完成時向佇列加入一個記號,然後讓主執行緒從佇列中讀取與執行緒數量相同的記" -"號。" +"緒在完成時向佇列加入一個 token,然後讓主執行緒從佇列中讀取與執行緒數量相同的 " +"token。" #: ../../faq/library.rst:290 msgid "How do I parcel out work among a bunch of worker threads?" @@ -516,7 +516,7 @@ msgid "" "necessary to ensure that each job is handed out exactly once." msgstr "" "或者,如果你想精細控制分派演算法,也可以自己手動撰寫邏輯。使用 :mod:`queue` " -"模組建立一個存放工作清單的佇列。:class:`~queue.Queue` 類別維護一個物件串列," +"模組建立一個存放工作清單的佇列。:class:`~queue.Queue` 類別維護一個物件清單," "並提供將項目加入佇列的 ``.put(obj)`` 方法,以及取出項目的 ``.get()`` 方法。這" "個類別會處理必要的鎖定,確保每項工作都恰好只被分派一次。" diff --git a/library/dataclasses.po b/library/dataclasses.po index 3e1cd539eee..dffbab99976 100644 --- a/library/dataclasses.po +++ b/library/dataclasses.po @@ -102,7 +102,7 @@ msgid "" "`. With two exceptions described below, nothing in " "``@dataclass`` examines the type specified in the variable annotation." msgstr "" -"``@dataclass`` 裝飾器會檢查類別以找出 ``field``\\ 。``field`` 的定義是具有\\ " +"``@dataclass`` 裝飾器會檢查類別以找出 ``field``。``field`` 的定義是具有\\ " ":term:`型別註釋 `\\ 的類別變數。除了下面描述的兩個例外," "``@dataclass`` 完全不會檢查變數註釋中指定的型別。" @@ -363,7 +363,7 @@ msgid "" "__match_args__` will not be generated." msgstr "" "*match_args*:如果為真(預設為 ``True``),會根據所產生的 " -":meth:`~object.__init__` 方法中非僅限關鍵字參數的串列,建立 " +":meth:`~object.__init__` 方法中非僅限關鍵字參數的清單,建立 " ":attr:`~object.__match_args__` tuple(即使沒有產生 :meth:`!__init__` 也一樣," "見上文)。如果為假,或是類別中已經定義了 :attr:`!__match_args__`,則不會產生 " ":attr:`!__match_args__`。" @@ -1293,7 +1293,7 @@ msgid "" "The final type of :attr:`!x` is :class:`int`, as specified in class :class:`!" "C`." msgstr "" -"最終的欄位串列依序為 :attr:`!x`、:attr:`!y`、:attr:`!z`。:attr:`!x` 的最終型" +"最終的欄位清單依序為 :attr:`!x`、:attr:`!y`、:attr:`!z`。:attr:`!x` 的最終型" "別是 :class:`int`,如類別 :class:`!C` 中所指定。" #: ../../library/dataclasses.rst:702 @@ -1374,7 +1374,7 @@ msgid "" "list of fields: parameters derived from regular fields are followed by " "parameters derived from keyword-only fields." msgstr "" -"請注意,參數已經從它們在欄位串列中出現的順序重新排序:由一般欄位衍生的參數排" +"請注意,參數已經從它們在欄位清單中出現的順序重新排序:由一般欄位衍生的參數排" "在前面,接著才是由僅限關鍵字欄位衍生的參數。" #: ../../library/dataclasses.rst:738 @@ -1382,7 +1382,7 @@ msgid "" "The relative ordering of keyword-only parameters is maintained in the re-" "ordered :meth:`!__init__` parameter list." msgstr "" -"在重新排序後的 :meth:`!__init__` 參數串列中,僅限關鍵字參數之間的相對順序會維" +"在重新排序後的 :meth:`!__init__` 參數清單中,僅限關鍵字參數之間的相對順序會維" "持不變。" #: ../../library/dataclasses.rst:743 From 12ba1f9d53549a7e4834796626491b0c4cc5af1f Mon Sep 17 00:00:00 2001 From: josix Date: Sun, 25 Oct 2026 00:29:19 +0800 Subject: [PATCH 5/6] Add rendered-HTML review and past review corrections to translation skills - .scripts/render_review.py (stdlib only): builds the given PO files' pages like `make build`, dumps the rendered prose to .render-review/, and flags rendered-only problems (leftover roles, CJK-Latin spacing, stray spaces around full-width punctuation, zh_CN wording, English paragraphs) - new review-rendered-html skill: build, read the text dump as a reader, and inspect Playwright screenshots of the changed sections; includes a table of common rendered-only problems and fixes - translate-po/references/review-corrections.md: what reviewers most often correct, mined from ~1,500 inline review comments in 150 merged PRs (#763-#1246), with examples and a table of reviewer-enforced terms - translate-po gains a past-review checklist step and a rendered-page step; validate-translation and check-terminology reference both; CLAUDE.md points to the new script; .gitignore covers the review output dirs Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01H2AUU3pZYEkfs9BY3mm7bn --- .claude/skills/check-terminology/SKILL.md | 1 + .claude/skills/review-rendered-html/SKILL.md | 94 ++++ .claude/skills/translate-po/SKILL.md | 39 ++ .../references/review-corrections.md | 433 ++++++++++++++++++ .claude/skills/validate-translation/SKILL.md | 18 +- .gitignore | 3 + .scripts/render_review.py | 185 ++++++++ CLAUDE.md | 1 + 8 files changed, 773 insertions(+), 1 deletion(-) create mode 100644 .claude/skills/review-rendered-html/SKILL.md create mode 100644 .claude/skills/translate-po/references/review-corrections.md create mode 100644 .scripts/render_review.py diff --git a/.claude/skills/check-terminology/SKILL.md b/.claude/skills/check-terminology/SKILL.md index 6a1639248bd..0f53879ed90 100644 --- a/.claude/skills/check-terminology/SKILL.md +++ b/.claude/skills/check-terminology/SKILL.md @@ -14,6 +14,7 @@ Check terminology against project glossary and zh_CN/zh_TW mappings. 2. **Secondary:** `focused_terminology_dictionary.csv` - 118 key terms 3. **Tertiary:** `.scripts/google_translate/utils.py` - zh_CN to zh_TW mappings 4. **Usage evidence (not authority):** `.scripts/translation_memory.sh query ""` - how existing translated .po entries render the term (skip if the command fails) +5. **Reviewer-enforced terms:** the "Terminology enforced by reviewers" table in `../translate-po/references/review-corrections.md` - pairs reviewers repeatedly corrected (e.g. 傳回→回傳, 您→你, 協議→協定, 運行→執行), some context-dependent (串列 vs 清單, 檔案 vs 文件) ## Check Process diff --git a/.claude/skills/review-rendered-html/SKILL.md b/.claude/skills/review-rendered-html/SKILL.md new file mode 100644 index 00000000000..c24fd844080 --- /dev/null +++ b/.claude/skills/review-rendered-html/SKILL.md @@ -0,0 +1,94 @@ +--- +name: review-rendered-html +description: Builds translated PO files into HTML and reviews the rendered page for reading flow and formatting, by reading the extracted page text and checking Playwright screenshots. Use after translating or editing a PO file, before opening a PR. +argument-hint: [ ...] +--- + +# review-rendered-html + +PO-level checks (`msgfmt`, `make lint`, terminology) cannot show how a +translation reads on the published page. reST roles disappear, links and +literals change spacing, and Sphinx adds its own labels. Review the rendered +HTML of every page you changed before opening a PR. + +## Prerequisites + +- CPython clone at `../cpython` on the branch matching `VERSION` in the + Makefile, and the docs venv (`make prepare_deps` sets up both) +- Playwright MCP tools (`browser_navigate`, `browser_take_screenshot`) for + screenshots; without them, read the text dump only and say so in the report + +## Step 1: Build and dump the rendered text + +```bash +python3 .scripts/render_review.py library/dataclasses.po [faq/library.po ...] +``` + +- Builds only the given pages (same Sphinx options as `make build`) and writes + the page prose, without code blocks, to `.render-review/.txt` +- Prints heuristic findings with the line in the text dump; `--no-build` + re-checks without rebuilding +- "build reported problems" about `redirects to builtins/...` comes from a + reused build directory and does not affect the page + +## Step 2: Read the text dump as a reader + +Read `.render-review/.txt` end to end, focusing on paragraphs from the +entries you changed. For each one check: + +- **Flow:** the sentence reads as natural Taiwanese technical writing; no + translationese (被…所…, 將會是, 除…之外的其他事項), no dangling clauses + after a role's text disappears +- **Spacing:** one space between CJK and Latin/digits/literals; no space + next to full-width punctuation; no stray space inside Chinese text +- **Punctuation:** full-width `,。:;()「」` around Chinese; the colon before + a code block stays `:` +- **Completeness:** compare with the English page when meaning is unclear + +Confirm or dismiss every heuristic finding by reading its context. Findings in +entries you did not touch are follow-ups, not part of the current change. + +Paragraphs still in English are usually not your bug. Either the entry is +untranslated, or the PO msgid is older than the CPython clone, because the +source changed after the last sync. Mention them in the report and don't fix +them in the PO by hand. + +## Step 3: Screenshot and inspect the layout + +Serve the build and open the sections you changed: + +```bash +python3 -m http.server 8765 --bind 127.0.0.1 --directory ../cpython/Doc/build/html +``` + +Then use the Playwright MCP tools: + +1. `browser_resize` to 1280x1600 +2. `browser_navigate` to `http://127.0.0.1:8765/.html#` +3. `browser_take_screenshot` with `filename: ".playwright-mcp/.png"` + (the MCP only writes inside the repo; `.playwright-mcp/` is gitignored) +4. Read the PNG and check: links and `:term:` text sit flush with the Chinese + around them, inline code has normal spacing, parameter lists, notes and + admonitions keep their structure, and headings and the sidebar TOC show + the translated titles + +Stop the server when done. + +## Common rendered-only problems and fixes + +| Rendered | Cause in msgstr | Fix | +|---|---|---| +| `field可以` | ``` ``field``\ 可以 ``` | ``` ``field`` 可以 ``` (keep the space after a literal before CJK) | +| `全域直譯器鎖(GIL) 來` | `` :term:`…`\ (GIL) 來 `` | `` :term:`…`\ (GIL)來 `` (full-width parens, no extra space) | +| `磁碟區上的 替代資料串流` | `` 的 `替代資料串流 `_ `` | `` 的\ `替代資料串流 `_ `` | +| `:meth:` or backticks visible | role typo such as `:meth: ` with a space | restore the exact role syntax | +| `` `\ `` visible | escaped backslash doubled in the PO | one `\\ ` in the PO file (`\ ` in the value) | + +A reST role or link must be followed by whitespace or ASCII/closing +punctuation, so a full-width opening bracket after a role still needs `\ `. + +## Report + +List per page: findings fixed, findings dismissed (with reason), follow-ups +in untouched entries, English paragraphs and their likely cause, and the +screenshots you checked. diff --git a/.claude/skills/translate-po/SKILL.md b/.claude/skills/translate-po/SKILL.md index d25b1866354..92ed4e7bb61 100644 --- a/.claude/skills/translate-po/SKILL.md +++ b/.claude/skills/translate-po/SKILL.md @@ -76,6 +76,41 @@ See `references/rst-cheatsheet.md` for complete rules. 4. If uncertain, add `#, fuzzy` flag 5. Never use zh_CN terms from `references/forbidden-terms.md` +### Step 5: Check Against Past Review Corrections + +Reviewers of this repo keep asking for the same fixes. Before finishing, +re-read every changed msgstr against `references/review-corrections.md` +(mined from ~1,500 review comments). The most frequent ones: + +1. **Phrasing and meaning:** no English word order, piles of 的 or needless + 被; the meaning must match the source exactly (most common correction) +2. **Terms reviewers enforce:** argument→引數 / parameter→參數, 回傳 (not + 傳回/返回), 你 (not 您), 引發 (raise), 設定例外 (C API "set an + exception"), 透過, 協定, 命令列, 用戶端, 執行 (not 運行), 產生 (not 生成), + 串列 only for a Python list (otherwise 清單); see the table in the reference +3. **Spacing:** no space next to full-width punctuation, after 。, or + between CJK characters +4. **`\\ `:** use it between CJK and markup, but not next to `,。、:;)「」` + (a full-width `(` right after markup still needs it) +5. **Punctuation:** `、` for enumerations; keep `...` as in the source; keep + ASCII quotes around literal values +6. **Markup:** re-add ``literal`` markup Poedit drops; balanced backticks +7. **Completeness:** no omissions (qualifiers, parentheticals, second + occurrences) and no additions the source does not state (超譯) +8. **Keep English:** socket, token, runtime, yield, library names, and words + that are really parameter names; translate comments in code samples +9. **Consistency:** one rendering per term within a page; search existing + translations (translation memory) before coining a term +10. **PO mechanics:** never just strip `#, fuzzy` without updating the + translation; index-only entries as `English(譯文)` + +### Step 6: Review the Rendered Page + +After the PO file is saved, build and read the rendered page with the +`review-rendered-html` skill (`python3 .scripts/render_review.py .po`, +then Playwright screenshots of the changed sections). Spacing around +literals, roles and links can only be judged on the rendered page. + ## Output Format ``` @@ -95,6 +130,8 @@ Before outputting, verify: - [ ] Terminology matches glossary - [ ] No forbidden zh_CN terms - [ ] Consistent with translation memory hits, unless they conflict with the glossary +- [ ] Checked against past review corrections (`references/review-corrections.md`) +- [ ] Rendered page reads naturally and its layout is correct (`review-rendered-html`) ## Examples @@ -154,4 +191,6 @@ msgstr "可能需要審核的翻譯" - `references/terminology.md` - Key term translations (186 terms) - `references/forbidden-terms.md` - zh_CN to zh_TW mappings - `references/rst-cheatsheet.md` - reST syntax rules +- `references/review-corrections.md` - What reviewers most often correct, with examples and enforced terms - `.scripts/translation_memory.sh` - Translation memory lookup over this repo's .po files +- `.scripts/render_review.py` - Build pages and check the rendered text (see `review-rendered-html`) diff --git a/.claude/skills/translate-po/references/review-corrections.md b/.claude/skills/translate-po/references/review-corrections.md new file mode 100644 index 00000000000..11debd2e46c --- /dev/null +++ b/.claude/skills/translate-po/references/review-corrections.md @@ -0,0 +1,433 @@ +# Common Review Corrections + +What reviewers of this repo most often ask translators to fix, mined from +merged PRs. Use it as a pre-PR checklist: the top sections are the most +frequent corrections. Counts are relative frequency, not exact figures. +Each rule says whether README.rst / the other references already cover it. + +Open question: #860 asked for 打印→列印, while `forbidden-terms.md` maps +打印→印出. Follow `forbidden-terms.md` until maintainers decide. + +## Sample + +- **Source:** the 150 most recent merged PRs that had reviewer inline comments. Bot, dependabot, `app/*` and "sync"/"bump" PRs were skipped, and so were the PR author's own replies. +- **PR range:** #763 – #1246, merged 2023-12-20 → 2026-10-05. +- **Comments analysed:** 1,552 inline review comments. 1,074 of them are ```` ```suggestion ```` blocks, which were diffed against the original line in `diff_hunk`. Also analysed: 83 non-trivial review summaries. +- **Method:** each suggestion was diffed at character level and auto-tagged (spacing, `\\ `, punctuation, roles, term swaps, CJK→Latin). The free-text notes were keyword-classified, then spot-read by hand. One comment can fall into several categories. Counts are therefore approximate: read them as *relative* frequency, not exact figures. +- **Coverage:** "Coverage" says whether a rule is already in `README.rst` (翻譯守則, lines ~240-377) or `.claude/skills/*/references/*.md`. Values are **NEW**, **ALREADY-COVERED** or **PARTIAL** (the base rule is covered; the specific sub-rule is new). + +--- + +## 1. Awkward, literal or inaccurate phrasing (翻譯腔 / meaning) + +**Rule:** the msgstr must read like natural Chinese and say exactly what the source says. Reviewers rewrite English sentence order, stacked 的, needless 被 passives, and wrong subject/object. + +- **Count:** ~508 suggestion rewrites in 109 PRs (CJK changes beyond a term swap). Of these, 100 notes say "meaning wrong / 原文想表達…" (55 PRs) and 68 say "不通順 / 不流暢 / 像英文句型" (43 PRs). +- **Coverage:** NEW. The skills have no guidance on phrasing or on checking meaning. + +**Typical fixes:** +- Wrong object of the verb: + - Before: `將 *iterable* 中的內容擴展至 *list*` + - After: `以 *iterable* 中的內容擴展 *list*` + - Reviewer note: 「擴展」的受詞應該是 list. (#1178) +- English clause order rewritten: + - Before: `在 Unicode Technical Standard #18 中的嵌套集合與集合操作可能會在未來添加支援` + - After: `未來可能會支援 Unicode Technical Standard #18 中的嵌套集合與集合操作` (#1147) +- Literal "just": + - Before: `而不是僅被引入時執行` + - After: `而不會在被引入時就執行` (#1126) +- Other recurring notes: + - "nit: too many 的?" (#1190) + - 「的」好像有點過多 (#1010) + - 修改後最後一句還是比較像是英文句型的結構 (#876) + - 原文是 *is now* 而非 *is not* (#1029) + - 「一般也比較常聽到『不支援 64 位元作業系統』而不是『…不被支援』」, i.e. prefer the active voice (#828) + +## 2. Terminology: wrong term or a zh_CN term + +**Rule:** use the project term list (wiki 術語列表, `glossary.po`), not zh_CN vocabulary or ad-hoc choices. + +- **Count:** ~348 comments in 93 PRs. A full breakdown is in the table at the end. +- **Coverage:** + - PARTIAL. Most zh_CN swaps are in `forbidden-terms.md` and `terminology.md`. + - Several frequently enforced pairs are missing (marked NEW in the table): 傳回, 拋出, 通過, 協議, 命令行, 客戶端, 清單 vs 串列, 檔案 vs 文件, and more. + +**Examples:** +- 參數 → 引數 for *argument*: 28 comments in 16 PRs, the single most frequent fix. + - Before: `參數 *start*,*stop* 和 *step* 會被計算多次` + - After: `引數 *start*、*stop* 和 *step*…` (#1149) + - The reverse also happens: *parameter* must be 參數 (#1095, #1048). +- C API wording. "raise" is 引發, but "sets an exception" is 設定例外, not 拋出異常. + - Before: `並拋出 :exc:\`IndexError\` 異常` + - After: `並設定 :exc:\`IndexError\` 例外` (#1164) +- Python list vs a generic list: + - "當原文指稱的是 Python list 時我們習慣翻譯成「串列」,但如果不是 Python list 那大多時候就會翻譯成「清單」" (#1076; also #1052 and #1115) + +## 3. CJK–Latin spacing and stray spaces + +**Rule:** +- One half-width space between CJK and Latin letters, digits or inline markup. +- **No** space between a full-width punctuation mark and its neighbour. +- No space between two CJK characters, and no space after `。`. + +- **Count:** ~243 comments in 78 PRs. + - Missing CJK–Latin space: 132 comments in 63 PRs. + - Space beside full-width punctuation removed: 97 comments in 35 PRs. +- **Coverage:** + - The base CJK–Latin rule is ALREADY-COVERED (README; validation-rules). + - "No space next to full-width punctuation, even when the neighbour is markup or English" is PARTIAL (README only says 符號英文間不用). + - "No space after 。 / between CJK chars" is NEW. + +**Examples:** +- `你可以在Python 3.14+` → `你可以在 Python 3.14+` (#1242) +- `Class method會基於數字` → `Class method 會基於數字` (#1229) +- ```__annotations__`` 。` → ```__annotations__``。` (#1242) +- Notes: + - "句號後面不用空格" (#1095) + - "中文字間不留空白" (#1057, #1054) + - "英文與中式標點符號間不用留空格" (#828) + +## 4. `\\ ` zero-width separator misuse + +**Rule:** +- Put `\\ ` between a CJK character and a reST role, `*emphasis*` or link, where the markup needs a boundary. +- Do **not** put it next to full-width `,。、:;)「」`. A `(` that directly follows markup is the risky case: it can break the build. +- Plain spaces never need escaping. + +- **Count:** 115 comments in 48 PRs. + - Separators added: 62 comments in 33 PRs. + - Separators removed: 53 comments in 20 PRs; 15 of those were next to full-width punctuation. +- **Coverage:** + - Adding `\\ ` before roles is ALREADY-COVERED (README; rst-cheatsheet). + - "Remove `\\ ` next to full-width punctuation", "both `\\ ` and `\\` work, prefer `\\ `" and "spaces need no escape" are NEW. + +**Examples:** +- Added: + - Before: `請見\ :ref:\`安裝 IDLE <…>\`` + - After: `請見\\ :ref:\`安裝 IDLE <…>\`` (#1203: single backslash is wrong in PO) + - Before: `包含一個 :term:\`路徑條目 \` 的串列` + - After: `包含一個\\ :term:\`路徑條目 \`\\ 的串列` (#1190) +- Removed: + - Before: `規格(\\ ``__spec__``\\ )` + - After: `規格(``__spec__``)` (#1190) + - Review #941: 在(除了括號以外的)全形標點符號旁不用放 `\\ ` + - Review #808: 把 `\\ 、\\ ` 都改成 `、` +- Notes: + - "實際上 `\\ ` 與 `\\` 效果相同…目前專案大多用前者" (#1175) + - "空白不用 escape" (#820) + +## 5. Punctuation + +**Rule:** +- Chinese sentences use full-width `,。:;()`. +- Use `、` to separate enumerated items. +- A trailing `::` becomes `: ::`. +- Keep `...` exactly as in the source (do not convert it to `…`). +- Use 「」 for emphasis quotes. Keep straight `"..."` when the quoted text is a literal value or name. + +- **Count:** ~119 comments in 47 PRs. + - Half-width `()` changed to full-width: 31 comments in 11 PRs. + - `: ::` fixes: 16 comments in 11 PRs. + - 「」 added: 8. 「」 reverted to `"…"`: 7. + - `,` changed to `、`: 5. Ellipsis: 2. +- **Coverage:** + - Full/half width, parentheses and `: ::` are ALREADY-COVERED. + - `、` for lists, `...` kept as in the source, and "literal values keep ASCII quotes" are NEW. + +**Examples:** +- `中 ::` → `中: ::` (#1149) +- `錯誤時回傳 ``-1``,並無異常設定 (除非…)` → `…並無例外設定(除非…)` (#1149) +- `十六進位,八進位,還有二進位` → `十六進位、八進位,還有二進位` (#1205). Other `、` comments: 宜改頓號 / 改頓號? (#945) +- `套用「default」動作` → `套用 \"default\" 動作` (#1115). Note: double quotes that wrap untranslated literals stay ASCII (#1108). +- `時鐘每秒只\\ “跳” 50 次` → `只「跳」50 次` (#908) +- `…` → `...` (#1175: "和原文使用相同符號") + +## 6. reST roles and inline markup + +**Rule:** +- Keep every role, ``literal`` and link intact. +- Translate the *display text* of `:ref:`, `:term:`, `:source:` and `:option:` as `譯文 `. +- Re-add ``…`` markup when the source has it; Poedit does not carry it over. +- Never break the backticks. + +- **Count:** ~107 comments in 49 PRs. + - ``literal`` markup added back: 31 comments in 18 PRs. + - Role display text translated: 13 comments in 8 PRs. +- **Coverage:** + - Role preservation and display-text translation are ALREADY-COVERED (rst-cheatsheet). + - "Re-add ``literal`` markup that is missing in msgstr" and "check that backticks are balanced" are NEW. + +**Examples:** +- `:ref:\`keyword \`` → `:ref:\`關鍵字 \`` (#1175) +- `:ref:\`non-blocking \`` → `\\ :ref:\`非阻塞的 \`\\ ` (#953) +- `傳遞旗標 strict=False` → `傳遞旗標 ``strict=False``` (#1169: 「程式碼」標記 poedit 不會自動帶上) +- `` `-PY_SSIZE_T_MAX`` `` (unbalanced) → ```` ``-PY_SSIZE_T_MAX`` ```` (#1149) + +## 7. Consistency within a file + +**Rule:** within one page, the same English term gets the same rendering. Search the existing translations (pogrep / GitHub search) before you coin a new term. Different pages may differ. + +- **Count:** 99 comments in 56 PRs. +- **Coverage:** PARTIAL. validation-rules has an "Inconsistent term" check, but nothing says to search the existing translations or that the scope is one page. + +**Examples:** +- "這行出現兩個 parse 但翻譯不同" (#921) +- "timeout 前面有翻成「超時」,要選擇其中一個統一用詞" (#953) +- "lookahead / lookbehind 已經在 library/re 中被翻譯成「預查」與「回查」,希望能統一用詞" (#1175) +- Reviewers once used 時間戳 and 時間戳記 in the same file; they asked for one. They also asked for 產生 over 生成, to match the existing text (#1213). +- "單篇文章內盡量統一,不同文章不一樣的話我覺得沒關係" (#921) + +## 8. Annotate rare or ambiguous terms with the original English, first occurrence only + +**Rule:** if a term has no settled zh_TW translation, could be confused with another term, or would be hard to map back to English, add `譯文 (English)` at its first occurrence on the page only. + +- **Count:** 59 comments in 37 PRs. +- **Coverage:** ALREADY-COVERED (README rule 6). Reviewers still enforce it very often, so it is worth a checklist line. + +**Examples:** +- `用於降低DoS攻擊的風險` → `用於降低阻斷服務 (DoS) 攻擊的風險` (#1205) +- `就地初始化` → `原地 (in place) 初始化` (#1164) +- `點號參照` → `點號參照 (dotted reference)` (#1126) +- Terms reviewers asked to annotate: pickle, peer, transaction, hook, introspection, magic number, segmentation fault, real-time, finder/loader/importer. +- "原文附註只要在第一次出現的地方就好" (#777, #797; also #828) + +## 9. Untranslated English left in msgstr (should translate) + +**Rule:** translate ordinary English words and phrases, including role display text and comments inside code samples. Leave identifiers and string literals in code unchanged. + +- **Count:** 54 comments in 35 PRs. +- **Coverage:** NEW. The skills list what to *keep*, but not that leftover English is a defect. + +**Examples:** +- `以 :ref:\`debug mode \`` → `以\\ :ref:\`偵錯模式 \`\\ ` (#1178) +- `Callable` → `可呼叫` (#905) +- `Application Note` → `應用簡記` (#1079) +- Notes: + - "displayed text 需翻譯" (#1190) + - "docstring 應翻譯" (#1050) + - "目前慣例是 code comment 也需要翻譯" (#1246) +- Counterpoint: An automated review flagged a translated string literal inside a `property()` example (#1222). Translate comments; leave literals in code alone. + +## 10. Omissions (missing words, clauses or segments) + +**Rule:** every clause, qualifier ("optional", "numeric", "original", "usually") and parenthetical in the source must appear in the msgstr. + +- **Count:** 44 comments in 26 PRs. +- **Coverage:** NEW. + +**Examples:** +- "(which identifies the rule that's being defined) 這句話好像沒翻譯到", fixed by adding `(用來識別被定義的規則)` (#1175) +- "漏翻第二個 community" (#1203) +- "沒翻到 optional", "漏翻 operands" (#876) +- "原文 'decode to text' 沒有翻譯到" (#1029) +- "`PythonFinalizationError` 這行目前原文有、譯文沒有" (#1010) + +## 11. Do not translate (keep English) + +**Rule:** keep in English: +- `socket`, `runtime`, `yield`, `token` +- library or module names such as itertools +- literal option values +- words that are really parameter names (`*input_charset*`, `using`, `buffer`, `bytes`) + +`pickle` and similar words may be translated only if the original is added. + +- **Count:** 38 comments in 31 PRs. +- **Coverage:** + - PARTIAL. keep-in-english.md covers runtime, pickle, yield, keywords and modules. + - socket, token, "library names", and "words that refer to a parameter" are NEW. + +**Examples:** +- `執行時` → ` runtime` (#1190). "runtime 我們通常就不翻譯" (#1057) +- "socket 應該直接原文就好" (#857, #938) +- "我自己是希望 library name 都不要翻譯" (#950) +- `字元集合` → `*input_charset*` (#989) +- `緩衝` → `buffer` "指 parameter" (#953) +- "yield 和 generate 容易搞混,所以我們 yield 通常不翻或會附註原文" (#876, #916) + +## 12. Over-translation (additions not in the source) + +**Rule:** do not add explanations, qualifiers or facts that the source does not state (超譯). + +- **Count:** 23 comments in 17 PRs. +- **Coverage:** NEW. + +**Examples:** +- "「對於整數型別的操作數」原文好像沒有這句" (#876) +- "原文沒有提到「預設參數」" (#1029) +- "它是 generator 沒錯,但會不會補充過多資訊呢?…希望忠實表達原意" (#945) +- "翻譯成「特性」感覺超譯了" → 功能 (#1175) + +## 13. Second person: 你, not 您 + +**Rule:** always address the reader with 你. + +- **Count:** 19 comments in 11 PRs. +- **Coverage:** NEW. + +**Examples:** +- `若您使用 Linux` → `若你是使用 Linux` (#1203) +- "我們 convention 是統一用「你」不用「您」" (#941) +- "請都把「您」改成「你」" (#770) + +## 14. PO and PR mechanics + +**Rules:** +- Never edit msgid or its line breaks. +- Run `powrap` after editing. +- Don't just delete `#, fuzzy`: update the translation to match the changed msgid. +- No trailing whitespace and no stray `+` from diffs. +- Don't commit unrelated files. +- Index-only entries start with the English term and put the translation in parentheses. +- Delete commented-out obsolete translations. + +- **Count:** 20 comments in 12 PRs, plus several review summaries. +- **Coverage:** + - msgid and powrap are ALREADY-COVERED. + - fuzzy handling, the index-entry format, trailing whitespace and unrelated files are NEW. + +**Examples:** +- "這個還有差異不能直接刪 fuzzy 標記,要把原文 copy 過去" (#1010) +- "We normally do not modify the original strings (including how it places the line break)" (#1066) +- "數個原文的逗號被改到了,要改回來" (#1149) +- "此行有尾隨空白…" and "delete the `+`" (#1213) +- "看起來你不小心 commit 到這個檔案了" (#1178) +- Index entries: "建議翻譯內容是以原文開頭、後面括號附註譯文,以保持該詞彙在索引表列中的位置" (#860) +- "最後面 comment out 的翻譯可以都刪掉" (#906) + +## 15. Code blocks and formulas + +**Rule:** copy code blocks and math or formula syntax verbatim. Translate only comments. Use full-width punctuation inside translated comments. + +- **Count:** 12 comments in 10 PRs. +- **Coverage:** PARTIAL. rst-cheatsheet says "Only translate comments if needed". + +**Examples:** +- "code block 直接將原文複製到譯文部分即可" (#1149) +- "此處公式語法希望還是從原文直接搬過來" (#872) +- `# 去除P的公因數。 (但若m和n互質…)` → `# 去除 P 的公因數。(但若 m 和 n 互質…)` (#1229) +- "註解的標點符號很多沒有改成全形" (#1128) + +--- + +## Terminology enforced by reviewers + +Counts are suggestion edits where the rejected form disappears and the required form appears, plus explicit notes. "PRs" is the number of distinct PRs. "Cov" says whether the pair is in `forbidden-terms.md` / `terminology.md` / `validation-rules.md`. + +| English | Required zh_TW | Rejected | Count (PRs) | Example PR | Cov | +|---|---|---|---|---|---| +| argument | 引數 | 參數 | 28 (16) | #1149, #1066, #860 | COVERED (terminology.md) | +| return | 回傳 | 傳回 | 21 (9) | #1164, #1097 | **NEW** (only 返回 is listed) | +| you | 你 | 您 | 18 (10) + notes | #1203, #941 | **NEW** | +| support | 支援 | 支持 | 14 (9) | #1175, #855 | COVERED | +| return | 回傳 | 返回 | 13 (9) | #1092, #1074 | COVERED | +| function (programming) | 函式 | 函數 (math only) | 13 (8) | #966, #876 | COVERED | +| call | 呼叫 | 調用 | 13 (8) | #953, #889 | COVERED | +| exception | 例外 | 異常 | 12 (7) | #1164, #1066 | COVERED | +| implement | 實作 | 實現 | 11 (5) | #876, #1066 | COVERED | +| list (Python list) | 串列 | 清單 (OK for a non-Python list) | 9 (6) | #1095, #1076 | **NEW** (conditional rule) | +| protocol | 協定 | 協議 | 9 (4) | #868, #855 | **NEW** as a forbidden term | +| parse | 剖析 (resolve = 解析) | 解析 | 8 (7) | #1147, #908, #1068 | COVERED | +| string | 字串 | 字符串 | 8 (3) | #1074 | COVERED | +| via / through | 透過 | 通過 | 7 (6) | #983, #950 | **NEW** | +| create | 建立 | 創建 | 7 (4) | #953, #860 | COVERED | +| client | 用戶端 (user = 使用者) | 客戶端 | 7 (4) | #1164, #1128, #1074 | **NEW** | +| line (command line, line no.) | 列 (命令列) | 行 (命令行) | 7+5 (4+2) | #1062, #1027, #885 | **NEW** | +| file | 檔案 (document = 文件) | 文件 | 7 (3) | #1068, #860 | **NEW** (only document→文件 listed) | +| default | 預設 | 默認 | 7 (3) | #876 | COVERED | +| set an exception (C API) | 設定例外 | 拋出異常 | 6 (1) | #1164 | **NEW** | +| raise | 引發 | 拋出 / 產生 | 5 (4) + notes | #1074, #828 | **NEW** (only in keep-in-english as a keyword) | +| reference | 參照 | 引用 | 4 (3) | #1147, #867 | COVERED (via glossary) | +| iterate | 疊代 | 迭代 | 4 (3) | #1027, #795 | COVERED | +| statement | 陳述式 | 語句 | 4 (2) | #1095, #966 | COVERED | +| object | 物件 | 對象 | 4 (2) | #1066, #953 | COVERED | +| data | 資料 | 數據 | 4 (2) | #1074, #956 | COVERED | +| blocking / non-blocking | 阻塞 / 非阻塞 | 阻擋 | 4 (2) | #953, #777 | **NEW** | +| pseudo-random | 偽隨機數 | 偽亂數 | 4 (1) | #785 | **NEW** | +| parameter | 參數 | 引數 | 3 (3) | #1095, #1048 | COVERED | +| compatible | 相容 | 兼容 | 3 (3) | #1147, #908 | COVERED | +| code | 程式碼 | 代碼 | 3 (3) | #1071, #983 | COVERED | +| obtain / get | 取得 / 抽取 | 獲取 | 3 (2) | #1190, #1066 | **NEW** | +| user | 使用者 | 用戶 | 3 (2) | #1074 | COVERED | +| setting / configure | 設定 / 配置 | 設置 / 呼叫 | 3 (2) | #1066, #1022 | COVERED | +| interface | 介面 | 接口 | 3 (1) | #1066 | **NEW** | +| hard disk | 磁碟 | 硬盤 | 3 (1) | #1066 | **NEW** | +| escape | 跳脫 | 轉換 / 轉義 | 3 (1) | #1097, #945 | COVERED | +| lookahead / lookbehind | 預查 / 回查 | 預看 | 3 (1) | #1175 | **NEW** | +| section (of a page) | 章節 / 段落 | 部分 | 3 (1) | #767, #983 | **NEW** | +| exit | 結束 | 退出 | 3 (1) | #767 | **NEW** | +| cache | 快取 | 緩存 | 3 (1) | #1092 | COVERED | +| pointer | 指標 | 指針 | 3 (1) | #1164 | COVERED | +| run | 執行 | 運行 / 跑 | notes | #767, #1128 | **NEW** | +| module | 模組 | 模塊 | 2 (2) | #1092, #1071 | COVERED | +| interpret | 直譯 | 解釋 | 2 (2) | #1115, #786 | COVERED | +| instance | 實例 | 實體 | 2 (2) | #1095 | COVERED | +| global | 全域 | 全局 | 2 (2) | #1092, #1027 | COVERED | +| expression | 運算式 | 表達式 | 2 (2) | #1164, #889 | COVERED | +| build | 建置 | 建構 / 構建 | 2 (2) | #1176, #1164 | **NEW** | +| base class | 基底類別 | 基礎類別 | 2 (2) | #1097, #817 | COVERED | +| response | 回應 | 響應 | 2 (2) | #1066, #820 | **NEW** | +| variable / optimize / program | 變數 / 最佳化 / 程式 | 變量 / 優化 / 程序 | 2 each | #1092, #979 | COVERED (程序 is **NEW**) | +| message | 訊息 | 信息 | 2 (1) | #1066, #824 | **NEW** (forbidden-terms maps 信息→資訊 only) | +| boolean | 布林 | 布爾 | 2 (1) | #1066 | COVERED | +| delegate | 委派 | 委託 | 2 (1) | #800 | **NEW** | +| audit event | 稽核事件 | 審計事件 | 2 (1) | #979 | **NEW** | +| entry | 條目 / 項目 (keep consistent) | mixed | 2 (1) + notes | #1074, #836 | **NEW** | +| constructor | 建構函式 | 建構器 | 2 (1) + note | #1066 | **NEW** | +| invoke | 叫用 | 調用 | 1 + notes | #1190, #1066 | COVERED | +| debug | 偵錯 | 除錯 / 調試 | 1 + notes | #1203, #1059 | COVERED (both allowed in skill; reviewers lean 偵錯) | +| signal | 訊號 | 信號 | 1 + note | #921, #860 | **NEW** | +| print | 列印 | 打印 | note | #860 | **CONFLICT**: forbidden-terms says 印出 | +| address | 位址 | 地址 | 1 | #1213 | **NEW** | +| port | 連接埠 | 端口 | 1 | #1066 | **NEW** | +| server | 伺服器 | 服務端 | 1 | #1066 | **NEW** | +| binary | 二進位 | 二進制 | 1 | #1205 | **NEW** | +| custom | 自訂 | 自定 / 自定義 | 1 + note | #1190, #945 | **NEW** | +| byte / character / macro / polymorphism | 位元組 / 字元 / 巨集 / 多型 | 字節 / 字符 / 宏 / 多態 | 1 each | #1076, #876, #1149, #1097 | COVERED (多型 is **NEW**) | +| generate | 產生 | 生成 | 1 + note | #785, #1213 | **NEW** | +| timestamp | 時間戳記 | 時間戳 | 1 + note | #1213 | **NEW** | +| introduce | 引進 / 採納 (not 引入, which is import) | 引入 | 1 + note | #1190 | COVERED | +| flag | 旗標 | 標誌 / 标识 | 2 (2) | #1076, #824 | COVERED | +| process | 行程 | 進程 / 程序 | 1 + notes | #1064, #828 | COVERED | +| package / class | 套件 / 類別 | 包 / 類 | notes | #1022, #876 | COVERED | +| keyword-only | 僅限關鍵字 | 關鍵字 / 只能用關鍵字 | 1 | #1128 | **NEW** | +| session | 工作階段 | 會話 | note | #1066 | **NEW** | +| stream | 串流 | 流 | note | #1029 | **NEW** | +| handler | 處理函式 | 處理器 | notes | #879, #853 | **NEW** | +| wrap | 包裝 | 封裝 (encapsulation) | 1 | #889 | **NEW** | +| context | 情境 | 內容 / 上下文 | notes | #770, #876 | COVERED | +| metadata | 詮釋資料 | 元數據 / 源資訊 | notes | #1190, #1164 | COVERED | +| concurrent | 並行 | 並發 | 1 | #1027 | **NEW** | +| network | 網路 | 網絡 | 1 | #777 | **NEW** | + +The terms below are under active discussion; no fixed rendering is enforced yet. Annotate the original instead of forcing a term: token, pattern (模式/樣式), wildcard, frame, property (特性?), serialize, peer (對等/對等端點), cipher, partial function, debug (偵錯 vs 除錯), invoke. + +--- + +## Already covered vs NEW — quick list + +| # | Rule | Status | +|---|---|---| +| 1 | Natural phrasing: no English word order, too many 的 or needless 被; check meaning | NEW | +| 2 | Term list / zh_CN replacement (base set) | ALREADY-COVERED | +| 2a | Missing pairs: 傳回→回傳, 拋出→引發 / 設定例外, 通過→透過, 協議→協定, 命令行→命令列, 客戶端→用戶端, 檔案 (file) vs 文件 (document), 串列 vs 清單 rule, 訊息, 獲取→取得, 介面, 磁碟, 位址, 連接埠, 伺服器, 回應, 建置, 阻塞, 執行 (not 運行), 稽核, 委派, 產生 (not 生成), 時間戳記 | NEW | +| 2b | 打印 → 列印 (reviewer) vs 印出 (forbidden-terms.md) | CONFLICT, needs resolving | +| 3 | CJK–Latin space | ALREADY-COVERED | +| 3a | No space next to full-width punctuation (also around markup); no space after 。 or between CJK | PARTIAL / NEW | +| 4 | `\\ ` before roles adjacent to CJK | ALREADY-COVERED | +| 4a | Remove `\\ ` next to `,。、:;)「」`; prefer `\\ ` over `\\`; never a single `\`; spaces need no escape | NEW | +| 5 | Full-width punctuation, full-width parentheses for Chinese content, `: ::` | ALREADY-COVERED | +| 5a | `、` for enumerations; keep `...` as in the source; ASCII quotes for literal values | NEW | +| 6 | Preserve roles; translate display text `譯文 ` | ALREADY-COVERED | +| 6a | Re-add missing ``literal`` markup; balanced backticks | NEW | +| 7 | Consistent term within a page; search existing translations first | PARTIAL | +| 8 | Annotate the original at first occurrence only | ALREADY-COVERED | +| 9 | Leftover English is a defect; translate comments in code samples, not string literals | NEW | +| 10 | No omissions (qualifiers, parentheticals, second instances) | NEW | +| 11 | Keep English: runtime, pickle, yield, keywords, modules | ALREADY-COVERED | +| 11a | Keep English: socket, token, library names, words that are parameter names | NEW | +| 12 | No over-translation (超譯) | NEW | +| 13 | 你, never 您 | NEW | +| 14 | Don't touch msgid; powrap | ALREADY-COVERED | +| 14a | Don't just strip fuzzy; no trailing whitespace or stray `+`; no unrelated files; index entries as `English(譯文)`; delete commented-out obsolete entries | NEW | +| 15 | Code and formula copied verbatim; comments get full-width punctuation | PARTIAL | diff --git a/.claude/skills/validate-translation/SKILL.md b/.claude/skills/validate-translation/SKILL.md index ccc8cf604be..992a984de19 100644 --- a/.claude/skills/validate-translation/SKILL.md +++ b/.claude/skills/validate-translation/SKILL.md @@ -38,6 +38,20 @@ Validate translated PO entries against project rules and report issues. - Maximum 79 characters per line - Multi-line format for longer content +### 6. Past Review Corrections +- Check changed entries against + `../translate-po/references/review-corrections.md`: phrasing and meaning, + omissions and additions, reviewer-enforced terms, `\\ ` next to full-width + punctuation, leftover English, 您, and stripped fuzzy flags +- Report meaning errors and omissions as ERROR, the rest as WARNING + +### 7. Rendered Page +- Run the `review-rendered-html` skill on every changed PO file: read the + text dump from `.scripts/render_review.py` and check Playwright screenshots + of the changed sections +- Report rendered-only problems (spacing around literals/links/roles, + translationese that only shows once roles become plain text) as WARNING + ## Review Process 1. **Parse PO file** - Extract msgid/msgstr pairs @@ -55,7 +69,9 @@ Validate translated PO entries against project rules and report issues. ## Quick Validation Commands -After review, run: `make lint`, `make wrap`, `make build .po` +After review, run: `make lint`, `make wrap`, and +`python3 .scripts/render_review.py .po` (builds the page like +`make build .po` and checks the rendered text) ## References diff --git a/.gitignore b/.gitignore index 5397c0884b6..69060c8d412 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ venv.bak/ # agentic-retrieval translation memory index .agentic-retrieval/ +# rendered-page review output (.scripts/render_review.py, Playwright MCP) +.render-review/ +.playwright-mcp/ diff --git a/.scripts/render_review.py b/.scripts/render_review.py new file mode 100644 index 00000000000..0363b0087da --- /dev/null +++ b/.scripts/render_review.py @@ -0,0 +1,185 @@ +"""Build translated pages and check the rendered HTML text. + +Usage (from the repo root):: + + python3 .scripts/render_review.py library/dataclasses.po [faq/library.po ...] + [--cpython ../cpython] [--no-build] [--out DIR] + +For each PO file this script: + +1. hard-links it into the CPython clone's ``Doc/locales`` and builds only the + matching page with Sphinx (same options as ``make build``); +2. extracts the rendered prose of the page (code blocks removed) into + ``/.txt`` so it can be read as a reader would see it; +3. reports heuristic findings with the paragraph they occur in. + +Findings are hints, not errors: read the text file and confirm each one. +Only the standard library is needed; the build needs the docs venv from +``make prepare_deps`` (``~/.venvs/python-docs-i18n``). +""" + +import argparse +import os +import re +import subprocess +import sys +from html.parser import HTMLParser +from pathlib import Path + +REPO = Path(__file__).resolve().parents[1] +VENV = Path.home() / ".venvs" / "python-docs-i18n" +CJK = "㐀-䶿一-鿿" + +# Labels produced by Sphinx's own zh_TW catalog, not by this repo. +SPHINX_LABELS = re.compile(r"在 \d+\.\d+ 版(被加入|的變更|之後被棄用|中被移除)[.:]") + +ZH_CN = ["函數", "返回", "對象", "默認", "字符串", "調用", "信息", "支持", + "創建", "代碼", "迭代", "生成", "設置", "實現", "鏈接", "聲明", + "運行", "數據", "線程", "進程", "內存", "異常", "模塊", "緩存"] + +CHECKS = [ + ("leftover reST role", re.compile( + r":(meth|func|class|attr|term|ref|exc|const|data|mod|deco|pep|" + r"program|keyword|envvar|option|file|samp|source):`?")), + ("raw reST markup", re.compile(r"``|`_|\*\*[^\s*,)][^*]*\*\*")), + ("literal backslash", re.compile(r"\\ ")), + ("no space between CJK and Latin/digit", re.compile( + rf"[{CJK}][A-Za-z0-9_]|[A-Za-z0-9_][{CJK}]")), + ("half-width punctuation after CJK", re.compile(rf"[{CJK}][,;:!?()]")), + ("space before full-width punctuation", re.compile(r" [,。、:;!?)」]")), + ("space after full-width bracket/punctuation", re.compile( + rf"[(「,。、:;)」] [{CJK}]")), + ("space between CJK characters", re.compile(rf"[{CJK}] [{CJK}]")), + ("zh_CN wording", re.compile("|".join(ZH_CN))), +] + + +class BodyText(HTMLParser): + """Collect block-level text inside ``div.body``, skipping code blocks.""" + + BLOCKS = {"p", "li", "dt", "h1", "h2", "h3", "h4", "th", "td", "caption"} + SKIP = {"pre", "script", "style"} + + def __init__(self): + super().__init__(convert_charrefs=True) + self.depth = 0 # >0 while inside div.body + self.skip = 0 # >0 while inside skipped elements + self.stack = [] + self.blocks = [] # (tag, text) + self.cur = None + + def handle_starttag(self, tag, attrs): + a = dict(attrs) + cls = a.get("class", "") or "" + if self.depth == 0: + if tag == "div" and "body" in cls.split(): + self.depth = 1 + self.stack = ["div"] + return + self.stack.append(tag) + if tag in self.SKIP or "headerlink" in cls or "highlight" in cls: + self.skip += 1 + self.stack[-1] = ("skip", tag) + return + if tag in self.BLOCKS and self.skip == 0: + self._flush() + self.cur = [tag, ""] + + def handle_endtag(self, tag): + if self.depth == 0 or not self.stack: + return + top = self.stack.pop() + if isinstance(top, tuple): + self.skip -= 1 + if tag in self.BLOCKS: + self._flush() + if not self.stack: + self._flush() + self.depth = 0 + + def handle_data(self, data): + if self.depth and not self.skip and self.cur is not None: + self.cur[1] += data + + def _flush(self): + if self.cur: + text = " ".join(self.cur[1].split()) + if text and text != "¶": + self.blocks.append((self.cur[0], text.rstrip("¶").strip())) + self.cur = None + + +def build(pos, cpython): + lc = cpython / "Doc" / "locales" / "zh_TW" / "LC_MESSAGES" + for po in pos: + dst = lc / po + dst.parent.mkdir(parents=True, exist_ok=True) + if dst.exists(): + dst.unlink() + os.link(REPO / po, dst) # re-link: editors and sed replace the inode + sources = " ".join(str(Path(p).with_suffix(".rst")) for p in pos) + env = dict(os.environ, PATH=f"{VENV / 'bin'}:{os.environ['PATH']}") + cmd = ["make", "-C", str(cpython / "Doc"), "html", f"SOURCES={sources}", + "SPHINXOPTS=-j auto -D language=zh_TW -D locale_dirs=locales " + "-D gettext_compact=0"] + res = subprocess.run(cmd, env=env, capture_output=True, text=True) + if res.returncode: + # The docs Makefile treats warnings as errors; unrelated redirect + # warnings are common in a reused build dir, so only warn here. + warn = [l for l in res.stdout.splitlines() + res.stderr.splitlines() + if "WARNING" in l or "ERROR" in l] + print("build reported problems (pages may still be fine):", + *warn[:10], sep="\n ", file=sys.stderr) + + +def review(po, cpython, out): + html = cpython / "Doc" / "build" / "html" / Path(po).with_suffix(".html") + if not html.exists(): + print(f"{po}: {html} not found (build failed?)") + return 1 + parser = BodyText() + parser.feed(html.read_text(encoding="utf-8")) + txt = out / Path(po).with_suffix(".txt") + txt.parent.mkdir(parents=True, exist_ok=True) + lines = [] + for n, (tag, text) in enumerate(parser.blocks, 1): + lines.append(("## " if tag.startswith("h") else "- ") + text) + txt.write_text("\n".join(lines) + "\n", encoding="utf-8") + + findings = 0 + english = 0 + print(f"\n=== {po}\n rendered text: {txt}") + for n, line in enumerate(lines, 1): + text = SPHINX_LABELS.sub("", line) + for name, rx in CHECKS: + for m in rx.finditer(text): + s = max(0, m.start() - 15) + print(f" L{n} [{name}] …{text[s:m.end() + 15]}…") + findings += 1 + letters = re.findall(rf"[A-Za-z{CJK}]", text) + cjk = re.findall(rf"[{CJK}]", text) + if len(letters) > 40 and len(cjk) / len(letters) < 0.05: + english += 1 + print(f" {findings} findings; {english} paragraphs still in English " + "(untranslated, or msgid older than the CPython clone)") + return 0 + + +def main(): + ap = argparse.ArgumentParser(description=__doc__.split("\n\n")[0]) + ap.add_argument("po", nargs="+", help="PO files relative to the repo root") + ap.add_argument("--cpython", default=str(REPO.parent / "cpython")) + ap.add_argument("--no-build", action="store_true") + ap.add_argument("--out", default=str(REPO / ".render-review")) + args = ap.parse_args() + cpython = Path(args.cpython).resolve() + if not (cpython / "Doc" / "conf.py").exists(): + sys.exit(f"CPython clone not found at {cpython} (run make prepare_cpython)") + if not args.no_build: + build(args.po, cpython) + out = Path(args.out) + return max(review(po, cpython, out) for po in args.po) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/CLAUDE.md b/CLAUDE.md index 16708b2e793..ce6e46f32b2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -42,3 +42,4 @@ make progress # Check translation progress - **Full guidelines:** `README.rst` (lines 240-368) - **Skills:** `.claude/skills/` for detailed translation workflows - **Translation memory:** `.scripts/translation_memory.sh query ""` - similar translated entries from this repo's .po files (needs `AGENTIC_RETRIEVAL_ENGINE` or `retrieval` on PATH; see README.rst) +- **Rendered review:** `python3 .scripts/render_review.py .po` - build the page and check the rendered text; see the `review-rendered-html` skill for the Playwright screenshot step From 619084725655cc8ab4589a2ebea1469cb369a225 Mon Sep 17 00:00:00 2001 From: josix Date: Sun, 25 Oct 2026 00:39:40 +0800 Subject: [PATCH 6/6] Fix zh_CN wording and markup in existing faq/library.po entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found by the rendered-HTML review of faq/library: - 運行 -> 執行, 聲明 -> 宣告, 設置 -> 設為, 實現 -> 實作, 鏈接 -> 連結, 獲得 -> 取得 - 生成 -> 產生 for random numbers, matching library/random.po (隨機數產生器) - restore the Web Programming wiki link, which used quotes instead of `text `_ and did not render as a link - join the :ref: chapter titles to the surrounding Chinese with \ and 「」; drop a stray space and fix the typo 就可以能 - review-rendered-html: bump a query string when re-screenshotting so the browser does not show a cached page Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01H2AUU3pZYEkfs9BY3mm7bn --- .claude/skills/review-rendered-html/SKILL.md | 3 +- faq/library.po | 48 ++++++++++---------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/.claude/skills/review-rendered-html/SKILL.md b/.claude/skills/review-rendered-html/SKILL.md index c24fd844080..dc63e0c5800 100644 --- a/.claude/skills/review-rendered-html/SKILL.md +++ b/.claude/skills/review-rendered-html/SKILL.md @@ -64,7 +64,8 @@ python3 -m http.server 8765 --bind 127.0.0.1 --directory ../cpython/Doc/build/ht Then use the Playwright MCP tools: 1. `browser_resize` to 1280x1600 -2. `browser_navigate` to `http://127.0.0.1:8765/.html#` +2. `browser_navigate` to `http://127.0.0.1:8765/.html?v=#`; + bump `v` after each rebuild, or the browser shows the cached old page 3. `browser_take_screenshot` with `filename: ".playwright-mcp/.png"` (the MCP only writes inside the repo; `.playwright-mcp/` is gitignored) 4. Read the PNG and check: links and `:term:` text sit flush with the Chinese diff --git a/faq/library.po b/faq/library.po index 6fe1415a930..4193867ed51 100644 --- a/faq/library.po +++ b/faq/library.po @@ -42,8 +42,8 @@ msgid "" "relevant standard library module. (Eventually you'll learn what's in the " "standard library and will be able to skip this step.)" msgstr "" -"查看\\ :ref:`函式庫參照 `\\ 以了解是否有相關的標準函式庫模組。 " -"(當你熟悉標準函式庫中的內容後就可以能跳過這一步。)" +"查看\\ :ref:`函式庫參照 `\\ 以了解是否有相關的標準函式庫模組。" +"(當你熟悉標準函式庫中的內容後,就可以跳過這一步。)" #: ../../faq/library.rst:21 msgid "" @@ -89,7 +89,7 @@ msgstr "用 C 編寫並動態載入的模組(.dll、.pyd、.so、.sl 等); msgid "" "modules written in C and linked with the interpreter; to get a list of " "these, type::" -msgstr "用 C 編寫並與直譯器鏈接的模組;要獲得這些 list,請輸入: ::" +msgstr "用 C 編寫並與直譯器連結的模組;要取得這些模組的清單,請輸入: ::" #: ../../faq/library.rst:42 msgid "" @@ -239,7 +239,9 @@ msgstr "為什麼我的訊號處理程式不起作用?" msgid "" "The most common problem is that the signal handler is declared with the " "wrong argument list. It is called as ::" -msgstr "最常見的問題是訊號處理程式是用錯誤的引數串列聲明的。它以以下形式來呼叫: ::" +msgstr "" +"最常見的問題是訊號處理程式宣告時使用了錯誤的引數清單。它會以如下形式被呼叫: " +"::" #: ../../faq/library.rst:123 msgid "handler(signum, frame)" @@ -247,7 +249,7 @@ msgstr "handler(signum, frame)" #: ../../faq/library.rst:125 msgid "so it should be declared with two parameters::" -msgstr "所以它應該用兩個參數聲明: ::" +msgstr "所以它應該宣告成接受兩個參數: ::" #: ../../faq/library.rst:127 msgid "" @@ -400,7 +402,7 @@ msgstr "" #: ../../faq/library.rst:245 msgid "None of my threads seem to run: why?" -msgstr "我的執行緒似乎都沒有運行:為什麼?" +msgstr "我的執行緒似乎都沒有執行:為什麼?" #: ../../faq/library.rst:247 msgid "" @@ -455,7 +457,7 @@ msgstr "" #: ../../faq/library.rst:269 msgid "A simple fix is to add a tiny sleep to the start of the run function::" -msgstr "一個簡單的修復方式是在運行函式的開頭新增一個小 sleep: ::" +msgstr "一個簡單的修復方式是在執行函式的開頭加入一個短暫的 sleep: ::" #: ../../faq/library.rst:271 msgid "" @@ -565,7 +567,7 @@ msgstr "" #: ../../faq/library.rst:340 msgid "When run, this will produce the following output:" -msgstr "運行時,這將產生以下輸出:" +msgstr "執行時,這將產生以下輸出:" #: ../../faq/library.rst:342 msgid "" @@ -783,11 +785,11 @@ msgid "" "since these must be written with multiple interpreters in mind in order to " "be usable, so many older extension modules will not be usable." msgstr "" -"減少 GIL 影響的另一種方法是將 GIL 設置為直譯器各自狀態的鎖 (per-interpreter-" -"state lock),而不是真正的全域鎖。這在 :ref:`Python 3.12 中首次實現 " -"`,並且可於 C API 中使用。預計 Python 3.13 將會提供其 " -"Python 介面。目前主要的限制可能是第三方擴充模組,因為實作時必須考慮到多個直譯" -"器才能使用,因此許多舊的擴充模組將無法使用。" +"減少 GIL 影響的另一種方法,是將 GIL 設為直譯器各自狀態的鎖 " +"(per-interpreter-state lock),而不是真正的全域鎖。這在 :ref:`Python 3.12 中首" +"次實作 `,並且可於 C API 中使用。預計 Python 3.13 將會提" +"供其 Python 介面。目前主要的限制可能是第三方擴充模組,因為實作時必須考慮到多" +"個直譯器才能使用,因此許多舊的擴充模組將無法使用。" #: ../../faq/library.rst:462 msgid "Input and Output" @@ -1042,16 +1044,16 @@ msgid "" "Reference Manual. Python has many modules that will help you build server-" "side and client-side web systems." msgstr "" -"請參閱函式庫參考手冊中標題為 :ref:`internet` 和 :ref:`netdata` 的章節。" -"Python 有許多模組可以幫助你建構伺服器端和用戶端的 Web 系統。" +"請參閱函式庫參考手冊中標題為「\\ :ref:`internet`\\ 」和「\\ :ref:`netdata`\\ " +"」的章節。Python 有許多模組可以幫助你建構伺服器端和用戶端的 Web 系統。" #: ../../faq/library.rst:596 msgid "" "A summary of available frameworks is maintained by Paul Boddie at https://" "wiki.python.org/moin/WebProgramming\\ ." msgstr "" -"Paul Boddie 有在 https://wiki.python.org/moin/" -"WebProgramming\\ 維護可用框架的摘要。" +"Paul Boddie 在 https://wiki.python.org/moin/WebProgramming 維護了一份可用框架" +"的摘要。" #: ../../faq/library.rst:601 msgid "What module should I use to help with generating HTML?" @@ -1062,8 +1064,8 @@ msgid "" "You can find a collection of useful links on the `Web Programming wiki page " "`_." msgstr "" -"你可以在 \"網頁程式維基頁面 \" 上找到一組有用的鏈接。" +"你可以在\\ `網頁程式維基頁面 `_" +"\\ 上找到一系列實用的連結。" #: ../../faq/library.rst:610 msgid "How do I send mail from a Python script?" @@ -1247,13 +1249,13 @@ msgstr "數學和數值" #: ../../faq/library.rst:713 msgid "How do I generate random numbers in Python?" -msgstr "如何在 Python 中生成隨機數?" +msgstr "如何在 Python 中產生隨機數?" #: ../../faq/library.rst:715 msgid "" "The standard module :mod:`random` implements a random number generator. " "Usage is simple::" -msgstr "標準模組 :mod:`random` 實作了一個隨機數生成器。用法很簡單: ::" +msgstr "標準模組 :mod:`random` 實作了一個隨機數產生器。用法很簡單: ::" #: ../../faq/library.rst:718 msgid "" @@ -1270,7 +1272,7 @@ msgstr "這將回傳 [0, 1) 範圍內的隨機浮點數。" #: ../../faq/library.rst:723 msgid "" "There are also many other specialized generators in this module, such as:" -msgstr "該模組中還有許多其他專用生成器,例如:" +msgstr "該模組中還有許多其他專用的產生器,例如:" #: ../../faq/library.rst:725 msgid "``randrange(a, b)`` chooses an integer in the range [a, b)." @@ -1302,4 +1304,4 @@ msgid "" "There's also a ``Random`` class you can instantiate to create independent " "multiple random number generators." msgstr "" -"還有一個 ``Random`` 類別,你可以將它實例化以建立多個獨立的隨機數生成器。" +"還有一個 ``Random`` 類別,你可以將它實例化以建立多個獨立的隨機數產生器。" \ No newline at end of file