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)