Skip to content

Commit 492f031

Browse files
authored
Merge pull request #1422 from paulvanbrenk/initializeloc
Add localizability to Node tools
2 parents 96300be + 5164156 commit 492f031

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+6690
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration>
3+
<startup>
4+
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/>
5+
</startup>
6+
</configuration>
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/* ****************************************************************************
2+
*
3+
* Copyright (c) Microsoft Corporation.
4+
*
5+
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6+
* copy of the license can be found in the License.html file at the root of this distribution.
7+
* By using this source code in any fashion, you are agreeing to be bound by the terms of
8+
* the Apache License, Version 2.0.
9+
*
10+
* You must not remove this notice, or any other, from this software.
11+
*
12+
* ***************************************************************************/
13+
14+
using System;
15+
using System.IO;
16+
17+
namespace VsctToXliff
18+
{
19+
class Program
20+
{
21+
private static readonly string[] Locales = new[] { "cs", "de", "en", "es", "fr", "it", "ja", "ko", "pl", "pt-BR", "ru", "tr", "zh-Hans", "zh-Hant" };
22+
23+
private static int Main(string[] args)
24+
{
25+
var parsedArgs = new Args(args);
26+
if (parsedArgs.IsError)
27+
{
28+
return -1;
29+
}
30+
31+
switch (parsedArgs.Mode)
32+
{
33+
case Mode.GenerateXliff:
34+
CreateXliffFiles(parsedArgs.SourceFile, parsedArgs.XliffDir);
35+
return 0;
36+
case Mode.GenerateVsct:
37+
CreateVsctFiles(parsedArgs.SourceFile, parsedArgs.XliffDir);
38+
return 0;
39+
case Mode.Error:
40+
default:
41+
Console.WriteLine($"Unexpected processing mode: \'{parsedArgs.Mode}\'.");
42+
return -1;
43+
}
44+
}
45+
46+
private static void CreateXliffFiles(string sourceFile, string xlfDir)
47+
{
48+
if (string.IsNullOrEmpty(sourceFile) || string.IsNullOrEmpty(xlfDir))
49+
{
50+
throw new ArgumentNullException("file and targetDir should be set.");
51+
}
52+
53+
var rootName = Utilities.VsctFileNameWithoutExtension(sourceFile);
54+
55+
var reader = new VsctFile(sourceFile);
56+
var writer = new XliffFile(xlfDir, rootName);
57+
58+
foreach (var locale in Locales)
59+
{
60+
writer.WriteTranslationFile(sourceFile, reader.ReadTranslatableUnits(), locale);
61+
}
62+
}
63+
64+
private static void CreateVsctFiles(string sourceFile, string xlfDir)
65+
{
66+
if (string.IsNullOrEmpty(sourceFile))
67+
{
68+
throw new ArgumentNullException("file should be set.");
69+
}
70+
71+
var targetDir = Path.GetDirectoryName(sourceFile);
72+
var rootName = Utilities.VsctFileNameWithoutExtension(sourceFile);
73+
74+
var vsctFile = new VsctFile(sourceFile);
75+
var xlfFiles = new XliffFile(xlfDir, rootName);
76+
77+
foreach (var locale in Locales)
78+
{
79+
if (StringComparer.OrdinalIgnoreCase.Equals(locale, "en"))
80+
{
81+
// for english just copy the file to a new file name
82+
var destFileName = Path.Combine(targetDir, $"{rootName}.en{VsctFile.VsctExt}");
83+
File.Copy(sourceFile, destFileName, overwrite: true);
84+
}
85+
else
86+
{
87+
var translations = xlfFiles.LoadTranslatedElements(locale);
88+
vsctFile.WriteTranslatedFile(translations, locale);
89+
}
90+
}
91+
}
92+
93+
private class Args
94+
{
95+
public bool IsError { get; }
96+
97+
public string XliffDir { get; }
98+
99+
public string SourceFile { get; }
100+
101+
public Mode Mode { get; }
102+
103+
public Args(string[] args)
104+
{
105+
if (args.Length < 3)
106+
{
107+
this.IsError = true;
108+
DisplayHelp();
109+
}
110+
else
111+
{
112+
this.SourceFile = Utilities.EnsureRootPath(args[0]);
113+
this.XliffDir = Utilities.EnsureRootPath(args[1]);
114+
switch (args[2].ToLowerInvariant())
115+
{
116+
case "--generatexlf":
117+
this.Mode = Mode.GenerateXliff;
118+
break;
119+
case "--generatevsct":
120+
this.Mode = Mode.GenerateVsct;
121+
break;
122+
default:
123+
this.IsError = true;
124+
break;
125+
}
126+
}
127+
}
128+
129+
private void DisplayHelp()
130+
{
131+
Console.WriteLine("usage: VsctToXliff.exe <sourcefile.vsct> <xliff dir> [--generatexlf | --generatevsct].");
132+
Console.WriteLine("--generatexlf\tThis will create xlf files for all VS locales in the xliff dir, overwriting any existing files!");
133+
Console.WriteLine("--generatevsct\tThis will create vsct files for all VS locales in the same dir as the sourecfile.vsct, overwriting any existing files!");
134+
}
135+
}
136+
137+
private enum Mode
138+
{
139+
Error,
140+
GenerateXliff,
141+
GenerateVsct,
142+
}
143+
}
144+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System.Reflection;
2+
using System.Runtime.InteropServices;
3+
4+
// General Information about an assembly is controlled through the following
5+
// set of attributes. Change these attribute values to modify the information
6+
// associated with an assembly.
7+
[assembly: AssemblyTitle("VsctToXliff")]
8+
[assembly: AssemblyDescription("Tool to generate Xliff files from vsct files, and generate localized vsct files.")]
9+
[assembly: AssemblyConfiguration("")]
10+
[assembly: AssemblyCompany("Microsoft")]
11+
[assembly: AssemblyProduct("VsctToXliff")]
12+
[assembly: AssemblyCopyright("Copyright © Microsoft 2016")]
13+
[assembly: AssemblyTrademark("")]
14+
[assembly: AssemblyCulture("")]
15+
16+
// Setting ComVisible to false makes the types in this assembly not visible
17+
// to COM components. If you need to access a type in this assembly from
18+
// COM, set the ComVisible attribute to true on that type.
19+
[assembly: ComVisible(false)]
20+
21+
// The following GUID is for the ID of the typelib if this project is exposed to COM
22+
[assembly: Guid("bbf7fd3f-26ba-4f6c-8485-f221cca1cf6a")]
23+
24+
// Version information for an assembly consists of the following four values:
25+
//
26+
// Major Version
27+
// Minor Version
28+
// Build Number
29+
// Revision
30+
//
31+
// You can specify all the values or you can default the Build and Revision Numbers
32+
// by using the '*' as shown below:
33+
// [assembly: AssemblyVersion("1.0.*")]
34+
[assembly: AssemblyVersion("1.0.0.0")]
35+
[assembly: AssemblyFileVersion("1.0.0.0")]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* ****************************************************************************
2+
*
3+
* Copyright (c) Microsoft Corporation.
4+
*
5+
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6+
* copy of the license can be found in the License.html file at the root of this distribution.
7+
* By using this source code in any fashion, you are agreeing to be bound by the terms of
8+
* the Apache License, Version 2.0.
9+
*
10+
* You must not remove this notice, or any other, from this software.
11+
*
12+
* ***************************************************************************/
13+
14+
using System;
15+
using System.IO;
16+
17+
namespace VsctToXliff
18+
{
19+
internal static class Utilities
20+
{
21+
public static string EnsureRootPath(string path)
22+
{
23+
if (Path.IsPathRooted(path))
24+
{
25+
return path;
26+
}
27+
28+
return new FileInfo(Path.Combine(Environment.CurrentDirectory, path)).FullName;
29+
}
30+
31+
/// <returns>The filename without extension or locale.</returns>
32+
public static string VsctFileNameWithoutExtension(string fileName)
33+
{
34+
// assume filename have the following structure: <filename>.<locale>.vsct
35+
var file = Path.GetFileName(fileName);
36+
37+
return file.Substring(0, file.IndexOf('.'));
38+
}
39+
}
40+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/* ****************************************************************************
2+
*
3+
* Copyright (c) Microsoft Corporation.
4+
*
5+
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6+
* copy of the license can be found in the License.html file at the root of this distribution.
7+
* By using this source code in any fashion, you are agreeing to be bound by the terms of
8+
* the Apache License, Version 2.0.
9+
*
10+
* You must not remove this notice, or any other, from this software.
11+
*
12+
* ***************************************************************************/
13+
14+
using System;
15+
using System.Collections.Generic;
16+
using System.IO;
17+
using System.Xml.Linq;
18+
19+
namespace VsctToXliff
20+
{
21+
internal sealed class VsctFile
22+
{
23+
private static readonly XNamespace NS = @"http://schemas.microsoft.com/VisualStudio/2005-10-18/CommandTable";
24+
private static readonly string[] ChildNames = { "ButtonText", "ToolTipText", "MenuText", "CommandName" };
25+
26+
public const string VsctExt = ".vsct";
27+
28+
public string FileName { get; }
29+
30+
public VsctFile(string fileName)
31+
{
32+
if (string.IsNullOrEmpty(fileName))
33+
{
34+
throw new ArgumentException("fileName should be set");
35+
}
36+
37+
if (!Path.IsPathRooted(fileName))
38+
{
39+
throw new ArgumentException("Expected a rooted path");
40+
}
41+
42+
this.FileName = fileName;
43+
}
44+
45+
public IEnumerable<ITranslationUnit> ReadTranslatableUnits()
46+
{
47+
var document = XDocument.Load(this.FileName);
48+
foreach (var element in this.GetTranslatableElements(document))
49+
{
50+
var id = element.Attribute("id").Value;
51+
var strings = element.Element(NS + "Strings");
52+
53+
foreach (var name in ChildNames)
54+
{
55+
var child = strings.Element(NS + name);
56+
if (child != null)
57+
{
58+
yield return new TranslationUnit($"{id}|{name}", child.Value);
59+
}
60+
}
61+
}
62+
}
63+
64+
private IEnumerable<XElement> GetTranslatableElements(XDocument root)
65+
{
66+
foreach (var menuItem in root.Descendants(NS + "Menu"))
67+
{
68+
yield return menuItem;
69+
}
70+
foreach (var menuItem in root.Descendants(NS + "Button"))
71+
{
72+
yield return menuItem;
73+
}
74+
}
75+
76+
public void WriteTranslatedFile(IDictionary<string, string> translations, string targetLanguage)
77+
{
78+
var document = XDocument.Load(this.FileName);
79+
foreach( var element in this.GetTranslatableElements(document))
80+
{
81+
var id = element.Attribute("id").Value;
82+
var strings = element.Element(NS + "Strings");
83+
84+
foreach (var name in ChildNames)
85+
{
86+
var child = strings.Element(NS + name);
87+
if (child != null && translations.TryGetValue($"{id}|{name}", out var value))
88+
{
89+
child.Value = value;
90+
}
91+
}
92+
}
93+
94+
var rootDir = Path.GetDirectoryName(this.FileName);
95+
var rootName = Utilities.VsctFileNameWithoutExtension(this.FileName);
96+
97+
document.Save(Path.Combine(rootDir, $"{rootName}.{targetLanguage}{VsctExt}"));
98+
}
99+
100+
private sealed class TranslationUnit : ITranslationUnit
101+
{
102+
public string Key { get; }
103+
public string EnglishValue { get; }
104+
105+
public TranslationUnit(string key, string value)
106+
{
107+
this.Key = key;
108+
this.EnglishValue = value;
109+
}
110+
}
111+
}
112+
113+
internal interface ITranslationUnit
114+
{
115+
string Key { get; }
116+
string EnglishValue { get; }
117+
}
118+
}

0 commit comments

Comments
 (0)