【问题标题】:Add second language support with root path in an existing ASP.NET WebForms site在现有 ASP.NET WebForms 站点中使用根路径添加第二语言支持
【发布时间】:2025-11-24 03:05:01
【问题描述】:

我从一个非常小的 ASP.NET WebForms 项目继承而来,我的客户想为其添加第二语言。

对于每个“somepage.aspx”,我想支持它的“第二语言路径”版本,例如“fr/somepage.aspx”。我想使用正常的全球化(CurrentCulture + 两种语言的资源文件)来处理这个问题,并避免复制每个页面。我必须保持原始路径有效,因此我暂时排除了 ASP.NET MVC(因为不知道我是否可以继续支持“.aspx”路径)。

这可能吗?

【问题讨论】:

  • 希望对您有所帮助:*.com/questions/373106/…
  • 它确实为重新开始提供了一个解决方案,但就我而言,我必须保持现有路径有效。真正的问题是“我怎样才能让“site.com/page.aspx”处理对“site.com/fr/page.aspx”的请求,但保持“site.com/fr/page.aspx”作为 URL用户看到。
  • 我想您可以创建两个路由(第一个路由用于有语言的页面,第二个用于没有语言的页面)并创建一个与您想要接受的语言匹配的约束。请参阅 msdn.microsoft.com/en-us/magazine/dd347546.aspxweblogs.asp.net/scottgu/archive/2009/10/13/…。您的所有路径都离开了您网站的根目录,还是您也有 N 个要支持的文件夹?
  • @MartinPlante 我根据您的个人资料用“c#”标记了这个问题。如果您希望使用其他语言,请更改它。
  • @Splash-X 您应该将该评论作为答案,因为这正是我想要的。目前,该站点都在根目录中。我将简单地支持“fr/...”和“en/...”的路由。

标签: c# asp.net globalization


【解决方案1】:

URL 路由适用于 ASP.NET。

您可以创建两条路线,第一条是符合您的语言的路线:

{语言}/{页面}

第二条路线只是

{页面}

在 MVC 中,我们可以创建路由约束,强制语言具有特定值(例如 en、en-us 等)如果可以在常规 ASP.NET WebForms 路由中执行相同的操作,我不肯定.

这里有两篇描述WebForms(非MVC)中路由主题的文章

http://msdn.microsoft.com/en-us/magazine/dd347546.aspx

http://weblogs.asp.net/scottgu/archive/2009/10/13/url-routing-with-asp-net-4-web-forms-vs-2010-and-net-4-0-series.aspx

已编辑以添加代码示例

在我的 Global.asax 中,我注册了以下内容:

    void RegisterRoutes(RouteCollection routes)
    {
        routes.Ignore("{resource}.asxd/{*pathInfo}");
        routes.Add(
            new Route(
                "{locale}/{*url}", //Route Path
                null, //Default Route Values
                new RouteValueDictionary{{"locale", "[a-z]{2}"}}, //constraint to say the locale must be 2 letters. You could also use something like "en-us|en-gn|ru" to specify a full list of languages
                 new Utility.Handlers.DefaultRouteHandeler() //Instance of a class to handle the routing
            ));

    }


    void Application_Start(object sender, EventArgs e)
    {
        // Code that runs on application startup
        RegisterRoutes(RouteTable.Routes);

    }

我还创建了一个单独的类(请参阅asp.net 4.0 web forms routing - default/wildcard route 作为指南。)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Compilation;
using System.Web.Routing;
using System.Web.UI;

namespace SampleWeb.Utility.Handlers
{
    public class DefaultRouteHandeler:IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            //Url mapping however you want here: 

            string routeURL = requestContext.RouteData.Values["url"] as string ;

            string pageUrl = "~/" + (!String.IsNullOrEmpty(routeURL)? routeURL:""); 

            var page = BuildManager.CreateInstanceFromVirtualPath(pageUrl, typeof(Page))
                       as IHttpHandler;
            if (page != null)
            {
                //Set the <form>'s postback url to the route 
                var webForm = page as Page;
                if (webForm != null)
                    webForm.Load += delegate
                    {
                        webForm.Form.Action =
                        requestContext.HttpContext.Request.RawUrl;
                    };
            }
            return page;
        }
    }
}

这是可行的,因为当 URL 中没有指定区域设置时,Web 表单的默认视图引擎会接管。当使用 2 个字母的语言环境(en?us?等)时,它也可以工作。在 MVC 中,我们可以使用 IRouteConstraint 并进行各种检查,例如确保语言环境在列表中、检查路径是否存在等,但在 WebForms 中,约束的唯一选择是使用 RouteValueDictonary。

现在,我知道代码原样存在问题,无法加载默认文档。所以http://localhost:25436/en/ 不会加载default.aspx 的默认文档,但是http://localhost:25436/en/default.aspx 确实有效。我会留给你解决。

我用子目录对此进行了测试,它可以工作。

【讨论】:

  • 如何创建将 {language}/{page} 下的所有内容映射到 {page} 的路由?
【解决方案2】:

您可以创建一个调用 HttpContext.RewritePath 的 ASP.NET HTTP 模块,以将请求从“fr/somepage.aspx”映射到“somepage.aspx”。这种技术最适用于集成模式下的 IIS 7.0,因为脚本和样式表的相对 URL 将解析为实际路径,例如“/fr/jquery.js”,并且这些路径也应该映射到“/jquery.js”。

namespace SampleApp
{
    public class LocalizationModule : IHttpModule
    {
        private HashSet<string> _supportedCultures =
            new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "de", "es", "fr" };
        private string _appPath = HttpRuntime.AppDomainAppVirtualPath;

        public void Dispose()
        {
        }

        public void Init(HttpApplication application)
        {
            application.BeginRequest += this.BeginRequest;

            _appPath = HttpRuntime.AppDomainAppVirtualPath;
            if (!_appPath.EndsWith("/"))
                _appPath += "/";
        }

        private void BeginRequest(object sender, EventArgs e)
        {
            HttpContext context = ((HttpApplication)sender).Context;
            string path = context.Request.Path;
            string cultureName = this.GetCultureFromPath(ref path);

            if (cultureName != null)
            {
                Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(cultureName);
                context.RewritePath(path);
            }
        }

        private string GetCultureFromPath(ref string path)
        {
            if (path.StartsWith(_appPath, StringComparison.OrdinalIgnoreCase))
            {
                int startIndex = _appPath.Length;
                int index = path.IndexOf('/', startIndex);

                if (index > startIndex)
                {
                    string cultureName = path.Substring(startIndex, index - startIndex);

                    if (_supportedCultures.Contains(cultureName))
                    {
                        path = _appPath + path.Substring(index + 1);
                        return cultureName;
                    }
                }
            }

            return null;
        }
    }
}

Web.config:

<!-- IIS 7.0 Integrated mode -->
<system.webServer>
    <modules>
        <add name="LocalizationModule" type="SampleApp.LocalizationModule, SampleApp" />
    </modules>
</system.webServer>

<!-- Otherwise -->
<system.web>
    <httpModules>
        <add name="LocalizationModule" type="SampleApp.LocalizationModule, SampleApp" />
    </httpModules>
</system.web>

【讨论】:

    【解决方案3】:

    您可以使用此代码更新 Global.Asax 中的 Application_BeginRequest。如果 global.asax 不存在,则创建它。

    Visual Studio 项目虚拟路径必须是 /

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
    
        string file_path = Request.RawUrl.ToLower();
        char[] separator = new char[] { '/' };
    
        string[] parts = file_path.Split(separator, StringSplitOptions.RemoveEmptyEntries);
    
        if (parts.Length > 0 && parts[0] == "fr")
        {
    
            System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture("fr-FR");
    
            Context.RewritePath("~/" + file_path.Substring(4), true);
        }
        else
        {
            System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture("en-US");
        }
    
    } 
    

    【讨论】:

      【解决方案4】:

      一种选择是将 aspx 的文本放在 &lt;%$ Resources: My translated text %&gt; 标记中。资源标签将使用 ResourceProviderFactory 解析以获取翻译后的值。这个 ResourceProviderFactory 您可以自己创建,例如从资源文件或数据库中获取翻译(只需实现 IResourceProvider.GetObject())。您可以在 web.config 中进行配置:

      <system.web>
        <globalization resourceProviderFactoryType="CustomResourceProviderFactory" uiCulture="fr" culture="en-GB"/>
      </system.web>
      

      见:http://msdn.microsoft.com/en-us/library/fw69ke6f(v=vs.80).aspx

      【讨论】: