Locally Variable

Henrik H was hired by a customer to fix some bugs in their application. The main one was that their C# web app didn't properly track the user's culture settings. Henrik spoke with their internal developer, who originally wrote the application, and was told: "Sometimes the culture name and LCID is out of sync. I don't understand why?"

Well, from that description, Henrik didn't understand either. Why was there a separate name and what the heck was LCID? Why were two variables getting synced? The only answer would be in the code, so Henrik dug in.

    public struct Culture
    {
        //Danish
        public const string DA = "da-DK";
        //English
        public const string EN = "en-US";
    }

We'll start with this struct. This isn't the WTF, but it certainly sets us up. We're saving locale names as easily accessible targets, so that our localization can look up the proper strings and formatting for our locale. That, itself, isn't too bad, and as it turns out in C# that const members of structs are automatically handled as being static, so code-wise, this is mostly fine- but with one note.

Their primary English speaking customers are in the UK, not the US. They don't actually do business directly in the US. I can imagine that elsewhere in the code, they end up having to "fix" date and numeric formatting, so it stays European.

Oh, who am I kidding. They they're not using the built-in formatting functions and wrote their own date formatting logic.

Let's look at the real WTF, though.

    void Session_Start(object sender, EventArgs e)
    {
        //set Danish as default startup language
        Session[Global.SESSION_KEY_CULTURE] = Culture.DA;
        if (Session[Global.SESSION_KEY_CULTURE].ToString() == "da-DK")
        {
            Application["LCID"] = 1030;
        }
        else
        {
            Application["LCID"] = 1033;
        }
    }

This function executes every time a new user opens the page. It defaults their Session variable to be the Danish locale. Pretty reasonable for a Danish company. Then, they immediately check the variable they just set, and if it's equal to the string literal "da-DK" (because they already forgot they made a constant), they set the "LCID" variable to a cryptic and meaningless numeric value.

And more important, they set the "LCID" variable as an Application variable. That's the ASP .Net way of making a global variable. Worse: a global variable shared across all user sessions.

Which goes a long way to explaining why the locale name and the "LCID" kept getting desynced- every time a new user opened the page, the global variable would get set to 1030, the value for Denmark, changing the LCID for every currently active user.

And, as stated, none of the code used any of C#'s built-in localization features, so the rest of the code was a mess of conditional statements that keyed off of the Global.SESSION_KEY_CULTURE value sometimes, and the LCID value other times. Turning every instance of Application["LCID"] into Session["LCID"] went a long way to fixing the immediate bug, but did nothing to trim back the thicket of localization based errors due to home-grow localization features.

I doubt Henrik's contract had enough budget for that. And even if they did, would the price be worth Henrik's sanity?

[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.

This post originally appeared on The Daily WTF.

Leave a Reply

Your email address will not be published. Required fields are marked *