-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Language file scanning tool for EPiServer websites.
- Loading branch information
Mathias Kunto
committed
Feb 26, 2014
1 parent
aed8084
commit 0d7293b
Showing
10 changed files
with
332 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?xml version="1.0" encoding="utf-8" ?> | ||
<configuration> | ||
<startup> | ||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> | ||
</startup> | ||
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | ||
<PropertyGroup> | ||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||
<ProjectGuid>{1D6AE8D4-469C-4D9E-A1C6-DF9B38959C2F}</ProjectGuid> | ||
<OutputType>Exe</OutputType> | ||
<AppDesignerFolder>Properties</AppDesignerFolder> | ||
<RootNamespace>LanguageScanner</RootNamespace> | ||
<AssemblyName>LanguageScanner</AssemblyName> | ||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> | ||
<FileAlignment>512</FileAlignment> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||
<PlatformTarget>AnyCPU</PlatformTarget> | ||
<DebugSymbols>true</DebugSymbols> | ||
<DebugType>full</DebugType> | ||
<Optimize>false</Optimize> | ||
<OutputPath>bin\Debug\</OutputPath> | ||
<DefineConstants>DEBUG;TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||
<PlatformTarget>AnyCPU</PlatformTarget> | ||
<DebugType>pdbonly</DebugType> | ||
<Optimize>true</Optimize> | ||
<OutputPath>bin\Release\</OutputPath> | ||
<DefineConstants>TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Reference Include="NDesk.Options"> | ||
<HintPath>packages\NDesk.Options.0.2.1\lib\NDesk.Options.dll</HintPath> | ||
</Reference> | ||
<Reference Include="System" /> | ||
<Reference Include="System.Core" /> | ||
<Reference Include="System.Xml.Linq" /> | ||
<Reference Include="System.Data.DataSetExtensions" /> | ||
<Reference Include="Microsoft.CSharp" /> | ||
<Reference Include="System.Data" /> | ||
<Reference Include="System.Xml" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Compile Include="Program.cs" /> | ||
<Compile Include="Properties\AssemblyInfo.cs" /> | ||
<Compile Include="Translation.cs" /> | ||
<Compile Include="XElementExtensions.cs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<None Include="App.config" /> | ||
<None Include="packages.config" /> | ||
</ItemGroup> | ||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | ||
Other similar extension points exist, see Microsoft.Common.targets. | ||
<Target Name="BeforeBuild"> | ||
</Target> | ||
<Target Name="AfterBuild"> | ||
</Target> | ||
--> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio 2012 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LanguageScanner", "LanguageScanner.csproj", "{1D6AE8D4-469C-4D9E-A1C6-DF9B38959C2F}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{1D6AE8D4-469C-4D9E-A1C6-DF9B38959C2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{1D6AE8D4-469C-4D9E-A1C6-DF9B38959C2F}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{1D6AE8D4-469C-4D9E-A1C6-DF9B38959C2F}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{1D6AE8D4-469C-4D9E-A1C6-DF9B38959C2F}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Xml.Linq; | ||
using NDesk.Options; | ||
|
||
namespace LanguageScanner | ||
{ | ||
class Program | ||
{ | ||
private static readonly Stopwatch StopWatch = new Stopwatch(); | ||
private static string[] _excludeExtensions = | ||
{ | ||
"dll", "xml", "pdb", "csproj", "designer.cs", "config", "cache", "swf", "css", | ||
"js", "jpg", "gif", "png", "bmp", "psd", "html", "orig", "txt", "fla", "pdf" | ||
}; | ||
private static bool _ignoreCase = false; | ||
private static bool _distinctTranslations = false; | ||
|
||
static void Main(string[] args) | ||
{ | ||
var rootPath = Directory.GetCurrentDirectory(); | ||
var langPath = string.Concat(Directory.GetCurrentDirectory(), @"\lang"); | ||
var printHelpAndExit = false; | ||
|
||
var options = new OptionSet | ||
{ | ||
{ "rp|rootPath=", string.Format("Path to the directory containing your source code; default is '{0}'", rootPath), arg => rootPath = arg }, | ||
{ "lp|langPath=", string.Format("Path to the directory containing your language files; default is '{0}'", langPath), arg => langPath = arg }, | ||
{ "ee|excludeExtensions=", string.Format("File extensions to exclude in code scan; default are {0}", string.Join(",", _excludeExtensions)), arg => _excludeExtensions = arg == null ? _excludeExtensions : arg.Split(',') }, | ||
{ "ic|ignoreCase", string.Format("Makes the scan case insensitive, increases working time; default is '{0}'", _ignoreCase.ToString().ToLower()), arg => _ignoreCase = arg != null }, | ||
{ "dt|distinctTranslations", string.Format("Remove duplicate XPaths, scan faster but file list will be inaccurate; default is '{0}'", _distinctTranslations), arg => _distinctTranslations = arg != null }, | ||
{ "h|?|help", "Shows the help", arg => printHelpAndExit = arg != null }, | ||
}; | ||
try | ||
{ | ||
var unknown = options.Parse(args); | ||
if (unknown.Any() || printHelpAndExit) | ||
{ | ||
options.WriteOptionDescriptions(Console.Out); | ||
Environment.Exit(0); | ||
} | ||
StopWatch.Start(); | ||
|
||
Console.Out.WriteLine("--- Starting to scan language files for translations ---"); | ||
var translations = ScanLanguageFilesIn(langPath).ToList(); | ||
var langPathTime = StopWatch.Elapsed; | ||
Console.Out.WriteLine("Language files scanned in: {0}", langPathTime.ToString("g")); | ||
Console.Out.WriteLine("Number of translations: {0}", translations.Count()); | ||
|
||
Console.Out.WriteLine("--- Starting to retrieve file paths for code files ---"); | ||
var filePaths = FilePathsIn(rootPath).ToArray(); | ||
var getFilePathsTime = StopWatch.Elapsed; | ||
Console.Out.WriteLine("File paths retrieved in: {0}", getFilePathsTime.ToString("g")); | ||
Console.Out.WriteLine("Number of file paths: {0}", filePaths.Count()); | ||
|
||
Console.Out.WriteLine("--- Starting to scan for orphans ---"); | ||
|
||
var possibleOrphans = translations.ConvertAll(t=>t); | ||
foreach (var path in filePaths) | ||
{ | ||
Console.Out.WriteLine("Starting to scan file: {0}", path); | ||
var content = _ignoreCase ? File.ReadAllText(path).ToLower() : File.ReadAllText(path); | ||
|
||
var usedTranslations = translations.Where(translation => content.Contains(translation.XPath)); | ||
foreach (var translation in usedTranslations) | ||
{ | ||
possibleOrphans.Remove(translation); | ||
} | ||
|
||
Console.Out.WriteLine("Done! Possible orphas now: {0}", possibleOrphans.Count()); | ||
} | ||
|
||
var scanCompleteTime = StopWatch.Elapsed; | ||
StopWatch.Stop(); | ||
|
||
foreach (var orphan in possibleOrphans) | ||
{ | ||
Console.Out.WriteLine("{0}: {1} ({2})", orphan.File, orphan.XPath, orphan.Content); | ||
} | ||
Console.Out.WriteLine("**************************************"); | ||
Console.Out.WriteLine("Language files scanned in: {0}", langPathTime.ToString("g")); | ||
Console.Out.WriteLine("File paths retrieved in: {0}", getFilePathsTime.ToString("g")); | ||
Console.Out.WriteLine("Scan compleated in: {0}", scanCompleteTime.ToString("g")); | ||
Console.Out.WriteLine("Possible orphans found: {0}", possibleOrphans.Count); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Console.Error.WriteLine(ex); | ||
} | ||
} | ||
|
||
private static IEnumerable<string> FilePathsIn(string currentDirectory) | ||
{ | ||
var paths = Directory.GetFiles(currentDirectory) | ||
.Where(file => !ShouldExclude(file)) | ||
.ToList(); | ||
foreach (var subDirectory in Directory.GetDirectories(currentDirectory)) | ||
{ | ||
paths.AddRange(FilePathsIn(subDirectory)); | ||
} | ||
return paths; | ||
} | ||
|
||
private static bool ShouldExclude(string file) | ||
{ | ||
return _excludeExtensions.Any(extension => file.ToLower().EndsWith(extension.ToLower())); | ||
} | ||
|
||
private static IEnumerable<Translation> ScanLanguageFilesIn(string langPath) | ||
{ | ||
var translations = new List<Translation>(); | ||
var paths = Directory.GetFiles(langPath) | ||
.Where(file => file.EndsWith(".xml")) | ||
.ToArray(); | ||
foreach (var file in paths) | ||
{ | ||
translations.AddRange(TranslationsIn(file)); | ||
} | ||
return _distinctTranslations ? translations.Distinct() : translations; | ||
} | ||
|
||
private static IEnumerable<Translation> TranslationsIn(string file) | ||
{ | ||
var doc = XDocument.Load(file); | ||
var leaves = from e in doc.Descendants() where !e.Elements().Any() select e; | ||
return leaves | ||
.Select(leaf => new Translation | ||
{ | ||
File = file, | ||
XPath = _ignoreCase ? leaf.XPath().ToLower() : leaf.XPath(), | ||
Content = leaf.Value | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System.Reflection; | ||
using System.Runtime.InteropServices; | ||
|
||
// General Information about an assembly is controlled through the following | ||
// set of attributes. Change these attribute values to modify the information | ||
// associated with an assembly. | ||
[assembly: AssemblyTitle("EPiServer Language Scanner")] | ||
[assembly: AssemblyDescription("Scans language files for translations and then looks through source code for usages of these")] | ||
[assembly: AssemblyConfiguration("")] | ||
[assembly: AssemblyCompany("")] | ||
[assembly: AssemblyProduct("LanguageScanner")] | ||
[assembly: AssemblyCopyright("Copyright © 2014")] | ||
[assembly: AssemblyTrademark("")] | ||
[assembly: AssemblyCulture("")] | ||
|
||
// Setting ComVisible to false makes the types in this assembly not visible | ||
// to COM components. If you need to access a type in this assembly from | ||
// COM, set the ComVisible attribute to true on that type. | ||
[assembly: ComVisible(false)] | ||
|
||
// The following GUID is for the ID of the typelib if this project is exposed to COM | ||
[assembly: Guid("02472c42-7ca5-4faa-acd1-67976c12ddd4")] | ||
|
||
// Version information for an assembly consists of the following four values: | ||
// | ||
// Major Version | ||
// Minor Version | ||
// Build Number | ||
// Revision | ||
// | ||
// You can specify all the values or you can default the Build and Revision Numbers | ||
// by using the '*' as shown below: | ||
// [assembly: AssemblyVersion("1.0.*")] | ||
[assembly: AssemblyVersion("1.0.0.0")] | ||
[assembly: AssemblyFileVersion("1.0.0.0")] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
Language File Editor version 1.0 for EPiServer CMS 6 R2 | ||
http://blog.mathiaskunto.com/rss | ||
|
||
Installation instructions: | ||
|
||
* Drop the files in your solution. | ||
* Update namespaces where necessary. | ||
* Update JavaScript and CSS paths in LanguageFileEditor.aspx if necessary. | ||
* Update Plugin path in LanguageFileEditor.aspx.cs if necessary. | ||
* Update LoadControl path in EditableXmlNode.ascx.cs if necessary. | ||
* Reference Newtonsoft.Json.Net35.dll or later (Available at http://json.codeplex.com/releases/view/64935) | ||
|
||
More information at: | ||
http://blog.mathiaskunto.com/2011/09/04/allowing-web-administrators-to-dynamically-update-episerver-language-files | ||
|
||
Well formulated bug reports and constructive feedback is always welcome. | ||
|
||
Disclaimer: | ||
Always make backups. You are using this at your own risk, | ||
and cannot hold the author responsible for anything that this may cause. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using System; | ||
|
||
namespace LanguageScanner | ||
{ | ||
public class Translation : IComparable<Translation> | ||
{ | ||
public string File { get; set; } | ||
public string XPath { get; set; } | ||
public string Content { get; set; } | ||
|
||
public int CompareTo(Translation other) | ||
{ | ||
return String.Compare(this.XPath, other.XPath, StringComparison.Ordinal); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
using System.Xml.Linq; | ||
|
||
namespace LanguageScanner | ||
{ | ||
public static class XElementExtensions | ||
{ | ||
public static string XPath(this XElement element) | ||
{ | ||
var tmp = element; | ||
var path = string.Empty; | ||
while (tmp != null) | ||
{ | ||
path = string.Concat("/", tmp.Name, path); | ||
tmp = tmp.Parent; | ||
} | ||
|
||
// EPiServer's language files starts with <languages><language>, and we don't want those in our paths. | ||
var startIndex = "/languages/language".Length; | ||
return path.Substring(startIndex, path.Length - startIndex); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<packages> | ||
<package id="NDesk.Options" version="0.2.1" targetFramework="net45" /> | ||
</packages> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters