Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Claudiu Bria 34 posts 146 karma points
    May 11, 2017 @ 13:02
    Claudiu Bria
    0

    Prices Culture

    Hi Rusty,

    Here's the screenshot of our Merchello implementation: enter image description here

    Umbraco version is 7.5.11, Merchello is 2.6.0(dev), current user in umbraco has language English (United Kingdom), Merchello Setting Currency is British Pound, Default Extended Content Culture is English (United Kingdom),

    then products Ajour, Element, Evolution, Flea and Paranormal have extended content set in both nb-NO and en-US cultures, the last product Test11 has extended content saved in en-GB only.

    In merchello.config I have:

      <currencyFormats>
        <!-- Define currency formats by code.
            PR: https://github.com/Merchello/Merchello/pull/1422
          <format currencyCode="sek" format="{1:0.00}{0}" />
        -->
        <format currencyCode="nok" format="{1:#\ ##0.00} {0}" />
        <format currencyCode="gbp" format="{0}{1:#,##0.00}" />
      </currencyFormats>
    

    Now the prices on the page:

    1) First are rendered the three products at the bottom since they do not have variants, and the rendered decimal prices (i.e £842 712,00) follow somehow the gdp currency format position

    <format currencyCode="gbp" format="{0}{1:#,##0.00}" />
    

    but with the value defined by the nok currency format pattern:

        <format currencyCode="nok" format="{1:#\ ##0.00} {0}" />
    

    The formatted currency is called directly from the AddToBasketForm.cshtml page from line around 217:

        sb.AppendFormat("<span>{0}</span>", product.Price.AsFormattedCurrency());
    

    When debugging the .AsFormattedCurrency() calls, breaking inside Merchello.Core\CurrencyContext.cs at line 121:

        public string FormatCurrency(decimal amount)
        {
            return string.Format(this.StoreCurrencyFormat.Format, this._storeCurrency.Symbol, amount);
        }
    

    and inspecting in the ImmediateWindow:

    System.Threading.Thread.CurrentThread.CurrentCulture

    I get the

    System.Threading.Thread.CurrentThread.CurrentCulture {nb-NO}
        Calendar: {System.Globalization.GregorianCalendar}
        CompareInfo: {CompareInfo - nb-NO}
        CultureTypes: SpecificCultures | InstalledWin32Cultures | FrameworkCultures
        DateTimeFormat: {System.Globalization.DateTimeFormatInfo}
        DisplayName: "Norwegian, Bokmål (Norway)"
        EnglishName: "Norwegian Bokmål (Norway)"
        IetfLanguageTag: "nb-NO"
        IsNeutralCulture: false
        IsReadOnly: false
        KeyboardLayoutId: 1044
        LCID: 1044
        Name: "nb-NO"
        NativeName: "norsk bokmål (Norge)"
        NumberFormat: {System.Globalization.NumberFormatInfo}
        OptionalCalendars: {System.Globalization.Calendar[1]}
        Parent: {nb}
        TextInfo: {TextInfo - nb-NO}
        ThreeLetterISOLanguageName: "nob"
        ThreeLetterWindowsLanguageName: "NOR"
        TwoLetterISOLanguageName: "nb"
        UseUserOverride: true
    

    2) Secondly are renderred the upper row products, since they have variants, so the endpoint

    productTableApi: '/umbraco/Merchello/ProductDataTableApi/'

    is called with the WebApi method

       [HttpPost]
        public virtual IEnumerable<TTable> PostGetProductDataTables(Guid[] keys)
    

    that calls in the ProductDataTableFactory.cs method:

    public TRow Create(IProductContentBase baseContent, bool isVariant = true)
    

    that contains the AsFormattedCurrency() calls again:

            var row = new TRow
                {
                    ProductKey = productKey,
                    ProductVariantKey = productVariantKey,
                    Sku = baseContent.Sku,
                    MatchKeys = matchKeys,
                    OnSale = baseContent.OnSale,
                    FormattedPrice = baseContent.Price.AsFormattedCurrency(),
                    Price = baseContent.Price,
                    SalePrice = baseContent.SalePrice,
                    FormattedSalePrice = baseContent.SalePrice.AsFormattedCurrency(),
                    IsAvailable = isAvaliable,
                    InventoryCount = baseContent.TotalInventoryCount,
                    OutOfStockPurchase = baseContent.OutOfStockPurchase,
                    IsForVariant = isVariant
                };
    

    Since this is inside the WebApi, there is no Culture carried from the page, so the rendered decimal prices (i.e £2,049.00) seem to follow now the gdp currency format position and pattern

    <format currencyCode="gbp" format="{0}{1:#,##0.00}" />
    

    but when debugging again the .AsFormattedCurrency() calls, breaking inside Merchello.Core\CurrencyContext.cs at line 121:

        public string FormatCurrency(decimal amount)
        {
            return string.Format(this.StoreCurrencyFormat.Format, this._storeCurrency.Symbol, amount);
        }
    

    and inspecting again in the ImmediateWindow:

    System.Threading.Thread.CurrentThread.CurrentCulture

    I now get the

    System.Threading.Thread.CurrentThread.CurrentCulture
    {en-US}
        Calendar: {System.Globalization.GregorianCalendar}
        CompareInfo: {CompareInfo - en-US}
        CultureTypes: SpecificCultures | InstalledWin32Cultures | FrameworkCultures
        DateTimeFormat: {System.Globalization.DateTimeFormatInfo}
        DisplayName: "English (United States)"
        EnglishName: "English (United States)"
        IetfLanguageTag: "en-US"
        IsNeutralCulture: false
        IsReadOnly: true
        KeyboardLayoutId: 1033
        LCID: 1033
        Name: "en-US"
        NativeName: "English (United States)"
        NumberFormat: {System.Globalization.NumberFormatInfo}
        OptionalCalendars: {System.Globalization.Calendar[2]}
        Parent: {en}
        TextInfo: {TextInfo - en-US}
        ThreeLetterISOLanguageName: "eng"
        ThreeLetterWindowsLanguageName: "ENU"
        TwoLetterISOLanguageName: "en"
        UseUserOverride: true
    

    I hope i got the behavior, do I interpret things correctly Rusty ?

    Why do I see this combination of gdp/nok formats in the rendered decimals ?

    Is there anything I'm missing, please ?

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    May 13, 2017 @ 15:35
    Rusty Swayne
    0

    Hey Claudiu

    Merchello by default only supports a single currency which is set in the back office AND it is not tied to the localization. That's only the textual content (the Umbraco properties).

    If you want to allow for different currencies there are a bunch of other things to consider, like making sure that an order is all done in the same currency, price conversions, etc.

    There have been a number of people who have done it - Simon Brownlie for one ...

  • Claudiu Bria 34 posts 146 karma points
    May 15, 2017 @ 07:12
    Claudiu Bria
    0

    Thanks for your answer Rusty.

    In our case here I am merely pointing to the mixed formatting of a single value of a price (£842 712,00).

    Since Merchello is not tied to localization, then in our case I found a solution to just use the default extended content culture setting as a global localization setting for Merchello:

    Helpers.cs:

    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Net.Mail;
    using System.Text.RegularExpressions;
    using System.Web;
    using Merchello.Core;
    using Merchello.Web;
    using MerchelloConstants = Merchello.Core.Constants;
    using Umbraco.Core;
    using Umbraco.Core.Services;
    using Umbraco.Web;
    
    namespace AjourCms.Ajour
    {
        public static class Helpers
        {
            public static string DecryptLicence(string licenceKey)
            {
                var licence = new AjourLicence(licenceKey);
                if (!licence.IsValid) return "Licence invalid" + (licence.Exception != null ? ": " + licence.Exception.Message : "");
                return "Users:" + (licence.Users != null ? licence.Users.Value.ToString() : "") + ", " +
                       (licence.IsTrial ? "Trial" : "Expires:" + (licence.ExpirationDate != null ? licence.ExpirationDate.Value.ToLongDateString() : ""));
            }
    
            public static CultureInfo MerchelloStoreCulture
            {
                get { return CultureInfo.GetCultureInfo(MerchelloContext.Current.Services.StoreSettingService.GetByKey(MerchelloConstants.StoreSetting.DefaultExtendedContentCulture).Value); }
            }
    
            public static string AjourCultureSuffix(CultureInfo culture)
            {
                return culture.Name.ToLowerInvariant().Replace("-", "_");
            }
    
            public static string Localize(string area, string key, ILocalizedTextService localizedTextService = null, CultureInfo cultureInfo = null)
            {
                if (string.IsNullOrWhiteSpace(area))
                {
                    if (string.IsNullOrWhiteSpace(key))
                    {
                        return "[!]";
                    }
                    return "[!/" + key + "]";
                }
                if (string.IsNullOrWhiteSpace(key))
                {
                    return "[!" + area + "/]";
                }
                if (cultureInfo == null) cultureInfo = MerchelloStoreCulture;
                if (cultureInfo == null)
                {
                    return "[!" + key + "]";
                }
                if (localizedTextService == null) localizedTextService = ApplicationContext.Current.Services.TextService;
                if (localizedTextService == null)
                {
                    return "[!" + key + "]";
                }
                return localizedTextService.Localize(area + "/" + key, cultureInfo);
            }
    
            public static string LocalizeProductEstablishingOptionChoice(string optionChoice, string localizedEstablishingFee, string localizedNoEstablishingFee)
            {
                string localizedOptionChoice;
                var genericName = optionChoice;
                string translatedName = null;
                if (genericName.Contains(":") && genericName.Split(':').Length > 0)
                {
                    decimal decimalFee;
                    var genericNameDecimalFee = genericName.Split(':')[1].Trim().Split(' ')[0];
                    genericNameDecimalFee = Regex.Replace(genericNameDecimalFee, "[^0-9.,]", "");
                    var serverCultureDecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator;
                    if (serverCultureDecimalSeparator == "." && genericNameDecimalFee.Contains(","))
                    {
                        genericNameDecimalFee = genericNameDecimalFee.Replace(",", ".");
                    }
                    if (serverCultureDecimalSeparator == "," && genericNameDecimalFee.Contains("."))
                    {
                        genericNameDecimalFee = genericNameDecimalFee.Replace(".", ",");
                    }
                    if (decimal.TryParse(genericNameDecimalFee, out decimalFee))
                    {
                        var parsedFee = decimalFee.AsFormattedCurrency();
                        translatedName = localizedEstablishingFee + ": " + parsedFee;
                    }
                }
                else
                {
                    translatedName = localizedNoEstablishingFee;
                }
                if (translatedName != null)
                {
                    localizedOptionChoice = translatedName;
                }
                else
                {
                    localizedOptionChoice = "!" + optionChoice;
                }
                return localizedOptionChoice;
            }
    
            public static string GetCurrentPaymentLogs()
            {
                var result = string.Empty;
                var currentCustomer = new CustomerContext(UmbracoContext.Current).CurrentCustomer;
                var logCacheKey = "" + currentCustomer.Key + "_Payment";
                if (HttpContext.Current.Cache[logCacheKey] != null)
                {
                    var storedLogs = HttpContext.Current.Cache[logCacheKey] as string;
                    if (!string.IsNullOrWhiteSpace(storedLogs))
                    {
                        var storedLogsLines = storedLogs.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
                        foreach (var storedLogsLine in storedLogsLines)
                        {
                            result += "<p>" + storedLogsLine + "</p>" + Environment.NewLine;
                        }
                        //result = storedLogs.Replace(Environment.NewLine, "<br/>");
                       // result = storedLogs;
                    }
                    HttpContext.Current.Cache.Remove(logCacheKey);
                }
                return result;
            }
        }
    }
    

    And then in pages:

    ...
    @using AjourHelpers = AjourCms.Ajour.Helpers;
    
    @{
        var product = Merchello.TypedProductContent(Model.ProductKey);
    
        // Local variable for partial view conditionals
        var hasVariants = product.ProductVariants.Any();
    
        var merchelloStoreCulture = AjourHelpers.MerchelloStoreCulture;
        System.Threading.Thread.CurrentThread.CurrentCulture = merchelloStoreCulture;
        System.Threading.Thread.CurrentThread.CurrentUICulture = merchelloStoreCulture;
    ...
    
    }
    

    With this, depending on our currencyCode formats defined in merchello.config:

    <format currencyCode="gbp" format="{0}{1:#,##0.00}" />
    <format currencyCode="nok" format="{1:#\ ##0.00} {0}" />
    

    we can have all prices displayed correctly for English (United Kingdom):

    £842,712.00

    and for Norwegian, Bokmål (Norway):

    842 712,00 kr

  • Rusty Swayne 1655 posts 4993 karma points c-trib
    May 17, 2017 @ 02:20
    Rusty Swayne
    0

    Hey Claudiu - Thanks for posting your solution.

    I've been looking at a library named NodaMoney (https://github.com/remyvd/NodaMoney) to handle this sort of thing in the future. I've actually replaced all decimal prices in Merchello in a private branch with the Money type and done a bit of testing ... works pretty slick.

Please Sign in or register to post replies

Write your reply to:

Draft