這篇《如何快速實現多語言處理:WinForm應用實戰開發指南 - 如何快速實現多語言處理?》里面介紹了WinForms開發中多語言的處理解決方案,整個多語言解決方案是以實際需求為驅動,以減少代碼改動,高效處理為目的,通過基類繼承的方式減少代碼修改,通過引入翻譯API方式減少翻譯處理時間,本隨筆繼續深化這個多語言處理方案的介紹,是指整合在開發框架中進行無縫的使用。
PS:給大家推薦這個WinForm應用界面開發組件——DevExpress WinForms,它能完美構建流暢、美觀且易于使用的應用程序,無論是Office風格的界面,還是分析處理大批量的業務數據,它都能輕松勝任!
DevExpress WinForms Subscription官方最新版免費下載試用,歷史版本下載,在線文檔和幫助文件下載-慧都網
1. 提取多語言處理JSON文件
我們通過把程序界面或者代碼里面的中文提取出來,放到JSON文件中,對不同模塊可以分為不同的JSON文件,如下所示。
然后使用輔助類的接口調用實現英文內容的翻譯,如下代碼所示:
//界面漢化
System.Threading.Thread.CurrentThread.CurrentUICulture=new System.Globalization.CultureInfo("en-US");//英文界面
//如果語言包有空白的翻譯內容,可以調用下面語句(百度翻譯)獲得翻譯內容,減少手工翻譯的繁瑣
//翻譯后將內容進行審核或調整一下即可使用
//正式發布程序需要屏蔽這個調用
JsonLanguage.Default.Translate();
可以把內容翻譯并調整好。
這些JSON資源文件,按照程序運行的相對目錄 Lang/en-US/ABC.json 目錄放置即可,可以根據不同的模塊或者需求分開不同的文件,程序加載多語言信息的時候,會全部進行加載。
2. 繼承窗體基類和使用統一的信息提示輔助類
準備好多語言文件后,那么多語言的處理還需要一些輔助的處理才能實現,由于我們已經把多語言處理的邏輯放在了窗體的基類,如BaseForm窗體基類里面,如果原來窗體就是繼承了這個基類,那么多語言處理邏輯會在窗體加載后自動進行處理的了。
通過繼承關系的處理,我們可以不用修改子窗體任何代碼就可以自動具有多語言的處理過程了,子窗體在加載完畢后,自動遍歷內部控件實現多語言的處理邏輯。
如果我們一些窗體,不方便集成這些基類,那么也可以使用增加代碼的方式進行處理即可,在窗體的Load或者Shown事件里面實現處理,如下代碼所示。
private void Form1_Shown(object sender, EventArgs e)
{
//窗體加載并顯示后,對窗體實現多語言處理
if (!this.DesignMode)
{
LanguageHelper.InitLanguage(this);
}
}
還有一種特殊的界面,就是一些信息的提示,如消息框的提示,這種默認的處理方式,如下面是一般的消息提示代碼。
XtraMessageBox.Show("歡迎使用Winform開發框架", "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Information);
我們在框架里面也是通過輔助類封裝的方式進行提示的,因此多語言也只需要修改這個輔助類就可以了,不用修改我們窗體里面已完成的代碼。
MessageDxUtil.ShowTips("歡迎使用Winform開發框架");
由于我們在框架里面統一使用消息提示處理,那么多語言的處理,在其內部實現即可,如下所示。
/// <summary>
/// 顯示一般的提示信息
/// </summary>
/// <param name="message">提示信息</param>
/// <param name="args">字符串里面的參數內容</param>
/// <returns></returns>
public static DialogResult ShowTips(string message, params object[] args)
{
//對消息的內容進行多語言處理
message=JsonLanguage.Default.GetString(message);
if (args !=null)
{
message=string.Format(message, args);
}
return DevExpress.XtraEditors.XtraMessageBox.Show(message, Caption_Tips, MessageBoxButtons.OK, MessageBoxIcon.Information);
}
如果是帶有一些參數占位符的多語言處理,我們一樣可以實現它。
MessageDxUtil.ShowTips("用戶【{0}】身份驗證正確", loginName);
通過窗體基類BaseForm內部邏輯的修改,以及一些輔助類如MessageDxUtil的修改,我們悄悄的就實現了多語言的處理邏輯,原來的代碼還是不用修改就支持了多語言的處理,大大節省了工作量。
當然如果是一些特殊的情況,我們還是可以通過統一的多語言處理邏輯來獲得它的內容,如下代碼所示。
3. 模塊化的多語言支持
除了上面介紹的一些總體邏輯,我們在WinForms開發過程中,往往也是以模塊化的開發方式來提高開發效率,如利用框架的公用類庫、基礎界面模塊、分頁控件模塊、字典模塊、權限管理系統等等,這些模塊都是整個模塊化過程中的一部分,當然多語言的支持也是很必要,由于我們已經通過基類和多余與模塊獨立的方式來簡化它,但是里面的多語言資源我們也應該以模塊方式提供,翻譯好并集成在系統中使用即可。
上面的Basic.json是框架整個基礎的界面(如分頁、字典等)英文參照資源,Security.json為權限系統的界面資源,Commons.json為一些其他模塊的多語言資源等等 。
以及權限系統的多語言參照資源。
例如分頁控件的展示界面,我們的英文版效果如下所示(以用戶管理界面為例):
編輯界面效果如下所示:
權限管理系統整體的界面效果如下所示,包括工具欄、菜單、選項卡,以及里面的所有控件,測試均表現OK。
當然,里面的數據源肯定還是中文的,除非我們系統的數據全部以英文語境進行錄入。
本文轉載自:博客園 - 伍華聰
Ruffle 是一個開源免費的 Flash 播放器,網頁版 ruffle.js 體積很小,打個包 1.7 MB,調用也非常簡單。aardio 標準庫中的 web.view ( WebView2 ) 可以完美支持 ruffle.js ,不過我們先要解決 2 個小問題:
1、ruffle.js 通過網址加載 Flash 動畫不太正常會報錯,但是直接內存加載動畫數據沒問題。
2、瀏覽器不能直接用代碼訪問本地文件。
我們先使用 external 接口導出 aardio 函數來解決上面的問題,使用 aardio 將遠程或本地的 swf 文件先讀入到內存,再轉換為字節數組傳給 ruffle.js 就可以了,關鍵代碼如下:
import web.view;
var wb=web.view(winform);
wb.external={
getSwf=function(){
//可返回包含 SWF 文件二進制數據的數組、SAFEARRAY,buffer
return com.SafeArrayV( inet.http.get("https://update.aardio.com/v10.files/demo/transparent.swf") );
//本地文件可以直接返回 buffer
return string.loadBuffer("\res\tet.swf");
};
}
給 ruffle.js 返回數組就行(buffer、數組、COM 數組都行), 要注意 string.load 加載文件返回的是字符串,string.loadBuffer() 返回的是字節數組。
網頁里面用下面的 JavaScript 加載 Flash 動畫:
const ruffle=window.RufflePlayer.newest();
const player=ruffle.createPlayer();
player.style.width="100vw";
player.style.height="100vh";
const container=document.getElementById("container");
container.appendChild(player);
player.load({data: await aardio.getSwf() });
在 JS 里用 aardio.getSwf() 調用 aardio 導出的 wb.external.getSwf() 函數,注意所有本地函數在 WebView2 里都是異步函數,所以用了 await 取返回值( 要在異步函數里才能使用 await )。
ruffle.js 最好是通過 HTTP 服務器加載,這在 aardio 中很容易解決,如果創建了 aardio 工程,只要簡單地將所有網頁以及 ruffle.js 添加到工程的資源目錄內,然后用類似:
wb.go( wsock.tcp.simpleHttpServer.startUrl("/res/swf/index.html") )
打開網頁就可以了,aardio 會自動使用 HTTP 協議內存加載這個資源目錄下的所有文件(可以發布為獨立 EXE 文件)。
寫范例的時候為了方便大家復制就可以運行,沒有創建工程文件,HTML代碼與 aardio 代碼混寫在一起,所以我寫了一個擴展庫 web.ruffle 用于獲取通過 HTTP 服務器訪問 ruffle.js 的地址。
首先在 aardio 中導出 getRuffleScriptSrc 函數:
import web.ruffle;
import web.view;
var wb=web.view(winform);
wb.external={
getSwf=function(){
return com.SafeArrayV( inet.http.get("https://update.aardio.com/v10.files/demo/transparent.swf") );
};
getRuffleScriptSrc=function(){
return web.ruffle.getUrl("/ruffle/ruffle.js");
};
}
然后在 HTML 代碼中添加一個空的 script 元素:
<script id="ruffle"></script>
然后在 JavaScript 中調用 aardio 函數獲取到 ruffle.js 的地址并加載該 JS:
var ruffleScript=document.getElementById("ruffle");
ruffleScript.src=await aardio.getRuffleScriptSrc();
完整范例源碼請參考 「 aardio 范例 / Web 界面 / web.view / Flash 」
aardio 擴展庫 process.ruffle —— 可以讓 Ruffle 桌面版的窗口嵌入我們的軟件界面。這個庫之前的實現是把 Ruffle 創建的桌面獨立窗口加上 WS_CHILD 樣式變為子窗口,再指定父窗口,調整大小后嵌入我們的界面。但是這種方式 —— 有時候會出現一些奇怪的問題( 例如 Flash 動畫卡住,鼠標晃一下才會動 )。
在 aardio 里還有一個更好的選擇:我們可以用 orphanWindow 功能將獨立窗口偽裝為子窗口,關鍵代碼只有一句:
this._form.orphanWindow(,this.hwndFlash);
Ruffle 桌面版打開 Flash 會創建一個帶標題欄,帶邊框的獨立窗口,如果在打開 Flash 動畫以后再去移除窗口邊框 —— 那就會看到帶邊框的窗口在屏幕上一閃而過。下面我們就來解決這個問題。
首先我們用 process.apiHook 替代 process 創建 Ruffle 進程:
this.apiHook=process.apiHook(ruffleExePath,args,{
suspended=true;//啟動后暫停
});
注意參數里指定了進程啟動后暫停,下面安裝好 API 鉤子以后再恢復運行。
下面先安裝鉤子攔截創建窗口的 API 函數 CreateWindowExW ,以及設置窗口位置的函數 SetWindowPos 。關鍵代碼如下:
//安裝 HOOK
this.hookCreateWindowEx=this.apiHook.install("User32.dll"
,"CreateWindowExW","CreateWindowExHook.dll","_CreateWindowExHook@48");
this.hookSetWindowPos=this.apiHook.install("User32.dll"
,"SetWindowPos","CreateWindowExHook.dll","_hookSetWindowPos@28");
然后再獲取外部進程的 hookSet 函數并轉換為 aardio 函數調用:
var hookSet=this.process.remoteApi("void(addr pc,addr ps,addr hwndHost)"
,"CreateWindowExHook.dll","hookSet","cdecl");
hookSet(
this.hookCreateWindowEx.addrTrampoline,//這是原來的 CreateWindowEx 函數指針地址
this.hookSetWindowPos.addrTrampoline,//這是原來的 SetWindowPos 函數指針地址
this._form.hwnd
);
現在可以用下面的代碼讓進程繼續運行了:
this.process.resume();
再加上下面的代碼保證主進程退出時 ruffle.exe 也會自動退出:
this.process.assignToJobObject(..process.job.limitKill);
DLL 的源代碼很少,在 process.ruffle 擴展庫目錄下可以找到。要注意 Ruffle 創建了多個窗口,設置參數要避免改動不可見的那個窗口。
測試一下:
調用范例:
import win.ui;
/*DSG{{*/
var winform=win.form(text="開源 Flash 控件 Ruffle";right=759;bottom=512)
winform.add(
static={cls="static";text="Static";left=3;top=1;right=758;bottom=443;db=1;dl=1;dr=1;dt=1;z=1}
)
/*}}*/
import process.ruffle;
var ruffle=process.ruffle(winform.static);
winform.show();
//自定義 Flash 參數
ruffle.flashVars={k="v",k2=123};
//也可以打開本地文件
ruffle.go("https://ruffle.rs/demo/swfs/wasted_sky.swf");
win.loopMessage();
請注意 Ruffle 桌面版不支持 Win7。