本篇文章更新時間:2026/06/03
如有資訊過時或語誤之處,歡迎使用 Contact 功能通知或向一介資男的 LINE 社群反應。
如果本站內容對你有幫助,歡迎贊助支持 。
內容目錄
問題:多語系環境下,全域搜尋只認當前語言
iDempiere 支援多語系,你可以用繁體中文登入、用英文登入、用日文登入。但有一個體驗問題從來沒被解決:
全域搜尋(Alt+G)只能用當前登入語系搜尋選單項目。
具體來說:
- 登入語系是
zh_TW,你輸入「Purchase Order」→ 找不到。你必須輸入「採購單」。 - 登入語系是
en_US,你輸入「會計」→ 找不到。你必須輸入「Accounting Rules」。
這在跨國團隊或雙語環境中非常惱人。一個台灣工程師可能記得功能的英文名稱(因為文件是英文的),但系統設成中文;一個外國顧問可能知道中文名稱(因為客戶這樣叫),但系統設成英文。
更實際的場景:你在看英文教學文件,文件裡寫「open the Purchase Order window」,你得先在腦中翻譯成中文,再去搜尋「採購單」。每天不知道做幾次這種無意義的翻譯。
解決方案:跨語系搜尋,一行設定都不用
idempiere-multilang-search 是一個 iDempiere 外掛,安裝後全域搜尋自動支援所有已翻譯語系的比對。
Before:登入 zh_TW,只能用中文搜尋。
After:登入 zh_TW,輸入「Purchase Order」→ 找到「採購單」。輸入「採購」也能找到。兩種語言同時比對。
反過來也一樣:登入 en_US,輸入「會計」→ 找到「Accounting Rules」。
不需要任何設定。安裝、重啟、完成。
系統需求
- iDempiere 12 或更新版本
- 至少 2 個啟用的語系,且選單項目已翻譯(System Admin → Language → Verify Language)
安裝方式
方式一:update-prd.sh(建議)
cd /opt/idempiere
./update-prd.sh file:///path/to/extracted/repository tw.idempiere.multilang.search
systemctl restart idempiere
方式二:Felix Web Console
- 從 Releases 下載 plugin JAR
- 開啟
http://<server>:8080/osgi/system/console/bundles(帳號:SuperUser / System) - 點 Install/Update,選擇 JAR,點 Install or Update
- 重啟 iDempiere
方式三:Docker
services:
idempiere:
image: idempiereofficial/idempiere:12-release
entrypoint:
- bash
- -c
- |
cp /custom-plugins/*.jar /opt/idempiere/plugins/
grep -q multilang /opt/idempiere/configuration/org.eclipse.equinox.simpleconfigurator/bundles.info 2>/dev/null || \
echo 'tw.idempiere.multilang.search,1.0.0,plugins/tw.idempiere.multilang.search_1.0.0.jar,4,false' >> /opt/idempiere/configuration/org.eclipse.equinox.simpleconfigurator/bundles.info
exec ./docker-entrypoint.sh idempiere
volumes:
- ./plugins:/custom-plugins:ro
驗證安裝成功
重啟後檢查 server log,應出現:
Multi-Language Global Search plugin initialized
Multi-Language Search: loaded N alternative labels for M menu items
Multi-Language Global Search patched successfully
解除安裝
移除外掛後重啟即可。沒有資料庫變更、沒有殘留設定,系統恢復原始行為。
技術深入:為什麼是 OSGi Fragment
這個外掛和我之前介紹的 看板外掛、預約管理外掛 在架構上有根本差異。前兩個是 WAB(Web Application Bundle)— 獨立的 web 應用程式,有自己的 servlet 和靜態資源。這個則是 OSGi Fragment。
Fragment vs Bundle
| 特性 | Bundle(WAB) | Fragment |
|---|---|---|
| 獨立性 | 有自己的 classloader、lifecycle | 附加到 host bundle,共享 classloader |
| 啟動狀態 | Active(state=32) | Resolved/Fragment(state=4),不會是 Active |
| 適用場景 | 新增獨立功能(新頁面、新 API) | 擴充/修補 host bundle 的既有功能 |
| 存取範圍 | 只能存取 host 的 exported packages | 可存取 host 的所有 packages(含 private) |
為什麼這裡必須用 Fragment?因為我們要修改的目標 — GlobalSearch 和 MenuSearchController — 是 iDempiere ZK Web UI 的內部類別。它們的關鍵 field 是 private 的,正常 bundle 無法存取。Fragment 附加到 host bundle(org.adempiere.ui.zk)後,共享同一個 classloader,可以存取所有內部類別。
為什麼不直接改原始碼
這是 iDempiere 外掛開發的鐵律:不改核心。改了核心,每次升版都是噩夢。OSGi Fragment 讓你在不動核心一行程式碼的前提下,「插入」到核心的運行過程中。
技術深入:Runtime Patching 機制
全域搜尋元件(GlobalSearch)是在 ZK Desktop 建立時由 HeaderPanel 初始化的。我們不能覆蓋它的 ZUL 檔案(多 fragment 的資源載入順序不可控),所以採用 Runtime Patching — 在元件建立之後、使用者操作之前,把舊元件替換成增強版。
流程
- 註冊 UiLifeCycle listener:透過 ZK 的
metainfo/zk/config.xml機制,fragment 在 classpath 中放置設定檔,ZK 啟動時自動掃描並註冊 listener。 - 攔截 Desktop 建立:listener 的
afterComponentAttached偵測到HeaderPanel被掛載時觸發。 - 延遲到正確時機:使用
Events.echoEvent延遲執行 patch,確保所有元件已完成初始化。echoEvent 的機制是 server → client → server 一次往返,保證在 UI 完整渲染後才執行。 - 替換元件:用 Reflection 取得
HeaderPanel.globalSearch(private field),取出舊的MenuSearchController中的tree,建立增強版的MultiLangMenuSearchController,再建立新的GlobalSearch替換舊的。
Graceful Degradation
整個 patching 過程包在 try-catch 中。如果任何步驟失敗(例如 iDempiere 升版改了 field 名稱),系統 fallback 到原始行為 — 全域搜尋仍然可用,只是回到單語系比對。不會壞掉,只是失去多語系功能。
技術深入:跨語系比對邏輯
核心修改在 MultiLangMenuSearchController 的比對器中。原始的 MenuListComparator 只比對 MenuItem.getLabel()(當前語系的顯示文字)。增強版額外比對一張 altLabelsMap。
altLabelsMap 的建立
使用者登入後,外掛執行一次 SQL 查詢:
- 查
AD_Menu_Trl:取得所有「非當前語系」的已翻譯選單名稱。 - 查
AD_Menu基礎表:如果使用者不是基礎語系(en_US),也把英文名稱加入。
結果是一個 Map<Integer, List<String>>:每個 AD_Menu_ID 對應一組「其他語系的名稱」。
比對流程
使用者輸入 "Purchase Order"
→ 先比對當前語系 label(zh_TW: "採購單")→ 不符合
→ 再比對 altLabelsMap 中該 menu 的其他語系名稱
→ 找到 "Purchase Order"(en_US)→ 符合!
→ 結果顯示「採購單」
效能影響極小:altLabelsMap 在登入時載入一次(通常幾百筆),搜尋時只是 in-memory 的字串比對。
設計原則
這個外掛體現了我認為 iDempiere plugin 開發應該追求的幾個原則:
- 零設定:安裝即生效。不需要使用者去後台開任何開關。
- 零殘留:移除後系統完全恢復原狀。沒有資料庫變更、沒有 AD 記錄、沒有設定檔。
- Graceful degradation:失敗時 fallback 到原始行為,不會破壞系統。
- 不改核心:所有擴充透過 OSGi 機制完成,核心升級不受影響。
授權
GPL v2 — 和 iDempiere 相同。
延伸閱讀
本站 iDempiere 系列:
- [iDempiere] 開源看板外掛:Kanban 管理 R_Request
- [iDempiere] 開源預約管理外掛:Google Calendar 風格行事曆
- [iDempiere] Plugin 開發入門:用 OSGi 擴充 iDempiere 而不改核心
如果你的 iDempiere 環境有多語系需求,或需要客製 plugin 開發協助,歡迎透過聯絡我們洽詢。
