專為 Minecraft 設計的翻譯系統,支援多種語言,打造出一個對使用者以及譯者皆有善的平台。
此專案從 2022/3/7 正式動工,理想是在 2023/1/1 能將所有功能全數完工。
總進度:約 16%
基礎設計:100%
API:100%
伺服器腳本:5%
函式庫 (Dart/Kotlin): 76.4%
翻譯器端 (UI):1%
遊戲端: 0%
最普遍的翻譯格式,使用資源包載入,檔案路徑為 assets/<namespace>/lang/<langCode>.json
。
namespace -> 命名空間 langCode -> 要載入的語言代碼,例如繁體中文 (台灣) 為
zh_tw
-
Patchouli 模組手冊
Patchouli 官方文檔
有些模組作者會開啟 i18n 的選項,可直接透過一般格式
來載入翻譯,但仍有許多作者是直接寫死在 json 裡面,如果直接覆蓋很容易遇到因版本、翻譯錯誤等因素造成的衝突問題,因此 RPMTranslator 預計會使用自訂的格式載入,再透過模組去將這些內容顯示到遊戲內 (mixin)。
預計的翻譯鍵格式為
patchouli.<namespace>.<bookName>.content.<fileFolder>.<fileName>.<field>
參數介紹:
- namespace
模組的 namespace - bookName
書本名稱,例如 Botania 模組中的lexicon
(植物學) - fileFolder
該檔案所在的資料夾,常見的有
entries
、categories
、templates
- fileName
該條目/類別的檔案名稱,例如
test_entry.json
的 fileName 就是test_entry
- field
原先在手冊內使用的 key ,例如該手冊的內容長這樣,那 field 就是name
與description
,非文字的內容或不可翻譯不算
{
"name": "Test Category",
"description": "This is a test category for testing!",
"icon": "<iconPath>"
}
例外的格式
譬如說該手冊的標題、子標題...
例如標題的翻譯鍵格式是
patchouli.<namespace>.<bookName>.addon.title
例如圖檔、模組自訂的檔案、自訂的手冊都會使用此方式載入,檔案路徑會在 assets/<filePath>
,若模組無法使用以上這些方式載入,預計會透過模組端去 mixin 模組來載入內容,或自己開發一個透過 java class/field 的名稱去辨別的特殊載入方式。
filePath -> 資源所在檔案路徑
代表一個模組所有文字格式的原始語系資訊。
儲存了檔案所屬的模組資訊 (namespace、RPMTW 資料庫的模組 UUID),此模組包含的原始語系檔案。
範例格式:
{
"uuid": "<uuid>",
"namespace": "mod_a",
"modUUID": "<所屬模組的 UUID (該模組必須收錄於 RPMTW 資料庫,可以為 null)>",
"patchouliAddons": ["title", "landingText..."]
}
代表一個文字格式的原始語系檔案。
儲存了檔案路徑、檔案格式、檔案中包含的原文條目。
- path
檔案路徑 對應於 repository 儲存的原文路徑
Patchouli 手冊的路徑類似這樣assets/<namespace>/patchouli_books/<bookName>/.../....json
- type
檔案格式
gsonLang
-> 1.13 以上版本所使用的格式
minecraftLang
-> 1.12 以下(含)版本所使用的格式)
patchouli
-> Patchouli 手冊的格式
plainText
-> 純文字格式,每行文字為一個原文條目,原文條目中的 key 則使用原文內容的 md5 雜湊值
- sources
檔案中包含的原文條目
範例格式:
{
"uuid": "<uuid>",
"sourceInfoUUID": "<此檔案所屬的模組的原始語系資訊UUID>",
"path": "assets/<namespace>/lang/en_us.json",
"type": "gsonLang",
"sources": ["SourceText1","SourceText2..."]
}
儲存了譯文,該譯文的譯者、投票資訊。
範例格式:
{
"uuid": "<uuid>",
"sourceUUID": "<此翻譯所屬的原文條目>",
"content": "你好,世界!",
"translatorUUID": "<譯者的UUID>",
"language": "zh-TW"
}
代表一個翻譯的條目
儲存了原文內容、翻譯鍵、翻譯內容、用到此原文的遊戲版本。
範例格式:
{
"uuid": "<uuid>",
"source": "Hello, World!",
"key": "gui.mod_a.title",
"gameVersions": ["1.16", "1.18"],
"type": "general"
}
代表對譯文的投票
其中 type 中的 up
代表贊成,down
代表不贊成。
範例格式:
{
"uuid": "<uuid>",
"type": "up",
"translationUUID": "<要投票的譯文的UUID>",
"userUUID": "<投票者的UUID>",
}
用於儲存專有名詞、用語、Minecraft 標準化譯名...,例如 Creeper
是指苦力怕
而非爬行者
參數說明:
- term 詞語名稱 (原文)
- translation 詞語譯文
- description 關於此詞彙的描述
- language 此詞彙所屬的語言
- modUUID 此詞彙所屬的模組 UUID,若為
null
則代表全域詞彙(任何地方都可使用)
範例格式:
{
"uuid": "<uuid>",
"term": "Creeper",
"translation": "苦力怕",
"description": "常見的敵對生物之一,它們悄無聲息地接近玩家,當距離目標3格內就會開始爆炸。由於它們獨特的外表和殺傷力以及破壞地形的能力,苦力怕成為了 Minecraft 的象徵標誌之一,遊戲內外都很受歡迎。",
"language": "zh-TW",
"modUUID": null
}
用於評論翻譯內容、討論翻譯...
參數說明:
- content 評論的內容,可使用 Markdown 格式,可以使用
@ <userUUID>
來標記別人 (在 UI 中不會顯示 UUID,而是 username) - type 評論的類型,如果是評論翻譯則為
translate
- userUUID 發送此評論的使用者 UUID
- parentUUID 如果評論類型是
translate
則它的父項為SourceText
, 如果類型是wiki
則父項為MinecraftMod
. - createdAt 此評論第一次發送的時間
- updatedAt 此評論最後編輯的時間
- isHidden 是否隱藏此評論,當評論被使用者按下刪除按鍵,就會被隱藏而非從資料庫完全移除資料,將會於一週後完全移除資料
- replyCommentUUID 是否有回覆其他評論,若有則返回該評論的 UUID,否則為
null
範例格式:
{
"uuid": "<uuid>",
"content": "@<tagUserUUID>\n不錯的翻譯!\n但有個字打錯了,不是 `醬魂` 應該是 `匠魂`,麻煩修正一下! 謝謝",
"type": "translate",
"userUUID" : "<addCommentUserUUID>",
"parentUUID": "<SourceTextUUID>",
"createdAt": "<timestamp>",
"updatedAt": "<timestamp>",
"isHidden": false,
"replyCommentUUID": null
}
{
"modSourceInfoUUID" : null,
"totalWords": 1000,
"translatedWords": {"zh-TW": 10, "zh-CN": 7},
"lastUpdated": <lastUpdatedTime>
}
{
"uuid": "<uuid>",
"userUUID": "<userUUID>",
"translatedCount": 1050,
"votedCount": 10
}
{
"translators": [<a>, <b>, <c>...],
"start": <startTime>,
"end": <endTime>,
"limit": 50,
"skip": 0,
"total": 32
}
主要用於處理自訂格式的翻譯檔案,預計使用 Github Action 進行打包,每當譯文更新時就會自動打包一次,翻譯內容會由伺服器端自動推送到儲存自訂翻譯用的 repository。
API 參數說明
- format 要匯出的格式
- language 語言代碼
- mods 要取得哪些模組的語系檔案,格式為該模組的 namespace
取得指定模組的所有一般格式
語系翻譯檔案
範例: https://<apiHost>/translate/export?format=json&language=zh-TW&version=1.18&namespaces=<modA>,<modB...>
{
"key1": "translation1",
"key2": "translation2",
"key3": "translation3"
}
取得指定模組的所有純文字格式
語系翻譯檔案
範例: https://<apiHost>/translate/export?format=plainText&language=zh-TW&version=1.18&namespaces=<modA>,<modB...>
{
"assetsPath1": "<translation>",
"assetsPath2": "<translation>"
}
取得指定模組的所有Patchouli 手冊
語系翻譯檔案,詳細定義可參考最上方的手冊翻譯格式
範例: https://<apiHost>/translate/export?format=patchouli&language=zh-TW&version=1.18&namespaces=<modA>,<modB...>
{
"patchouli.<namespace>.<bookName>.content.<fileFolder>.<fileName>.<field>": "translation1",
"patchouli.<namespace>.<bookName>.content.<fileFolder>.<fileName>.<field>": "translation2",
"patchouli.<namespace>.<bookName>.content.<fileFolder>.<fileName>.<field>": "translation3"
}
會有一個 repository 來儲存原文內容,一般格式與 Patchouli 手冊格式的檔案會透過腳本上傳,自訂資源則是透過人工手動上傳或實現特定類型的自動化處理。
伺服器會定時每一小時檢查是否有更動,若有更動將會開始同步新的原文
若遇到一個模組有多個遊戲版本,將會採用相同的翻譯,判斷條件為
- 屬於同一個模組(模組的 namespace 必須相同)
- 翻譯鍵必須相同
- 原文必須相同 在實際的翻譯界面中會直接忽視版本選項
將會開發一個工具,可以自動在網路上爬取模組的原文、手冊檔案,並自動更新至最新版,預計在 Github Action 上執行,目標覆蓋模組平台上有上架的所有模組
因為怕 RPMTW 伺服器承受不住,偷微軟拔拔的資源來用
預計從 CurseForge、Modrinth 這兩個模組平台上擷取
平台第一開始運作的時候會優先爬取熱門模組的檔案,接著會定時從 CurseForge 與 Modrinth 爬取新建立模組的資料
由於 CurseForge 的新模組數量較多,因此預計每 12 小時(半天)從 CurseForge 爬取一次,Modrinth 則是每 24 小時 (一天)
備註:由於 CurseForge 將於 2022 四月份停止舊版 API 的運作,新版的 API 將不會讓所有模組可供下載,如果模組作者有關閉被 API 擷取的設定,變成說需要手動新增,使全面覆蓋增加難度。
由於 RPMTW 第一開始翻譯內容在 Crowdin 上,因此此系統開發完成後,勢必要將之前的翻譯內容、貢獻紀錄全部遷移至此系統
TODO:實作方式
- 物品類
- 實體類
- GUI 類
- 手冊類
Class => 物件導向程式語言的一種構造,是建立物件的藍圖,詳情可參考網路上或維基百科的介紹 GUI => 圖形使用者介面,簡單講就是視覺化的界面 Mixin => 簡單來說就是混合 class,可以修改、替換 class 中的內容,達成一些模組/Minecraft沒有提供的功能,Minecraft 中的 Mixin Namespace -> 命名空間,模組 ID 也是命名空間的一種
伺服器: RPMTW/RPMTW-Server#8
API 拉取請求:RPMTW/RPMTW-Server#9
遊戲端: RPMTW/RPMTW-Platform-Mod#145