diff --git a/src/Dialogs/NestingOptions.cs b/src/Dialogs/NestingOptions.cs index 29fff0b..dadf681 100644 --- a/src/Dialogs/NestingOptions.cs +++ b/src/Dialogs/NestingOptions.cs @@ -53,5 +53,11 @@ public class NestingOptions : DialogPage [Category("Nesting rules")] [DefaultValue(false)] public bool EnableInterfaceImplementationRule { get; set; } + + [LocDisplayName("Enable .resx resource file rule")] + [Description("Nest language specific resource files under the neutral resource file")] + [Category("Nesting rules")] + [DefaultValue(false)] + public bool EnableResxResourceFileRule { get; set; } } } \ No newline at end of file diff --git a/src/FileNesting.csproj b/src/FileNesting.csproj index 73021c5..e9c7916 100644 --- a/src/FileNesting.csproj +++ b/src/FileNesting.csproj @@ -166,6 +166,7 @@ + source.extension.vsixmanifest diff --git a/src/Nesters/Automated/ResxFileNester.cs b/src/Nesters/Automated/ResxFileNester.cs new file mode 100644 index 0000000..f805166 --- /dev/null +++ b/src/Nesters/Automated/ResxFileNester.cs @@ -0,0 +1,68 @@ +using System; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.IO; +using System.Linq; +using EnvDTE; + +namespace MadsKristensen.FileNesting +{ + internal class ResxFileNester : IFileNester + { + private const string _resxExtension = ".resx"; + private static readonly string[] _sortedCultureNames = GetSortedCultureNames(); + + public NestingResult Nest(string fileName) + { + string extension = Path.GetExtension(fileName); + + if (!_resxExtension.Equals(extension, StringComparison.OrdinalIgnoreCase)) + return NestingResult.Continue; + + string baseName = Path.ChangeExtension(fileName, null); + string language = Path.GetExtension(baseName); + + if (!IsValidLanguage(language)) + return NestingResult.Continue; + + string neutralFileName = Path.ChangeExtension(baseName, _resxExtension); + + ProjectItem item = FileNestingPackage.DTE.Solution.FindProjectItem(neutralFileName); + + if (item != null) + { + item.ProjectItems.AddFromFile(fileName); + return NestingResult.StopProcessing; + } + + return NestingResult.Continue; + } + + public bool IsEnabled() + { + return FileNestingPackage.Options.EnableResxResourceFileRule; + } + + private bool IsValidLanguage(string language) + { + if (string.IsNullOrEmpty(language)) + return false; + + return Array.BinarySearch(_sortedCultureNames, language.TrimStart('.'), StringComparer.OrdinalIgnoreCase) >= 0; + } + + private static string[] GetSortedCultureNames() + { + CultureInfo[] allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures); + Contract.Assume(allCultures != null); + string[] cultureNames = allCultures + .SelectMany(culture => new[] { culture.IetfLanguageTag, culture.Name }) + .Distinct() + .ToArray(); + + Array.Sort(cultureNames, StringComparer.OrdinalIgnoreCase); + + return cultureNames; + } + } +} diff --git a/src/Nesters/FileNestingFactory.cs b/src/Nesters/FileNestingFactory.cs index ee61eaa..dbf2219 100644 --- a/src/Nesters/FileNestingFactory.cs +++ b/src/Nesters/FileNestingFactory.cs @@ -18,6 +18,7 @@ static class FileNestingFactory new PathSegmentNester(), new SpriteNester(), new AddedExtensionNester(), + new ResxFileNester(), }; @@ -51,7 +52,7 @@ private static void ItemAdded(ProjectItem item) { ProjectItem parent = item.Collection.Parent as ProjectItem; - if (parent == null || parent.Kind.Equals(VSConstants.ItemTypeGuid.PhysicalFile_string, StringComparison.OrdinalIgnoreCase)) + if (parent == null || !parent.Kind.Equals(VSConstants.ItemTypeGuid.PhysicalFile_string, StringComparison.OrdinalIgnoreCase)) RunNesting(item); } catch (Exception ex)