1
Vote

Polyglot - extension for multilingual version of pages

description

Hi,
we (Va&Cu) wrote little extension, which allows relatively easily create multilingual version of your web pages based on MWPSK.
The idea is simple - sections read and save data via specialized functions, which determine the correct language to be used and return or save true value for this language.
 
Live example of this extension you can see on our website www.vacu.cz and there is more information about this.
 
Enjoy. Va&Cu

file attachments

comments

vacu wrote Jun 18, 2009 at 11:23 AM

The reference to home page of this extension is not rendered as link, what to try the full url? http://www.vacu.cz

pappas79 wrote Jun 19, 2009 at 8:01 AM

Excellent but not SEO Friendly. Because the same page is in CZ, EL, EN. We have to distinguish those pages with url redirection and with a new sitemap output for the engines. The best way to do this seems to be the one which uses url rewriting which contains globalization info.
The first step is to do the mods of the polyglot20090317.zip, define a firstlevel virtual path for every page and additionally:

Modify Global.asax:
// because url rewriting is combined with forms authentication, the rewriting must take place in Application_AuthorizeRequest, so that User.Identity is available
private void Application_AuthorizeRequest(object sender, EventArgs e)
{
string currentURL = Request.Path.ToLower();

if (currentURL.EndsWith(".aspx")) {
    //checks to see if the file does not exsists.
    if (!System.IO.File.Exists(Server.MapPath(currentURL))) {
        string queryString = Request.ServerVariables("QUERY_STRING");
        string defaultPage = Request.ApplicationPath + "/default.aspx?pg=";


        string MyLocale = MyWebPagesStarterKit.WebSite.GetInstance().LocaleID;

        Configuration config = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration(System.Web.HttpContext.Current.Request.ApplicationPath);
        System.Configuration.ConfigurationSection section = config.GetSection("supportedLanguages");
        System.Xml.XmlDocument XMLDoc = new System.Xml.XmlDocument();
        XMLDoc.LoadXml(section.SectionInformation.GetRawXml());
        string[] languages = XMLDoc.FirstChild.Attributes("languages").Value.Split(new char[] { char.Parse(",") });
        foreach (string language in languages) {
            foreach (System.Globalization.CultureInfo specific in System.Globalization.CultureInfo.GetCultures(System.Globalization.CultureTypes.SpecificCultures)) {
                if (specific.TwoLetterISOLanguageName == language) {
                    if (currentURL.ToLower().Contains(specific.Name.ToLower() + "/")) {
                        currentURL = Strings.Replace(currentURL, specific.Name + "/", "", 1, -1, 1);
                        MyLocale = specific.Name;
                    }
                }
            }
        }

        string pageId = null;
        foreach (SiteMapNode node in SiteMap.RootNode.GetAllNodes()) {

            if (currentURL.contains(node.Url.ToLower()) | node.Url.ToLower().contains(currentURL)) {
                pageId = node("pageId");
                break; // TODO: might not be correct. Was : Exit For
            }
        }
        if (pageId == null) {
            pageId = MyWebPagesStarterKit.WebSite.GetInstance().HomepageId;
        }
        // Rewrites the path
        if (queryString != string.Empty) {
            Context.RewritePath((defaultPage + pageId + "&") + queryString, false);
        }
        else {
            Context.RewritePath(defaultPage + pageId, false);
        }

    }
}
}

AND the CustomSiteMapData.cs:
public class CustomSiteMapDataSource : SiteMapDataSource
{
    public CustomSiteMapDataSource() { }

    protected override HierarchicalDataSourceView GetHierarchicalView(string viewPath)
    {
    SiteMapNode root = Provider.RootNode;
        SiteMapNode newRoot = root.Clone();
    string currentURL = Page.Request.RawUrl.ToLower();
        string curLocale = System.Threading.Thread.CurrentThread.CurrentCulture.Name;
        //if (!currentURL.Contains(curLocale.ToLower() + "/")) { curLocale = ""; }
        SiteMapNodeCollection col = new SiteMapNodeCollection();
        foreach (SiteMapNode node in newRoot.ChildNodes){
    if (!node.Url.ToLower().Contains("administration/")){
     node.ReadOnly=false;
         node.Url = "/"+curLocale + node.Url.Substring(node.Url.LastIndexOf("/"));
     if(node.Url.Contains("//")){node.Url=curLocale + node.Url.Substring(node.Url.LastIndexOf("/"));}
     node.ReadOnly=true;
    }
            col.AddRange(RecursiveNodesCheck(node));
        }

    newRoot = null;
        root = null;
        return new SiteMapHierarchicalDataSourceView(col);
    }

 private SiteMapNodeCollection RecursiveNodesCheck(SiteMapNode Node)
    {
        if (Node.ChildNodes.Count == 0)
        {
            if (Node["VisibleInMenu"] == null)
                return (new SiteMapNodeCollection(Node));
            if (Node["VisibleInMenu"].ToLower() == "true" || Roles.IsUserInRole("Administrators"))
                return (new SiteMapNodeCollection(Node));
            else
                return (new SiteMapNodeCollection());
        }
        SiteMapNodeCollection OutputCollection = new SiteMapNodeCollection();
    SiteMapNode NewNode = Node.Clone();
        SiteMapNodeCollection ChildrenCollection = new SiteMapNodeCollection();
    string currentURL = Page.Request.RawUrl.ToLower();
        string curLocale = System.Threading.Thread.CurrentThread.CurrentCulture.Name;
        //if (!currentURL.Contains(curLocale.ToLower() + "/")) { curLocale = ""; }
        foreach (SiteMapNode ChildNode in Node.ChildNodes)
        {
    if (!ChildNode.Url.ToLower().Contains("administration/")){
     ChildNode.ReadOnly=false;
         ChildNode.Url = "/"+curLocale + ChildNode.Url.Substring(ChildNode.Url.LastIndexOf("/"));
     if(ChildNode.Url.Contains("//")){ChildNode.Url=curLocale + ChildNode.Url.Substring(ChildNode.Url.LastIndexOf("/"));}
     ChildNode.ReadOnly=true;
    }
            ChildrenCollection.AddRange(RecursiveNodesCheck(ChildNode));
        }
        NewNode.ChildNodes = ChildrenCollection;
        OutputCollection.Add(NewNode);
        return (OutputCollection);
    }
}
Plus you have to forget sitemap.axd, programmatically read the XML web.sitemap and create a new sitemap output for the engines adding in url for every page all the available language-cultrure code info. A usefull feature would be the ability to hide from menu a page which does not exist in a specific language. Maybe we could use your library to have multiple visible in menu for the same page in different languages. Another approach would be to ignore from menu pages which have not a title defined for a language.

pappas79 wrote Jun 29, 2009 at 9:04 AM

Yes indeed if you try for instance: http://www.vacu.cz you get the website in the default language but when you try:
http://www.vacu.cz/en-US/ you get 404. The correct is to use http://www.vacu.cz/en-US/homepage.aspx or http://www.vacu.cz/cz-CZ/homepage.aspx

rickardmagnusson wrote Sep 20, 2009 at 1:40 PM

Hi Va&Cu
I think your idea is brilliant. Like in a MWPSK manner you created a nice and functional extension.
I can agree that SEO produced pages need more but overall you thougt of most of the things and will work in most small websites.
One thing that came up was that the Sections should show (I think) the default cultured sections value in stead of (not translated) . See code below.
Also the default culture should be set in Website settings not in web.config. Like CurrentLang = WebSite.GetInstance().DefaultLocaleID
Really nice work. I will really like to here more from you and your projects.

public static string GetLocalized(string MultilangValue)
    {
        //nactu MultilangValue do tabulky
        DataTable table = GetMultilangTable(MultilangValue);

        if (table.Rows.Count == 0)
            return MultilangValue;

        //najdu zaznam(y) pro aktualni jazyk
        DataRow[] rows = table.Select(string.Format("[Lang] = '{0}'", CurrentLang));
        if (rows.Length > 0)
        {
            return rows[0]["Value"] as string;
        }
        else if (CurrentLang != WebSite.GetInstance().DefaultLocaleID)
        {
            rows = table.Select(string.Format("[Lang] = '{0}'", WebSite.GetInstance().DefaultLocaleID));
            return rows[0]["Value"] as string;
        }
        else
        {
            return "(not translated)"; //Will never fire
        }
    }

sallyharts wrote Dec 21, 2009 at 7:56 AM

Thanks!.. I've been a little confused about the whole thing for a long time now. I've read some <a href="http://www.rushessay.com">essays> from other sites but I never really understood them that well. This really helped a lot. Thanks again.

ldusit wrote Dec 18, 2010 at 5:29 PM

Hi VaCu,

I tried your code with MWPSK 1.3.0 and 1.3.1 but it didn't work.

In administration/CMS setup, when I changed language from combo box, it worked. However, when I click from the link, it didn't work. The web did not change but there was reload.

Could you help me?

Thank you very much in advance.

ldusit wrote Dec 19, 2010 at 1:11 AM

Hi VaCu,

I tried your code with MWPSK 1.3.0 and 1.3.1 but it didn't work.

In administration/CMS setup, when I changed language from combo box, it worked. However, when I click from the link, it didn't work. The web did not change but there was reload.

Could you help me?

Thank you very much in advance.

vacu wrote Dec 19, 2010 at 7:37 AM

Hi Idusit.
I have bad news. "Polyglot" extension was created for version 1.2.1 MWPSK, and is no longer maintained. With the release of ASP.NET MVC, MWPSK I left. You have to customize the code yourself or find someone who will do it for you. I have no free time. Sorry.

Varel (Va from Va&Cu)