首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> ASP.NET 基礎

ASP.NET WebForms 多語系 (i18n) 動態管理方案:資料庫驅動、使用者自訂、免重新部署

     編輯:ASP.NET 基礎

傳統 .resx 資源檔痛點

  • 新增控制項需開發人員手動加入資源鍵值。
  • 文字修改需改 code、重新編譯、部署。
  • 業務單位無法自行調整術語 (如「客戶」↔「顧客」)。

本方案核心架構

資料庫表 LanguageText
├── ControlId (PK)     -- 控制項唯一識別 (頁面+ID)
├── Culture (PK)       -- zh-TW, en-US, ja-JP...
├── DisplayText        -- 顯示文字
└── TooltipText        -- 提示文字 (可選)

執行流程:
1. 頁面載入 → 遞迴掃描所有目標控制項 (Label, Button, GridView Header...)
2. 依 ControlId + 當前 Culture 查詢 DB → 套用 Text/ToolTip
3. 管理頁面 → 列出所有可設定控制項 → 編輯多語文字 → 存回 DB
4. 下次造訪自動套用新文字,零部署

資料庫結構

CREATE TABLE dbo.PageText (
    PagePath      NVARCHAR(260) NOT NULL,  -- ~/Admin/UserList.aspx
    ControlId     NVARCHAR(128) NOT NULL,  -- lblTitle, btnSave, gvUsers_Header_Name
    Culture       CHAR(5) NOT NULL,        -- zh-TW, en-US, ja-JP
    DisplayText   NVARCHAR(MAX) NULL,
    TooltipText   NVARCHAR(MAX) NULL,
    UpdatedAt     DATETIME2 DEFAULT SYSUTCDATETIME(),
    UpdatedBy     NVARCHAR(50),
    CONSTRAINT PK_PageText PRIMARY KEY (PagePath, ControlId, Culture)
);

核心程式碼:基底頁面自動套用

public class LocalizedPage : System.Web.UI.Page
{
    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
        if (!IsPostBack) ApplyLocalization();
    }

    private void ApplyLocalization()
    {
        string culture = Thread.CurrentThread.CurrentUICulture.Name; // "zh-TW"
        string pagePath = Request.AppRelativeCurrentExecutionFilePath; // "~/Admin/UserList.aspx"

        var texts = DbHelper.GetPageTexts(pagePath, culture); // Dictionary
        ApplyToControls(this, texts);
    }

    private void ApplyToControls(Control parent, Dictionary texts)
    {
        foreach (Control c in parent.Controls)
        {
            string key = $"{pagePath}_{c.ID}";
            if (texts.TryGetValue(key, out var pair))
            {
                SwitchText(c, pair.DisplayText, pair.TooltipText);
            }
            if (c.HasControls) ApplyToControls(c, texts);
        }
    }

    private void SwitchText(Control c, string display, string tooltip)
    {
        switch (c)
        {
            case Label l: l.Text = display; break;
            case Button b: b.Text = display; break;
            case LinkButton lb: lb.Text = display; break;
            case HyperLink hl: hl.Text = display; break;
            case GridView gv: ApplyGridViewHeaders(gv, display); break; // 需對應 Header 控制項 ID
        }
        if (!string.IsNullOrEmpty(tooltip)) c.ToolTip = tooltip;
    }
}

管理介面:掃描並編輯

<%@ Page Language="C#" Inherits="LocalizedPage" %>
<asp:Button ID="btnScan" Text="掃描本頁可本地化控制項" OnClick="btnScan_Click" />
<asp:GridView ID="gvTexts" AutoGenerateColumns="False" runat="server">
    <Columns>
        <asp:BoundField DataField="ControlId" HeaderText="控制項" />
        <asp:TemplateField HeaderText="繁體中文">
            <ItemTemplate><asp:TextBox ID="txtZh" Text='<%# Eval("ZhText") %>' runat="server" /></ItemTemplate>
        </TemplateField>
        <asp:TemplateField HeaderText="English">
            <ItemTemplate><asp:TextBox ID="txtEn" Text='<%# Eval("EnText") %>' runat="server" /></ItemTemplate>
        </TemplateField>
        <asp:TemplateField HeaderText="日本語">
            <ItemTemplate><asp:TextBox ID="txtJa" Text='<%# Eval("JaText") %>' runat="server" /></ItemTemplate>
        </TemplateField>
    </Columns>
</asp:GridView>
<asp:Button ID="btnSave" Text="儲存所有語系" OnClick="btnSave_Click" />
protected void btnScan_Click(object sender, EventArgs e)
{
    var list = ScanLocalizableControls(this);
    gvTexts.DataSource = list;
    gvTexts.DataBind();
}

private List ScanLocalizableControls(Control parent)
{
    var list = new List();
    foreach (Control c in parent.Controls)
    {
        if (IsLocalizable(c))
        {
            string key = $"{Request.AppRelativeCurrentExecutionFilePath}_{c.ID}";
            list.Add(new LocalizableItem 
            { 
                ControlId = c.ID, 
                ControlType = c.GetType().Name,
                ZhText = GetDbText(key, "zh-TW"),
                EnText = GetDbText(key, "en-US"),
                JaText = GetDbText(key, "ja-JP")
            });
        }
        if (c.HasControls) list.AddRange(ScanLocalizableControls(c));
    }
    return list;
}

private bool IsLocalizable(Control c)
{
    return c is Label || c is Button || c is LinkButton || c is HyperLink 
        || c is GridView || c is Literal; // 依需求擴充
}

進階功能

  • 匯入/匯出 Excel:方便翻譯廠商離線作業。
  • 版本控管:文字變更記錄歷史,支援回滾。
  • 預設值回填:DB 無資料時自動抓取控制項原始 Text 寫入 DB,避免空白。
  • 快取策略:MemoryCache 快取頁面文字 10~30 分鐘,減少 DB 查詢。
  • GridView/Repeater 標題:掃描時產生虛擬 ControlId 如 gvUsers_Header_Name,對應 BoundField.HeaderText。

效能與安全

面向 做法
DB 查詢 單次查詢撈取整頁所有文字 (WHERE PagePath=@p AND Culture=@c)
快取 MemoryCache key: "PageText_{PagePath}_{Culture}",滑動過期 20 分鐘
XSS 儲存前 HTML Encode,輸出時 Literal/Label 自動編碼
權限 管理介面僅限「最高管理員」角色存取

遷移既有專案步驟

  1. 建立資料表、DbHelper、LocalizedPage 基底類別。
  2. 逐頁將 Inherits="System.Web.UI.Page" 改為 Inherits="LocalizedPage"。
  3. 執行一次「掃描」自動建立所有控制項預設文字至 DB。
  4. 驗證切換語系正常運作。
  5. 開放管理介面給業務調整術語。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved