locked
Custom Use of Localization Functionality RRS feed

  • Question

  • User748852498 posted

    I am creating an ASP.net application to scrub Legacy data. Part of the functionality requires generic fields (labels and button text) to be in the native language, so I plan on using the standard localization functionality of ASP.net (I have not decided if I am going to use a drop down or automatic detection yet). The other aspect of the application allows for different Legacy Sources to be loaded into the application into generic fields that we have identified in the database. But we want to label the fields based on the Legacy Source selected within the application. And I believe that using a customized localization file can achive this based on what the user selects from a drop down list. Is it possible to utilize two localization resource files on a single page in an ASP.net application?

    -JoeFletch

    Sunday, July 31, 2011 10:05 AM

Answers

  • User-1387801607 posted

    Hi Joe,

    First about caching:
    - Caching is always server-side
    - All users share the same cache, there is no user-specific cache

    I will try to explain how we deal with our resources.
    We have two tables
    - tblLanguage
       - LCID, varchar
       - Description, varchar

    - tblTranslation
       - ID, varchar
       - nl-NL, varchar
       - en-US, varchar

    Where each tblLanguage.ID references to its related column in tblTranslation.

    In code I have defined a Language class:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Text;
    using MySql.Data.MySqlClient;
    using System.Web.Caching;
    
    /// <summary>
    /// Summary description for Language
    /// </summary>
    public class Language
    {
        private const string CACHE_TRANSLATIONS = "Cache_Translations";
            
    
        public string LCID {get; set;}
        public string sDescription { get; set; }
    
        // We use a Dictionary instead of an ObjectList as these are faster //
        public Dictionary<string, string> dicTranslations; 
    
        
        public Language()
    	{
    		// Add constructor logic here
    
            // Get the users prefered Language //
            LCID = (HttpContext.Current.Profile as ProfileCommon).Language;
    
    
            // Determine if this particular TranslationsDictionary is already in Cache //
            if (HttpContext.Current.Cache[CACHE_TRANSLATIONS + LCID] != null)
            {
                // Get the required TranslationsDictionary from Cache //
                dicTranslations = (Dictionary<string, string>)HttpContext.Current.Cache[CACHE_TRANSLATIONS + LCID];
            }
            else
            {
                // Required TranslationsDictionary not yet in Cache //
                
                // Initialize the Translations-Dictionary //
                dicTranslations = new Dictionary<string, string>();
    
                // Get all translations from the DataBase //
                GetTranslationsFromDB(LCID);
    
                // Insert the Dictionary into Cache //
                HttpContext.Current.Cache.Insert(CACHE_TRANSLATIONS + LCID, dicTranslations, null,
                                                DateTime.Now.AddMinutes(30), 
                                                //DateTime.Now.AddSeconds(5),
                                                Cache.NoSlidingExpiration);
            }         
    	}
    
        private void GetTranslationsFromDB(string sALCID)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT * ");
            sb.Append(" FROM tbltranslation");
            
            string sSql = sb.ToString();
    
            using (MySqlConnection conn = DBResources.SportLogConnection())
            {
                MySqlCommand com = new MySqlCommand(sSql, conn);
                //com.Parameters.AddWithValue("@pmLCID", sALCID);            
    
                com.Connection.Open();
                MySqlDataReader dr = com.ExecuteReader();
    
                while (dr.Read())
                {
                    string sTranslationID = dr["id"].ToString();
                    string sValue = (dr[sALCID] is DBNull ? "" : dr[sALCID].ToString());
    
                    // Add each translation to the dictionary //
                    dicTranslations.Add(sTranslationID, sValue);
                }
            }
        }
    
        public static List<Language> GetAllLanguages()
        {
            List<Language> lstLanguage = new List<Language>();
    
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT * ");
            sb.Append(" FROM tbllanguage ");        
            sb.Append(" ORDER BY description");
            string sSql = sb.ToString();
    
            using (MySqlConnection conn = DBResources.SportLogConnection())
            {
                MySqlCommand com = new MySqlCommand(sSql, conn);
                com.Connection.Open();
                MySqlDataReader dr = com.ExecuteReader();
    
                while (dr.Read())
                {
                    Language lan = new Language();
    
                    lan.LCID = dr["lcid"].ToString();
                    lan.sDescription = dr["description"].ToString();
    
                    lstLanguage.Add(lan);
                }
            }
    
            return lstLanguage;
        }
        public string Translation(string sATranslationID)
        {
            string sResult;
            dicTranslations.TryGetValue(sATranslationID, out sResult);
    
            if ( (sResult == null) || (sResult == ""))
                 sResult = sATranslationID;
    
            return sResult;
        }
    
        
    }
    

     

    Note: as you can see this class only uses 1 language/resource per user at a time. You have to change it so it will have both your Language-resource and Source-resource. (You can either make a second class for the second resource type or expand the above.)

    In a page you can use this Language class as follows:

     

    private void LoadLanguage()
        {
            // Create Language-object //
            language = new Language();
    
            lblDisplayLanguage.Text = language.Translation("lblDisplayLanguage.Text");
    }

    .

    As you can see, the ID of each translation-entry in tblTranslation corresponds with the [ControlID].[PropertyID]

    In the above example, each language will be available in chache after its first use and stay there for the given amount of time. So if you have only two languages, there are only two objects in cache. These two objects are available for all your users.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, August 26, 2011 2:45 PM
  • User748852498 posted

    Hy JoeFletch,
    How did my example worked out for you? Could you please ‘mark as answer’ if it was helpful?
    Thanks!

    Thanks! Your post was quite helpful. I guess I really struggled with how the cache worked. Combining a translation/source ID with the actually source/Language Key was what cleared it up for me. For example, 1EN relates to ID 1 in the database with a Language Key of EN. I can also have 1NL in the cache and any user can retrieve these cached values. I did change the code quite a bit though.

        Public Function GetText(ByVal ID As Integer, ByVal Key As String) As String
            If Cache(ID.ToString & Key) Is Nothing Then
                Cache.Insert(ID.ToString & Key, LanguageSourceTextBLL.GetTextDataByIDLangaugeSource(ID, Key), Nothing, DateTime.Now.AddMinutes(3), TimeSpan.Zero)
                PageStatusLabel.Text = PageStatusLabel.Text & "<br />Pulled " & ID.ToString & Key & " from the database and placed it in the cache."
            Else
                PageStatusLabel.Text = PageStatusLabel.Text & "<br />Pulled " & ID.ToString & Key & " from cache."
            End If
    
            Return Cache(ID.ToString & Key)
        End Function

    I will be pulling out the PageStateLabel.Text entries and adding time to the cache expiration.

    But I still have one problem. I can not call this function on an aspx page within an asp.net control. Examples...

    <asp:Label ID="Label1" runat="server" Text='<% =GetText(1,"EN") %>' ></asp:Label>

    <asp:Label ID="Label1" runat="server" Text='<%# GetText(1,"EN") %>' ></asp:Label>

    But the following works...

    <% =GetText(1,"EN") %>

    -JoeFletch

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, September 6, 2011 3:05 PM
  • User-1387801607 posted

    Let me first say, I am not a VB guy so I do not know if all the notations are correct.

    But what I do see is the following:

    Return Cache(ID.ToString & Key)

    Every item coming from Cache is of type Object. Your method dough, returns a String.

    When you retrieve an item from Cache, convert it to String first before returning it:

    Return Cache(ID.ToString & Key).ToString()

    Something else, before adding VB code to your markup, try retrieving entries from cache in code-behind first.

    Dim sEntry as String
    
    sEntry = Cache(ID.ToString() & Key).ToString()
    
    Label1.Text = sEntry

    This will give you the posibility to step through all lines.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, September 6, 2011 3:58 PM

All replies

  • User1519636740 posted

    hi JoeFletch,

    What is the exact scenerio like?

    Thursday, August 4, 2011 3:18 AM
  • User748852498 posted

    Within my application I have different labels that need to be changed based on either a Language or a Source selected. Both selections will need to be made on a particular page and they will drive labels to be populated.

    When a Language is selected, I want the following objects to be updated...

    • Specific Description/Header Labels
    • Warning/Error/Validation messages/labels
    • Button text
    On the same page, after a Language is selected, a Source will be selected. I want the following objects to be updated based on a Sourceselected...
    • All Grid Header fields
    • Search field labels
    I have been reading about using localization/resource files, but I have not seen any literature about using two different non-Global resource files at the same time on the same page. Is this possible? Can I use a resource file to populate fields based on a custom resource file name? For example: Default.aspx.CustomName.resx?
    Thanks in advance for the input and response!
    -JoeFletch
    Thursday, August 4, 2011 7:00 AM
  • User748852498 posted

    Here is a crude picture of what I want.

    -JoeFletch

    Thursday, August 4, 2011 7:05 AM
  • User748852498 posted

    I'm wondering if localization / resource files are not the best option here. Would maybe caching from either an XML file or from SQL server work better?

    -JoeFletch

    Friday, August 26, 2011 8:18 AM
  • User-1387801607 posted

    Hi JoeFletch,

    As far as I know you can not use multiple (global or non-global) resource files at the same time at the same page.

    As you wrote in your last post, best seems to be caching both your resources from XML or DB. That is how we do it.

    Good luck!

    Friday, August 26, 2011 12:39 PM
  • User748852498 posted

    caching both your resources from XML or DB

    Does caching to an arrary at page load seem reasonable? I think that is what I am going to try now. My only concern is multiple users with different Languages and Sources selected. Is the caching on the client side or the server side? I can't see it being on the client side, then how would the aspx page be generated? If it is on the server, then does asp.net just cache specific to a user?

    -JoeFletch

    Friday, August 26, 2011 1:43 PM
  • User-1387801607 posted

    Hi Joe,

    First about caching:
    - Caching is always server-side
    - All users share the same cache, there is no user-specific cache

    I will try to explain how we deal with our resources.
    We have two tables
    - tblLanguage
       - LCID, varchar
       - Description, varchar

    - tblTranslation
       - ID, varchar
       - nl-NL, varchar
       - en-US, varchar

    Where each tblLanguage.ID references to its related column in tblTranslation.

    In code I have defined a Language class:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Text;
    using MySql.Data.MySqlClient;
    using System.Web.Caching;
    
    /// <summary>
    /// Summary description for Language
    /// </summary>
    public class Language
    {
        private const string CACHE_TRANSLATIONS = "Cache_Translations";
            
    
        public string LCID {get; set;}
        public string sDescription { get; set; }
    
        // We use a Dictionary instead of an ObjectList as these are faster //
        public Dictionary<string, string> dicTranslations; 
    
        
        public Language()
    	{
    		// Add constructor logic here
    
            // Get the users prefered Language //
            LCID = (HttpContext.Current.Profile as ProfileCommon).Language;
    
    
            // Determine if this particular TranslationsDictionary is already in Cache //
            if (HttpContext.Current.Cache[CACHE_TRANSLATIONS + LCID] != null)
            {
                // Get the required TranslationsDictionary from Cache //
                dicTranslations = (Dictionary<string, string>)HttpContext.Current.Cache[CACHE_TRANSLATIONS + LCID];
            }
            else
            {
                // Required TranslationsDictionary not yet in Cache //
                
                // Initialize the Translations-Dictionary //
                dicTranslations = new Dictionary<string, string>();
    
                // Get all translations from the DataBase //
                GetTranslationsFromDB(LCID);
    
                // Insert the Dictionary into Cache //
                HttpContext.Current.Cache.Insert(CACHE_TRANSLATIONS + LCID, dicTranslations, null,
                                                DateTime.Now.AddMinutes(30), 
                                                //DateTime.Now.AddSeconds(5),
                                                Cache.NoSlidingExpiration);
            }         
    	}
    
        private void GetTranslationsFromDB(string sALCID)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT * ");
            sb.Append(" FROM tbltranslation");
            
            string sSql = sb.ToString();
    
            using (MySqlConnection conn = DBResources.SportLogConnection())
            {
                MySqlCommand com = new MySqlCommand(sSql, conn);
                //com.Parameters.AddWithValue("@pmLCID", sALCID);            
    
                com.Connection.Open();
                MySqlDataReader dr = com.ExecuteReader();
    
                while (dr.Read())
                {
                    string sTranslationID = dr["id"].ToString();
                    string sValue = (dr[sALCID] is DBNull ? "" : dr[sALCID].ToString());
    
                    // Add each translation to the dictionary //
                    dicTranslations.Add(sTranslationID, sValue);
                }
            }
        }
    
        public static List<Language> GetAllLanguages()
        {
            List<Language> lstLanguage = new List<Language>();
    
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT * ");
            sb.Append(" FROM tbllanguage ");        
            sb.Append(" ORDER BY description");
            string sSql = sb.ToString();
    
            using (MySqlConnection conn = DBResources.SportLogConnection())
            {
                MySqlCommand com = new MySqlCommand(sSql, conn);
                com.Connection.Open();
                MySqlDataReader dr = com.ExecuteReader();
    
                while (dr.Read())
                {
                    Language lan = new Language();
    
                    lan.LCID = dr["lcid"].ToString();
                    lan.sDescription = dr["description"].ToString();
    
                    lstLanguage.Add(lan);
                }
            }
    
            return lstLanguage;
        }
        public string Translation(string sATranslationID)
        {
            string sResult;
            dicTranslations.TryGetValue(sATranslationID, out sResult);
    
            if ( (sResult == null) || (sResult == ""))
                 sResult = sATranslationID;
    
            return sResult;
        }
    
        
    }
    

     

    Note: as you can see this class only uses 1 language/resource per user at a time. You have to change it so it will have both your Language-resource and Source-resource. (You can either make a second class for the second resource type or expand the above.)

    In a page you can use this Language class as follows:

     

    private void LoadLanguage()
        {
            // Create Language-object //
            language = new Language();
    
            lblDisplayLanguage.Text = language.Translation("lblDisplayLanguage.Text");
    }

    .

    As you can see, the ID of each translation-entry in tblTranslation corresponds with the [ControlID].[PropertyID]

    In the above example, each language will be available in chache after its first use and stay there for the given amount of time. So if you have only two languages, there are only two objects in cache. These two objects are available for all your users.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, August 26, 2011 2:45 PM
  • User748852498 posted

    Excellent. Thanks for the detailed response and your suggestions. I will try it and get back to the thread.

    -JoeFletch

    Friday, August 26, 2011 3:09 PM
  • User-1387801607 posted

    Hy JoeFletch,
    How did my example worked out for you? Could you please ‘mark as answer’ if it was helpful?
    Thanks!

     

    Tuesday, September 6, 2011 6:28 AM
  • User748852498 posted

    Hy JoeFletch,
    How did my example worked out for you? Could you please ‘mark as answer’ if it was helpful?
    Thanks!

     

    Sorry, its been a rough couple of weeks (hurricane damage, household sicknesses, etc). I hope to get to it today.

    -JoeFletch

    Tuesday, September 6, 2011 8:25 AM
  • User748852498 posted

    Hy JoeFletch,
    How did my example worked out for you? Could you please ‘mark as answer’ if it was helpful?
    Thanks!

    Thanks! Your post was quite helpful. I guess I really struggled with how the cache worked. Combining a translation/source ID with the actually source/Language Key was what cleared it up for me. For example, 1EN relates to ID 1 in the database with a Language Key of EN. I can also have 1NL in the cache and any user can retrieve these cached values. I did change the code quite a bit though.

        Public Function GetText(ByVal ID As Integer, ByVal Key As String) As String
            If Cache(ID.ToString & Key) Is Nothing Then
                Cache.Insert(ID.ToString & Key, LanguageSourceTextBLL.GetTextDataByIDLangaugeSource(ID, Key), Nothing, DateTime.Now.AddMinutes(3), TimeSpan.Zero)
                PageStatusLabel.Text = PageStatusLabel.Text & "<br />Pulled " & ID.ToString & Key & " from the database and placed it in the cache."
            Else
                PageStatusLabel.Text = PageStatusLabel.Text & "<br />Pulled " & ID.ToString & Key & " from cache."
            End If
    
            Return Cache(ID.ToString & Key)
        End Function

    I will be pulling out the PageStateLabel.Text entries and adding time to the cache expiration.

    But I still have one problem. I can not call this function on an aspx page within an asp.net control. Examples...

    <asp:Label ID="Label1" runat="server" Text='<% =GetText(1,"EN") %>' ></asp:Label>

    <asp:Label ID="Label1" runat="server" Text='<%# GetText(1,"EN") %>' ></asp:Label>

    But the following works...

    <% =GetText(1,"EN") %>

    -JoeFletch

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, September 6, 2011 3:05 PM
  • User-1387801607 posted

    Let me first say, I am not a VB guy so I do not know if all the notations are correct.

    But what I do see is the following:

    Return Cache(ID.ToString & Key)

    Every item coming from Cache is of type Object. Your method dough, returns a String.

    When you retrieve an item from Cache, convert it to String first before returning it:

    Return Cache(ID.ToString & Key).ToString()

    Something else, before adding VB code to your markup, try retrieving entries from cache in code-behind first.

    Dim sEntry as String
    
    sEntry = Cache(ID.ToString() & Key).ToString()
    
    Label1.Text = sEntry

    This will give you the posibility to step through all lines.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, September 6, 2011 3:58 PM
  • User748852498 posted

    Return Cache(ID.ToString & Key).ToString()

    I have made this update. Thanks.

    This will give you the posibility to step through all lines.

    That is why I use a status label, so see each step along the way. The strings are being retrieved and stored in the cache. I am just having some trouble placing the retrieval code into the asp.net controls.

    -JoeFletch

    Tuesday, September 6, 2011 4:30 PM