Exemplo n.º 1
0
 public ActionResult SetLanguage(string langtag, string returnUrl)
 {
     // If valid 'langtag' passed.
     i18n.LanguageTag lt = i18n.LanguageTag.GetCachedInstance(langtag);
     if (lt.IsValid())
     {
         // Set persistent cookie in the client to remember the language choice.
         Response.Cookies.Add(new System.Web.HttpCookie("i18n.langtag")
         {
             Value    = lt.ToString(),
             HttpOnly = true,
             Expires  = DateTime.UtcNow.AddYears(1)
         });
     }
     // Owise...delete any 'language' cookie in the client.
     else
     {
         var cookie = Response.Cookies["i18n.langtag"];
         if (cookie != null)
         {
             cookie.Value   = null;
             cookie.Expires = DateTime.UtcNow.AddMonths(-1);
         }
     }
     // Update PAL setting so that new language is reflected in any URL patched in the
     // response (Late URL Localization).
     HttpContext.SetPrincipalAppLanguageForRequest(lt);
     // Patch in the new langtag into any return URL.
     if (returnUrl.IsSet())
     {
         returnUrl = LocalizedApplication.Current.UrlLocalizerForApp.SetLangTagInUrlPath(HttpContext, returnUrl, UriKind.RelativeOrAbsolute, lt == null ? null : lt.ToString()).ToString();
     }
     //Redirect user agent as approp.
     return(this.Redirect(returnUrl));
 }
Exemplo n.º 2
0
        private void Application_SetAppLangulage(object sender, EventArgs e)
        {
            var langCookie = HttpContext.Current.Request.Cookies["Mcd_AM.LangTag"];

            if (langCookie != null)
            {
                var langtag = langCookie.Value;

                i18n.LanguageTag lt = i18n.LanguageTag.GetCachedInstance(langtag);
                if (lt.IsValid())
                {
                    // Set persistent cookie in the client to remember the language choice.
                    HttpContext.Current.Response.Cookies.Add(new HttpCookie("Mcd_AM.LangTag")
                    {
                        Value    = lt.ToString(),
                        HttpOnly = true,
                        Expires  = DateTime.UtcNow.AddYears(1)
                    });
                }
                // Owise...delete any 'language' cookie in the client.
                else
                {
                    var cookie = HttpContext.Current.Response.Cookies["Mcd_AM.LangTag"];
                    if (cookie != null)
                    {
                        cookie.Value   = null;
                        cookie.Expires = DateTime.UtcNow.AddMonths(-1);
                    }
                }
                // Update PAL setting so that new language is reflected in any URL patched in the
                // response (Late URL Localization).
                HttpContext.Current.SetPrincipalAppLanguageForRequest(lt);
            }
        }
Exemplo n.º 3
0
        public static LanguageTag GetMatchingAppLanguage(LanguageItem[] languages, int maxPasses = -1)
        {
            LanguageTag lt = null;

            LocalizedApplication.Current.TextLocalizerForApp.GetText(null, null, languages, out lt, maxPasses);
            return(lt);
        }
Exemplo n.º 4
0
        public string SetLangTagInUrlPath(System.Web.HttpContextBase context, string url, UriKind uriKind, string langtag)
        {
            switch (UrlLocalizationScheme)
            {
            case UrlLocalizationScheme.Scheme2:
            {
                if (DetermineDefaultLanguageFromRequest(context).Equals(langtag))
                {
                    return(url);
                }
                break;
            }

            case UrlLocalizationScheme.Scheme3:
            {
                throw new InvalidOperationException();
            }
            }

            string siteRootPath = ExtractAnySiteRootPathFromUrl(ref url, uriKind);

            url = LanguageTag.SetLangTagInUrlPath(url, uriKind, langtag);

            // If site root path was trimmed from the URL above, add it back on now.
            if (siteRootPath != null)
            {
                PatchSiteRootPathIntoUrl(siteRootPath, ref url, uriKind);
            }

            return(url);
        }
Exemplo n.º 5
0
 protected void Application_BeginRequest(object source, EventArgs e)
 {
     i18n.LanguageTag lt = i18n.LanguageTag.GetCachedInstance("en");
     if (lt.IsValid())
     {
         Response.Cookies.Add(new HttpCookie("i18n.langtag")
         {
             Value    = lt.ToString(),
             HttpOnly = true,
             Expires  = DateTime.UtcNow.AddYears(1)
         });
     }
     else
     {
         var cookie = Response.Cookies["i18n.langtag"];
         if (cookie != null)
         {
             cookie.Value   = null;
             cookie.Expires = DateTime.UtcNow.AddMonths(-1);
         }
     }
     if (HttpContext.Current.Request.RawUrl.ToLower().Contains("content") || HttpContext.Current.Request.RawUrl.ToLower().Contains("style"))
     {
         HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
     }
     HttpContext.Current.SetPrincipalAppLanguageForRequest(lt);
 }
Exemplo n.º 6
0
        /// <summary>
        /// Helper for instantiating a LanguageItem array from a compact string form
        /// previously returned by <see cref="DehydrateLanguageItemsToString"/>.
        /// </summary>
        /// <param name="strLanguageItems">
        /// Compact string representation of a language item array.
        /// May be null/empty string in which case a a single-item language item array
        /// representing a null PAL is returned.
        /// Example values:
        ///     "fr-CA;q=1,fr;q=0.5"
        ///     "en-CA;q=2,de;q=0.5,en;q=1,fr-FR;q=0,ga;q=0.5"
        ///     "en-CA;q=1,de;q=0.5,en;q=1,fr-FR;q=0,ga;q=0.5"
        ///     "en-CA;q=1"
        ///     "?;q=2"
        ///     "?;q=2,de;q=0.5,en;q=1,fr-FR;q=0,ga;q=0.5"
        ///     ""
        /// </param>
        /// <returns>A language item array e.g. a UserLanguages value.</returns>
        /// <exception cref="ArgumentException">strLanguageItems is invalid</exception>
        public static LanguageItem[] HydrateLanguageItemsFromString(string strLanguageItems)
        {
            int         pos;
            LanguageTag pal = null;

            if (strLanguageItems.IsSet())
            {
                // Read PAL from the front of the compact string.
                pos = strLanguageItems.IndexOf(';');
                if (pos == -1)
                {
                    throw new ArgumentException("strLanguageItems is invalid");
                }
                string strPal = strLanguageItems.Substring(0, pos);
                if (strPal != "?")
                {
                    pal = new LanguageTag(strPal);
                }
                // Strip off the PAL from the front of the compact string.
                pos = strLanguageItems.IndexOf(',');
                if (pos != -1)
                {
                    strLanguageItems = strLanguageItems.Substring(pos + 1);
                }
                else
                {
                    strLanguageItems = "";
                }
            }
            //
            return(ParseHttpLanguageHeader(strLanguageItems, pal));
        }
Exemplo n.º 7
0
        public ActionResult SetLanguage(string langtag, string returnUrl)
        {
            //if valid "langtag" passed
            i18n.LanguageTag lt = i18n.LanguageTag.GetCachedInstance(langtag);
            if (lt.IsValid())
            {
                //Set persistent coockie in the client to remember the language choice.
                Response.Cookies.Add(
                    new HttpCookie("i18n.langtag")
                {
                    Value    = lt.ToString(),
                    HttpOnly = true,
                    Expires  = DateTime.UtcNow.AddYears(1)
                });
            }
            //delete any language coockie in the client.
            else
            {
                var coockie = Response.Cookies["i18n.langtag"];
                if (coockie != null)
                {
                    coockie.Value   = null;
                    coockie.Expires = DateTime.UtcNow.AddMonths(-1);
                }
            }

            System.Web.HttpContext.Current.SetPrincipalAppLanguageForRequest(lt);
            //if(returnUrl.IsSet())
            //{
            //    returnUrl = LocalizedApplication.Current.UrlLocalizerForApp.SetLangTagInUrlPath(HttpContext, returnUrl, UriKind.RelativeOrAbsolute, lt == null ? null : lt.ToString()).ToString();
            //}

            return(Redirect(returnUrl));
        }
Exemplo n.º 8
0
        public ActionResult SetLanguage(string langtag, string returnUrl)
        {
            try
            {
                // If valid 'langtag' passed.
                i18n.LanguageTag lt = i18n.LanguageTag.GetCachedInstance(langtag);
                if (lt.IsValid())
                {
                    WebsiteLanguage = langtag;
                    // Set persistent cookie in the client to remember the language choice.
                    Response.Cookies.Add(new HttpCookie(CommonsConst.Const.i18nlangtag)
                    {
                        Value    = lt.ToString(),
                        HttpOnly = true,
                        Expires  = DateTime.UtcNow.AddYears(1)
                    });

                    if (User.Identity.IsAuthenticated)
                    {
                        _userService.UpdateLanguageUser(langtag, User.Identity.Name);
                        UserSession LoggedUser = UserSession;
                        LoggedUser.LanguageTag = langtag;
                        UserSession            = LoggedUser;
                    }
                }
                // Owise...delete any 'language' cookie in the client.
                else
                {
                    var cookie = Response.Cookies[CommonsConst.Const.i18nlangtag];
                    if (cookie != null)
                    {
                        cookie.Value   = null;
                        cookie.Expires = DateTime.UtcNow.AddMonths(-1);
                    }
                }
                // Update PAL setting so that new language is reflected in any URL patched in the
                // response (Late URL Localization).
                System.Web.HttpContext.Current.SetPrincipalAppLanguageForRequest(lt);
                // Patch in the new langtag into any return URL.
                if (!string.IsNullOrEmpty(returnUrl))
                {
                    returnUrl = LocalizedApplication.Current.UrlLocalizerForApp.SetLangTagInUrlPath(HttpContext, returnUrl, UriKind.RelativeOrAbsolute, lt == null ? null : lt.ToString()).ToString();
                    returnUrl = returnUrl.Replace(lt.ToString() + "?", lt.ToString() + "/Home?");
                }
                // Redirect user agent as approp.
            }
            catch (Exception e)
            {
                Commons.Logger.GenerateError(e, System.Reflection.MethodBase.GetCurrentMethod().DeclaringType, "Lanuguage = " + langtag + " and url = " + returnUrl);
            }
            if (returnUrl != "")
            {
                return(this.Redirect(returnUrl));
            }
            else
            {
                return(null);
            }
        }
Exemplo n.º 9
0
        private static string GetCacheKey(string langtag)
        {
            //return string.Format("po:{0}", langtag).ToLowerInvariant();
            // The above will cause a new string to be allocated.
            // So subsituted with the following code.

            // Obtain the cache key without allocating a new string (except for first time).
            // As this method is a high-frequency method, the overhead in the (lock-free) dictionary
            // lookup is thought to outweigh the potentially large number of temporary string allocations
            // and the consequently hastened garbage collections.
            return(LanguageTag.GetCachedInstance(langtag).GlobalKey);
        }
Exemplo n.º 10
0
        public EmployeeSession afterLoginInitial(string access_token, dynamic access_result)
        {
            EmployeeSession empSession = EmployeeSession.LoadByJsonString(HttpContext.Current.Session["empSession"].ToString());

            empSession.accessToken = access_token;
            if (access_result.CompanyId != null)
            {
                empSession.companyId = access_result.CompanyId;
            }
            empSession.photoURL  = access_result.PhotoURL;
            empSession.id        = access_result.Id;
            empSession.firstName = access_result.FirstName;
            empSession.lastName  = access_result.LastName;
            empSession.email     = access_result.Email;
            if (access_result.AdminFlag != null)
            {
                empSession.adminFlag = bool.Parse((string)access_result.AdminFlag);
            }
            else
            {
                empSession.adminFlag = false;
            }
            empSession.issued         = access_result.issued;
            empSession.expires        = access_result.expires;
            empSession.employeeNumber = access_result.EmployeeNumber;
            if (access_result.Lang != null)
            {
                empSession.Lang = access_result.Lang;
            }
            else
            {
                empSession.Lang = "en";
            }
            i18n.LanguageTag langTag = i18n.LanguageTag.GetCachedInstance(empSession.Lang);
            System.Web.HttpContext.Current.SetPrincipalAppLanguageForRequest(langTag);

            HttpContext.Current.Session["empSession"] = empSession.Serialize();

            StringBuilder logMessage = new StringBuilder();

            logMessage.AppendLine("audit: User Login Successful.");
            logMessage.AppendLine("email:" + empSession.email);
            Global._sfAuditLogger.Audit(logMessage);

            return(empSession);
        }
Exemplo n.º 11
0
        public virtual ConcurrentDictionary <string, LanguageTag> GetAppLanguages()
        {
            ConcurrentDictionary <string, LanguageTag> AppLanguages = (ConcurrentDictionary <string, LanguageTag>)System.Web.HttpRuntime.Cache["i18n.AppLanguages"];

            if (AppLanguages != null)
            {
                return(AppLanguages);
            }
            lock (Sync)
            {
                AppLanguages = (ConcurrentDictionary <string, LanguageTag>)System.Web.HttpRuntime.Cache["i18n.AppLanguages"];
                if (AppLanguages != null)
                {
                    return(AppLanguages);
                }
                AppLanguages = new ConcurrentDictionary <string, LanguageTag>();

                // Insert into cache.
                // NB: we do this before actually populating the collection. This is so that any changes to the
                // folders before we finish populating the collection will cause the cache item to be invalidated
                // and hence reloaded on next request, and so will not be missed.
                System.Web.HttpRuntime.Cache.Insert("i18n.AppLanguages", AppLanguages, _translationRepository.GetCacheDependencyForAllLanguages());

                // Populate the collection.
                List <string> languages = _translationRepository.GetAvailableLanguages().Select(x => x.LanguageShortTag).ToList();

                // Ensure default language is included in AppLanguages where appropriate.
                if (LocalizedApplication.Current.MessageKeyIsValueInDefaultLanguage &&
                    !languages.Any(x => LocalizedApplication.Current.DefaultLanguageTag.Equals(x)))
                {
                    languages.Add(LocalizedApplication.Current.DefaultLanguageTag.ToString());
                }

                foreach (var langtag in languages)
                {
                    if (IsLanguageValid(langtag))
                    {
                        AppLanguages[langtag] = LanguageTag.GetCachedInstance(langtag);
                    }
                }

                // Done.
                return(AppLanguages);
            }
        }
Exemplo n.º 12
0
        public string ExtractLangTagFromUrl(System.Web.HttpContextBase context, string url, UriKind uriKind, bool incomingUrl, out string urlPatched)
        {
            string siteRootPath = ExtractAnySiteRootPathFromUrl(ref url, uriKind);

            string result = LanguageTag.ExtractLangTagFromUrl(url, uriKind, out urlPatched);

            switch (UrlLocalizationScheme)
            {
            case UrlLocalizationScheme.Scheme1:
            {
                break;
            }

            case UrlLocalizationScheme.Scheme2:
            {
                // If the URL is nonlocalized incoming URL, this implies default language.
                if (result == null && incomingUrl)
                {
                    result     = DetermineDefaultLanguageFromRequest(context).ToString();
                    urlPatched = url;
                }
                break;
            }

            case UrlLocalizationScheme.Scheme3:
            default:
            {
                throw new InvalidOperationException();
            }
            }

            // If site root path was trimmed from the URL above, add it back on now.
            if (siteRootPath != null &&
                urlPatched != null)
            {
                PatchSiteRootPathIntoUrl(siteRootPath, ref urlPatched, uriKind);
            }

            return(result);
        }
Exemplo n.º 13
0
        public static LanguageTag GetInferredLanguage(this HttpContextBase context)
        {
            // langtag = best match between
            // 1. Inferred user languages (cookie and Accept-Language header)
            // 2. App Languages.
            LanguageTag lt             = null;
            HttpCookie  cookie_langtag = context.Request.Cookies.Get("i18n.langtag");

            if (cookie_langtag != null)
            {
                lt = LanguageHelpers.GetMatchingAppLanguage(cookie_langtag.Value);
            }
            if (lt == null)
            {
                lt = LanguageHelpers.GetMatchingAppLanguage(context.GetRequestUserLanguages());
            }
            if (lt == null)
            {
                throw new InvalidOperationException("Expected GetRequestUserLanguages to fall back to default language.");
            }
            return(lt);
        }
Exemplo n.º 14
0
        /// <summary>
        /// Implements the Early Url Localization logic.
        /// <see href="https://docs.google.com/drawings/d/1cH3_PRAFHDz7N41l8Uz7hOIRGpmgaIlJe0fYSIOSZ_Y/edit?usp=sharing"/>
        /// </summary>
        public void ProcessIncoming(
            System.Web.HttpContextBase context)
        {
            // Is URL explicitly excluded from localization?
            if (!m_urlLocalizer.FilterIncoming(context.Request.Url))
            {
                return;
            }             // YES. Continue handling request.

            bool allowRedirect =
                context.Request.HttpMethod.Equals("GET", StringComparison.OrdinalIgnoreCase) ||
                context.Request.HttpMethod.Equals("HEAD", StringComparison.OrdinalIgnoreCase);

            // NO. Is request URL localized?
            string urlNonlocalized;
            string langtag = m_urlLocalizer.ExtractLangTagFromUrl(context, context.Request.RawUrl, UriKind.Relative, true, out urlNonlocalized);

            if (langtag == null)
            {
                // NO.
                // langtag = best match between
                // 1. Inferred user languages (cookie and Accept-Language header)
                // 2. App Languages.
                LanguageTag lt = context.GetInferredLanguage();

                // If redirection allowed...redirect user agent (browser) to localized URL.
                // The principle purpose of this redirection is to ensure the browser is showing the correct URL
                // in its address field.
                if (allowRedirect)
                {
                    RedirectWithLanguage(context, context.Request.RawUrl, lt.ToString(), m_urlLocalizer);
                    return;
                }

                // Otherwise, handle the request under the language infered above but without doing the redirect.
                // NB: this will mean that the user agent (browser) won't have the correct URL displayed;
                // however, this typically won't be an issue because we are talking about POST, PUT and DELETE methods
                // here which are typically not shown to the user.
                else
                {
                    context.SetPrincipalAppLanguageForRequest(lt);
                    return; // Continue handling request.
                }
            }

            // YES. Does langtag EXACTLY match an App Language?
            LanguageTag appLangTag = LanguageHelpers.GetMatchingAppLanguage(langtag);

            if (appLangTag.IsValid() &&
                appLangTag.Equals(langtag))
            {
                // YES. Establish langtag as the PAL for the request.
                context.SetPrincipalAppLanguageForRequest(appLangTag);

                // Rewrite URL for this request.
                context.RewritePath(urlNonlocalized);

                // Continue handling request.
                return;
            }

            // NO. Does langtag LOOSELY match an App Language?
            else if (appLangTag.IsValid() &&
                     !appLangTag.Equals(langtag))
            {
                // YES. Localize URL with matching App Language.
                // Conditionally redirect user agent to localized URL.
                if (allowRedirect)
                {
                    RedirectWithLanguage(context, urlNonlocalized, appLangTag.ToString(), m_urlLocalizer);
                    return;
                }
            }
            // NO. Do nothing to URL; expect a 404 which corresponds to language not supported.
            // Continue handling request.
        }
Exemplo n.º 15
0
        /// <summary>
        /// Sessions the start.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">E.</param>
        private void Session_Start(object sender, EventArgs e)
        {
            string languageBrowser = null;

            try
            {
                if (Request.IsAuthenticated)
                {
                    UserService _userService  = new UserService();
                    var         ConnectedUser = _userService.GetUserByUserName(User.Identity.Name);
                    if (ConnectedUser != null)
                    {
                        languageBrowser = ConnectedUser.Language?.Code;
                    }
                }
                else
                {
                    Session[CommonsConst.Const.UserSession] = null;
                }
                if (String.IsNullOrEmpty(languageBrowser))
                {
                    string[] languages = Request?.UserLanguages;
                    if (languages != null && languages.Length > 0)
                    {
                        string Favoritelanguage = languages[0];
                        languageBrowser = CommonsConst.Const.DefaultCulture;
                        CategoryService _categoryService = new CategoryService();
                        var             ListLanguages    = _categoryService.GetSelectionList(CommonsConst.CategoryTypes.Language);
                        if (ListLanguages != null && ListLanguages.Count > 0)
                        {
                            foreach (var Language in ListLanguages)
                            {
                                if (Language.Code == Favoritelanguage || Favoritelanguage.IndexOf(Language.Code + "-") > -1)
                                {
                                    languageBrowser = Language.Code;
                                    break;
                                }
                            }
                        }
                    }
                }



                if (!String.IsNullOrEmpty(languageBrowser))
                {
                    Session[CommonsConst.Const.WebsiteLanguageSession] = languageBrowser;
                    i18n.LanguageTag lt = i18n.LanguageTag.GetCachedInstance(languageBrowser);
                    Response.Cookies.Add(new HttpCookie(CommonsConst.Const.i18nlangtag)
                    {
                        Value    = lt.ToString(),
                        HttpOnly = true,
                        Expires  = DateTime.UtcNow.AddYears(1)
                    });


                    System.Web.HttpContext.Current.SetPrincipalAppLanguageForRequest(lt);
                }
            }
            catch (Exception ex)
            {
                Commons.Logger.GenerateError(ex, System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
            }
        }
Exemplo n.º 16
0
        public virtual string GetText(string msgid, string msgcomment, LanguageItem[] languages, out LanguageTag o_langtag, int maxPasses = -1)
        {
            // Validate arguments.
            if (maxPasses > (int)LanguageTag.MatchGrade._MaxMatch + 1)
            {
                maxPasses = (int)LanguageTag.MatchGrade._MaxMatch + 1;
            }
            // Init.
            bool fallbackOnDefault = maxPasses == (int)LanguageTag.MatchGrade._MaxMatch + 1 ||
                                     maxPasses == -1;
            // Determine the key for the msg lookup. This may be either msgid or msgid+msgcomment, depending on the prevalent
            // MessageContextEnabledFromComment setting.
            string msgkey = msgid == null ?
                            msgid:
                            TemplateItem.KeyFromMsgidAndComment(msgid, msgcomment, _settings.MessageContextEnabledFromComment);
            // Perform language matching based on UserLanguages, AppLanguages, and presence of
            // resource under msgid for any particular AppLanguage.
            string text;

            o_langtag = LanguageMatching.MatchLists(
                languages,
                GetAppLanguages().Values,
                msgkey,
                TryGetTextFor,
                out text,
                Math.Min(maxPasses, (int)LanguageTag.MatchGrade._MaxMatch));
            // If match was successfull
            if (text != null)
            {
                // If the msgkey was returned...don't output that but rather the msgid as the msgkey
                // may be msgid+msgcomment.
                if (text == msgkey)
                {
                    return(msgid);
                }
                return(text);
            }
            // Optionally try default language.
            if (fallbackOnDefault)
            {
                o_langtag = LocalizedApplication.Current.DefaultLanguageTag;
                return(msgid);
            }
            //
            return(null);
        }
Exemplo n.º 17
0
        /// <summary>
        /// Given a list of user-preferred languages (in order of precedence) and the list of languages
        /// in which an arbitrary resource is available (AppLanguages), returns the AppLanguage which
        /// the user is most likely able to understand.
        /// </summary>
        /// <param name="UserLanguages">
        /// A list of user-preferred languages (in order of precedence).
        /// </param>
        /// <param name="AppLanguages">
        /// The list of languages in which an arbitrary resource is available.
        /// </param>
        /// <param name="key">
        /// Optionally specifies the key or a message to be looked up in order to validate
        /// a language selection. Only if the language passes the validation will it be selected.
        /// Set in conjunction with TryGetTextFor.
        /// May be null (while TryGetTextFor is non-null) which specifies that one or more messages
        /// must exists for a language for it to be considered valid (PO-valid).
        /// </param>
        /// <param name="TryGetTextFor">
        /// Optional delegate to be called in order to validate a language for selection.
        /// See TextLocalizer.TryGetTextFor for more details.
        /// </param>
        /// <param name="o_text">
        /// When language validation is enabled (TryGetTextFor is non-null) outputs the translated
        /// text that was returned by TryGetTextFor when the language was validated.
        /// If key == null then this will be set to "".
        /// </param>
        /// <param name="maxPasses">
        /// 0 - allow exact match only
        /// 1 - allow exact match or default-region match only
        /// 2 - allow exact match or default-region match or script match only
        /// 3 - allow exact match or default-region match or script match or language match only
        /// -1 to set to most tolerant (i.e. 4).
        /// </param>
        /// <param name="relatedTo">
        /// Optionally applies a filter to the user languages considered for a match.
        /// When set, then only user languages that have a matching language to that of relatedTo
        /// are considered.
        /// </param>
        /// <param name="palPrioritization">
        /// Indicates whether PAL Prioritization is enabled.
        /// </param>
        /// <returns>
        /// LanguageTag instance selected from AppLanguages with the best match, or null if there is no match
        /// at all (or UserLanguages and/or AppLanguages is empty).
        /// It is possible for there to be no match at all if no language subtag in the UserLanguages tags
        /// matches the same of any of the tags in AppLanguages list.
        /// </returns>
        /// <exception cref="System.ArgumentNullException">Thrown if UserLanguages or AppLanguages is null.</exception>
        /// <remarks>
        /// This method called many times per request. Every effort taken to avoid it making any heap allocations.<br/>
        /// <br/>
        /// Principle Application Language (PAL) Prioritization:<br/>
        ///   User has selected an explicit language in the webapp e.g. fr-CH (i.e. PAL is set to fr-CH).
        ///   Their browser is set to languages en-US, zh-Hans.
        ///   Therefore, UserLanguages[] equals fr-CH, en-US, zh-Hans.
        ///   We don't have a particular message in fr-CH, but have it in fr and fr-CA.
        ///   We also have message in en-US and zh-Hans.
        ///   We presume the message from fr or fr-CA is better match than en-US or zh-Hans.
        ///   However, without PAL prioritization, en-US is returned and failing that, zh-Hans.
        ///   Therefore, for the 1st entry in UserLanguages (i.e. explicit user selection in app)
        ///   we try all match grades first. Only if there is no match whatsoever for the PAL
        ///   do we move no to the other (browser) languages, where return to prioritizing match grade
        ///   i.e. loop through all the languages first at the strictest match grade before loosening
        ///   to the next match grade, and so on.
        /// Refinement to PAL Prioritization:<br/>
        ///   UserLanguages (UL) = de-ch,de-at (PAL = de-ch)<br/>
        ///   AppLanguages  (AL) = de,de-at,en<br/>
        ///   There is no exact match for PAL in AppLanguages.<br/>
        ///   However:<br/>
        ///    1. the second UL (de-at) has an exact match with an AL<br/>
        ///    2. the parent of the PAL (de) has an exact match with an AL.<br/>
        ///   Normally, PAL Prioritization means that 2. takes precedence.
        ///   However, that means choosing de over de-at, when the user
        ///   has said they understand de-at (it being preferable to be
        ///   more specific, esp. in the case of different scripts under
        ///   the same language).<br/>
        ///   Therefore, as a refinement to PAL Prioritization, before selecting
        ///   'de' we run the full algorithm again (without PAL Prioritization)
        ///   but only considering langtags related to the PAL.
        /// </remarks>
        public static LanguageTag MatchLists(
            LanguageItem[] UserLanguages,
            IEnumerable <LanguageTag> AppLanguages,
            string key,
            Func <string, string, string> TryGetTextFor,
            out string o_text,
            int maxPasses          = -1,
            LanguageTag relatedTo  = null,
            bool palPrioritization = true)
        {
            int         idxUserLang = 0;
            LanguageTag ltUser;

            // Validate arguments.
            if (UserLanguages == null)
            {
                throw new ArgumentNullException("UserLanguages");
            }
            if (AppLanguages == null)
            {
                throw new ArgumentNullException("AppLanguages");
            }
            if (maxPasses > (int)LanguageTag.MatchGrade._MaxMatch)
            {
                maxPasses = (int)LanguageTag.MatchGrade._MaxMatch;
            }

            //#78
            //if (key != null && key.Equals("Sign In", StringComparison.InvariantCultureIgnoreCase)) {
            //    key = key; }

            // If one or more UserLanguages determined for the current request
            if (UserLanguages.Length != 0)
            {
                // First, find any match for the PAL (see PAL Prioritization notes above).
                // If a PAL has been determined for the request
                if (palPrioritization &&
                    (ltUser = (LanguageTag)UserLanguages[0].LanguageTag) != null &&
                    (relatedTo == null || ltUser.Match(relatedTo, LanguageTag.MatchGrade.LanguageMatch) != 0))      // Apply any filter on eligible user languages.
                // Wiz through all match grades for the Principle Application Language.
                {
                    for (int pass = 0; pass <= (int)LanguageTag.MatchGrade._MaxMatch; ++pass)
                    {
                        LanguageTag.MatchGrade matchGrade = (LanguageTag.MatchGrade)pass;
                        foreach (LanguageTag langApp in AppLanguages)
                        {
                            // If languages do not match at the current grade...goto next.
                            if (ltUser.Match(langApp, matchGrade) == 0)
                            {
                                continue;
                            }
                            // Optionally test for a resource of the given key in the matching language.
                            if (TryGetTextFor != null)
                            {
                                o_text = TryGetTextFor(langApp.ToString(), key);
                                if (o_text == null)
                                {
                                    continue;
                                }
                            }
                            else
                            {
                                o_text = null;
                            }
                            // We have a match between PAL and an AL that is NOT an exact match,
                            // there may be a UL that is related to the PAL but has a closer (more specific)
                            // match to an AL. See "Refinement to PAL Prioritization" notes above for more details.
                            if (matchGrade != LanguageTag.MatchGrade.ExactMatch)
                            {
                                LanguageTag lt = MatchLists(
                                    UserLanguages,
                                    AppLanguages,
                                    key,
                                    TryGetTextFor,
                                    out o_text,
                                    maxPasses,
                                    langApp,
                                    false); // false = disable PAL Prioritization.
                                if (lt != null)
                                {
                                    return(lt);
                                }
                            }
                            // Match.
                            ++UserLanguages[idxUserLang].UseCount;
                            return(langApp);
                        }
                    }
                }
                // PAL didn't match so skip over that now.
                ++idxUserLang;
                // No match for PAL, so now try for the browser languages, this time prioritizing the
                // match grade.
                for (int pass = 0; pass <= (int)LanguageTag.MatchGrade._MaxMatch; ++pass)
                {
                    LanguageTag.MatchGrade matchGrade = (LanguageTag.MatchGrade)pass;
                    for (int i = idxUserLang; i < UserLanguages.Length; ++i)
                    {
                        ltUser = (LanguageTag)UserLanguages[i].LanguageTag;
                        if (ltUser == null)
                        {
                            continue;
                        }
                        // TODO: move the Match functionality to this class, and make it operate on ILanguageTag.
                        // Or consider making the Match logic more abstract, e.g. requesting number of passes from
                        // the object, and passing a pass value through to Match.
                        // Apply any filter on eligible user languages.
                        if (relatedTo != null)
                        {
                            if (ltUser.Match(relatedTo, LanguageTag.MatchGrade.LanguageMatch) == 0)
                            {
                                continue;
                            }
                        }
                        foreach (LanguageTag langApp in AppLanguages)
                        {
                            // If languages do not match at the current grade...goto next.
                            if (ltUser.Match(langApp, matchGrade) == 0)
                            {
                                continue;
                            }
                            // Optionally test for a resource of the given key in the matching language.
                            if (TryGetTextFor != null)
                            {
                                o_text = TryGetTextFor(langApp.ToString(), key);
                                if (o_text == null)
                                {
                                    continue;
                                }
                            }
                            else
                            {
                                o_text = null;
                            }
                            // Match.
                            ++UserLanguages[i].UseCount;
                            return(langApp);
                        }
                    }
                }
            }
            // No match at all.
            o_text = null;
            return(null);
        }
Exemplo n.º 18
0
        // Static helpers
        /// <summary>
        /// Parses an HTTP Accept-Language or Content-Language header value, returning
        /// a representative ordered array of LanguageItem instances, sorted in order of
        /// language preference.
        /// E.g. "de;q=0.5, en;q=1, fr-FR;q=0,ga;q=0.5".
        /// Notably, is able to re-order elements based on quality.
        /// </summary>
        /// <remarks>
        /// The first element position in the returned array is reserved for an item that
        /// describes the Principal Application Language (PAL) for the request. If/when the PAL
        /// is not set, that element will be a null item (LanguageItem.LanguageTag == null).
        ///
        /// This method is designed to be as efficient as possible, typically requiring
        /// only a single heap alloc, for the returned array object itself.
        /// </remarks>
        /// <param name="headerval">
        /// HTTP Accept-Language header value.
        /// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html.
        /// May be null or empty string for zero languages.
        /// </param>
        /// <param name="pal">
        /// Optional language to store at the first element position in the array, which is reserved
        /// for the Principal Application Language (PAL). Any such LanguageItem stored that has a quality
        /// value of 2 (LanguageItem.PalQualitySetting). Null if no such language to be stored there and the
        /// item to be set as null (LanguageItem.LanguageTag == null).
        /// </param>
        /// <returns>
        /// Array of languages items (with possibly null LanguageTag members) sorted in order or language preference.
        /// </returns>
        public static       LanguageItem[] ParseHttpLanguageHeader(string headerval, ILanguageTag pal = null)
        {
            // This method is designed to be as efficient as possible (avoiding string allocations where possible).
            //
            int begin, end, pos1;
            int len     = headerval != null ? headerval.Length : 0;
            int ordinal = 0;
            // Init array with enough elements for each language entry in the header.
            var LanguageItems = new LanguageItem[(len > 0 ? headerval.CountOfChar(',') + 1 : 0) + 1];

            // First element position is reserved for any PAL.
            LanguageItems[ordinal] = new LanguageItem(pal, PalQualitySetting, ordinal);
            ++ordinal;
            // For each language component of the header (delimited by comma)
            for (begin = 0; begin < len; begin = end + 1)
            {
                end = headerval.IndexOf(',', begin);
                if (-1 == end)
                {
                    end = len;
                }
                float qvalue = 1;
                pos1 = headerval.IndexOf(';', begin);
                if (-1 != pos1 &&
                    pos1 < end)
                {
                    // pos1 -> ";q=n"
                    if (pos1 - begin < 2 || // room for valid langtag
                        pos1 + 3 >= headerval.Length ||
                        headerval[pos1 + 1] != 'q' ||
                        headerval[pos1 + 2] != '=')
                    {
                        continue;
                    }
                    if (!ParseHelpers.TryParseDecimal(headerval, pos1 + 3, -1, out qvalue))
                    {
                        continue;
                    }
                    if (qvalue < 0f || qvalue > 1.0f)
                    {
                        continue;
                    }
                }
                else
                {
                    pos1 = end;
                }
                // Skip over any whitespace. We expect this to make the following Trim redundant,
                // thus saving on an alloc.
                while (headerval[begin] == ' ')
                {
                    ++begin;
                }
                // Extract language subtag e.g. "fr-FR".
                // NB: we expect this to be efficient and not allocate a new string as
                // a string matching the trimmed value is most likely already Intern (held by
                // the LanguageTag cache as a key value). Only first time here for a particular
                // value will a new string possibly be allocated.
                string langtag = headerval.Substring(begin, pos1 - begin).Trim();
                // Wrap langtag.
                LanguageTag lt = i18n.LanguageTag.GetCachedInstance(langtag);
                if (lt == null || !lt.Language.IsSet())
                {
                    continue;
                }
                // Ignore the langtag if already added.
                //if (pal.IsValid() && pal.Equals(lt)) {
                //    continue; }
                // NB: the above check disabled as it can cause the first lang in the header,
                // where it matches the PAL intially, to be lost if/when the PAL is later changed
                // to something else.
                // Store a new representative item.
                // NB: LanguageItem is a value type so no alloc done here.
                LanguageItems[ordinal] = new LanguageItem(lt, qvalue, ordinal);
                ++ordinal;
            }
            // Truncate any extra elements from end of array.
            if (ordinal != LanguageItems.Length)
            {
                LanguageItems = LanguageItems.Where(x => x.LanguageTag.IsValid()).ToArray();
            }
            // If there was no PAL, and the header value was invalid then we will have no language items, so add the default
            if (LanguageItems.Length == 0)
            {
                LanguageItems = new LanguageItem[] { new LanguageItem(LocalizedApplication.Current.DefaultLanguageTag, PalQualitySetting, 0) }
            }
            ;
            // Rearrange items into order of precedence. This is facilitated by LanguageItem's
            // impl. of IComparable.
            Array.Sort(LanguageItems);
            // Done.
            return(LanguageItems);
        }
    }