Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move CultureInfoGenerator and CultureCache projects from Localization repo #52

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Common.sln
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Primit
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Primitives.Tests", "test\Microsoft.Extensions.Primitives.Tests\Microsoft.Extensions.Primitives.Tests.xproj", "{61F72E92-B3AE-4A10-B838-44F80AED40AE}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.Globalization.CultureInfoCache.Sources", "src\Microsoft.Extensions.Globalization.CultureInfoCache.Sources\Microsoft.Extensions.Globalization.CultureInfoCache.Sources.xproj", "{248DAAAB-7DF9-4024-BF1E-5366FDC89BD4}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Extensions.CultureInfoGenerator.Sources", "src\Microsoft.Extensions.CultureInfoGenerator.Sources\Microsoft.Extensions.CultureInfoGenerator.Sources.xproj", "{41F14B0D-5F94-49C7-A5EA-12EB846C2BDB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -96,6 +100,14 @@ Global
{61F72E92-B3AE-4A10-B838-44F80AED40AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61F72E92-B3AE-4A10-B838-44F80AED40AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{61F72E92-B3AE-4A10-B838-44F80AED40AE}.Release|Any CPU.Build.0 = Release|Any CPU
{248DAAAB-7DF9-4024-BF1E-5366FDC89BD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{248DAAAB-7DF9-4024-BF1E-5366FDC89BD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{248DAAAB-7DF9-4024-BF1E-5366FDC89BD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{248DAAAB-7DF9-4024-BF1E-5366FDC89BD4}.Release|Any CPU.Build.0 = Release|Any CPU
{41F14B0D-5F94-49C7-A5EA-12EB846C2BDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{41F14B0D-5F94-49C7-A5EA-12EB846C2BDB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{41F14B0D-5F94-49C7-A5EA-12EB846C2BDB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{41F14B0D-5F94-49C7-A5EA-12EB846C2BDB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -115,5 +127,7 @@ Global
{E7B38D6C-90EF-4C35-8313-6E05C1DA6354} = {6878D8F1-6DCE-4677-AA1A-4D14BA6D2D60}
{711B5F84-1EDC-49A7-9643-766CE93A0466} = {FEAA3936-5906-4383-B750-F07FE1B156C5}
{61F72E92-B3AE-4A10-B838-44F80AED40AE} = {6878D8F1-6DCE-4677-AA1A-4D14BA6D2D60}
{248DAAAB-7DF9-4024-BF1E-5366FDC89BD4} = {FEAA3936-5906-4383-B750-F07FE1B156C5}
{41F14B0D-5F94-49C7-A5EA-12EB846C2BDB} = {FEAA3936-5906-4383-B750-F07FE1B156C5}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>

<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>41f14b0d-5f94-49c7-a5ea-12eb846c2bdb</ProjectGuid>
<RootNamespace>Microsoft.Extensions.CultureInfoGenerator.Sources</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>

<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
110 changes: 110 additions & 0 deletions src/Microsoft.Extensions.CultureInfoGenerator.Sources/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Globalization;
using System.IO;
using System.Linq;
using Microsoft.Dnx.Runtime;
using Microsoft.Win32;

namespace Microsoft.Extensions.CultureInfoGenerator
{
public class Program
{
private readonly string _appName;
private readonly string _appPath;

public Program(IApplicationEnvironment appEnvironment)
{
_appName = appEnvironment.ApplicationName;
_appPath = appEnvironment.ApplicationBasePath;
}

public void Main(string[] args)
{
var outputFilePath = Path.GetFullPath(args.Length > 0 ? args[0] : Path.Combine(_appPath, "../Microsoft.Extensions.Globalization.CultureInfoCache.Sources/CultureInfoList.cs"));
var netFxVersion = Get45or451FromRegistry();
var windowsVersion = Environment.OSVersion;

using (var writer = new StreamWriter(outputFilePath, false))
{
writer.WriteLine($@"// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

// *************************** THIS FILE IS GENERATED BY A TOOL ***************************
// To make changes to this file look at the CultureInfoGenerator project in this solution.

using System;
using System.Collections.Generic;

namespace Microsoft.Extensions.Globalization
{{
/// <summary>
/// Contains a list of known culture names that can be used to create a <see cref=""System.Globalization.CultureInfo""/>.
/// </summary>
public static partial class CultureInfoCache
{{
/// <summary>
/// This list of known cultures was generated by {_appName} using .NET Framework {netFxVersion} on
/// {windowsVersion}.
/// As new versions of .NET Framework and Windows are released, this list should be regenerated to ensure it
/// contains the latest culture names.
/// </summary>
public static readonly HashSet<string> KnownCultureNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{{"
);

var cultures = CultureInfo.GetCultures(
CultureTypes.NeutralCultures
| CultureTypes.InstalledWin32Cultures
| CultureTypes.SpecificCultures);

writer.WriteLine(string.Join($",{Environment.NewLine}", cultures.Select(c => $" \"{c.Name}\"")));
writer.WriteLine(
@" };
}
}");

Console.WriteLine($"{cultures.Length} culture names written to {outputFilePath}");
}
}

// .NET Framework detection code copied from https://msdn.microsoft.com/en-us/library/hh925568%28v=vs.110%29.aspx#net_d
private static string Get45or451FromRegistry()
{
using (var ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)
.OpenSubKey("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full\\"))
{
var releaseKey = Convert.ToInt32(ndpKey.GetValue("Release"));
return CheckFor45DotVersion(releaseKey);
}
}

// Checking the version using >= will enable forward compatibility,
// however you should always compile your code on newer versions of
// the framework to ensure your app works the same.
private static string CheckFor45DotVersion(int releaseKey)
{
if (releaseKey >= 393273)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we update this to whatever .NET 4.6 RTM uses?

{
return "4.6 RC or later";
}
if (releaseKey >= 379893)
{
return "4.5.2 or later";
}
if (releaseKey >= 378675)
{
return "4.5.1 or later";
}
if (releaseKey >= 378389)
{
return "4.5 or later";
}
// This line should never execute. A non-null release key should mean
// that 4.5 or later is installed.
return "No 4.5 or later version detected";
}
}
}
17 changes: 17 additions & 0 deletions src/Microsoft.Extensions.CultureInfoGenerator.Sources/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, why is the generator a "Sources" package? Shouldn't it be a regular old console app package that can be run?

"version": "1.0.0-*",
"description": "Generates a list of known culture names from the OS using CultureInfo.GetCultures. This tool is intended to be run on Windows using full .NET Framework.",
"repository": {
"type": "git",
"url": "https://github.com/aspnet/common"
},
"dependencies": {
"Microsoft.Dnx.Runtime.Abstractions": "1.0.0-*"
},
"commands": {
"Microsoft.Extensions.CultureInfoGenerator.Sources": "Microsoft.Extensions.CultureInfoGenerator.Sources"
},
"frameworks": {
"dnx451": { }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Concurrent;
using System.Globalization;

namespace Microsoft.Extensions.Globalization
{
/// <summary>
/// Provides read-only cached instances of <see cref="CultureInfo"/>.
/// </summary>
public static partial class CultureInfoCache
{
private static readonly ConcurrentDictionary<string, CacheEntry> _cache = new ConcurrentDictionary<string, CacheEntry>();

/// <summary>
/// Gets a read-only cached <see cref="CultureInfo"/> for the specified name. Only names that exist in
/// <see cref="KnownCultureNames"/> will be used.
/// </summary>
/// <param name="name">The culture name.</param>
/// <returns>
/// A read-only cached <see cref="CultureInfo"/> or <c>null</c> a match wasn't found in
/// <see cref="KnownCultureNames"/>.
/// </returns>
public static CultureInfo GetCultureInfo(string name)
{
// Allow only known culture names as this API is called with input from users (HTTP requests) and
// creating CultureInfo objects is expensive and we don't want it to throw either.
if (name == null || !KnownCultureNames.Contains(name))
{
return null;
}

var entry = _cache.GetOrAdd(name, n =>
{
try
{
return new CacheEntry(CultureInfo.ReadOnly(new CultureInfo(n)));
}
catch (CultureNotFoundException)
{
// This can still throw as the list of culture names we have is generated from latest .NET Framework
// on latest Windows and thus contains names that won't be supported on lower framework or OS versions.
// We can just cache the null result in these cases as it's ultimately bound by the list anyway.
return new CacheEntry(cultureInfo: null);
}
});

return entry.CultureInfo;
}

private class CacheEntry
{
public CacheEntry(CultureInfo cultureInfo)
{
CultureInfo = cultureInfo;
}

public CultureInfo CultureInfo { get; }
}
}
}
Loading