private static void PrintLogo(LocBamlOptions option) { if (option == null || !option.HasNoLogo) { Console.WriteLine(StringLoader.Get("Msg_Copyright", GetAssemblyVersion())); } }
internal static BamlLocalizableResourceKey StringToResourceKey(string value) { int nameEnd = value.LastIndexOf(':'); if (nameEnd < 0) { throw new ArgumentException(StringLoader.Get("ResourceKeyFormatError")); } string name = value.Substring(0, nameEnd); int classEnd = value.LastIndexOf('.'); if (classEnd < 0 || classEnd < nameEnd || classEnd == value.Length) { throw new ArgumentException(StringLoader.Get("ResourceKeyFormatError")); } string className = value.Substring(nameEnd + 1, classEnd - nameEnd - 1); string propertyName = value.Substring(classEnd + 1); return(new BamlLocalizableResourceKey( name, className, propertyName )); }
/// <summary> /// Write the localizable key-value pairs /// </summary> /// <param name="options"></param> internal static void Write(LocBamlOptions options) { InputBamlStreamList bamlStreamList = new InputBamlStreamList(options); using (ITranslationWriter writer = options.GetTranslationWriter()) { options.WriteLine(StringLoader.Get("WriteBamlValues")); for (int i = 0; i < bamlStreamList.Count; i++) { options.Write(" "); options.Write(StringLoader.Get("ProcessingBaml", bamlStreamList[i].Name)); // Search for comment file in the same directory. The comment file has the extension to be // "loc". string commentFile = Path.ChangeExtension(bamlStreamList[i].Name, "loc"); TextReader commentStream = null; try { if (File.Exists(commentFile)) { commentStream = new StreamReader(commentFile); } // create the baml localizer BamlLocalizer mgr = new BamlLocalizer( bamlStreamList[i].Stream, new BamlLocalizabilityByReflection(options.Assemblies), commentStream ); // extract localizable resource from the baml stream BamlLocalizationDictionary dict = mgr.ExtractResources(); // write out each resource foreach (DictionaryEntry entry in dict) { BamlLocalizableResourceKey key = (BamlLocalizableResourceKey)entry.Key; BamlLocalizableResource resource = (BamlLocalizableResource)entry.Value; writer.WriteResource(bamlStreamList[i].Name, LocBamlConst.ResourceKeyToString(key), resource); } options.WriteLine(StringLoader.Get("Done")); } finally { if (commentStream != null) { commentStream.Close(); } } } // close all the baml input streams, output stream is closed by writer. bamlStreamList.Close(); } }
public static int Main(string[] args) { //Debugger.Break(); AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; LocBamlOptions options; string errorMessage; GetCommandLineOptions(args, out options, out errorMessage); if (errorMessage != null) { // there are errors PrintLogo(options); Console.WriteLine(StringLoader.Get("ErrorMessage", errorMessage)); Console.WriteLine(); PrintUsage(); return(ErrorCode); // error } // at this point, we obtain good options. if (options == null) { // no option to process. Noop. return(SuccessCode); } _dispatcher = Dispatcher.CurrentDispatcher; PrintLogo(options); try{ // it is to parse if (options.ToParse) { ParseBamlResources(options); } else { GenerateBamlResources(options); } } catch (Exception e) { #if DEBUG throw e; #else Console.WriteLine(e.Message); return(ErrorCode); #endif } return(SuccessCode); }
public static int Main(string[] args) { //register "pack://" uri System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Application).TypeHandle); LocBamlOptions options; string errorMessage; GetCommandLineOptions(args, out options, out errorMessage); if (errorMessage != null) { // there are errors PrintLogo(options); Console.WriteLine(StringLoader.Get("ErrorMessage", errorMessage)); Console.WriteLine(); PrintUsage(); return(ErrorCode); // error } // at this point, we obtain good options. if (options == null) { // no option to process. Noop. return(SuccessCode); } _dispatcher = Dispatcher.CurrentDispatcher; PrintLogo(options); try{ // it is to parse if (options.ToParse) { ParseBamlResources(options); } else { GenerateBamlResources(options); } } catch (Exception e) { #if DEBUG throw e; #else Console.WriteLine(e.Message); return(ErrorCode); #endif } return(SuccessCode); }
public String Lookup(String strOpt, out bool bRequiresValue, out bool bCanHaveValue) { String strOptLower = strOpt.ToLower(CultureInfo.InvariantCulture); int i; bool bMatched = false; int iMatch = -1; // Compare option to stored list. for (i = 0; i < m_aOptions.Length; i++) { // Exact matches always cause immediate termination of // the search if (strOptLower.Equals(m_aOptions[i])) { bRequiresValue = m_bRequiresValue[i]; bCanHaveValue = m_bCanHaveValue[i]; return(m_aOptions[i]); } // Check for potential match (the input word is a prefix // of the current stored option). if (m_aOptions[i].StartsWith(strOptLower)) { // If we've already seen a prefix match then the // input word is ambiguous. if (bMatched) { throw new ArgumentException(StringLoader.Get("Err_AmbigousOption", strOpt)); } // Remember this partial match. bMatched = true; iMatch = i; } } // If we get here with bMatched set, we saw one and only one // partial match, so we've got a winner. if (bMatched) { bRequiresValue = m_bRequiresValue[iMatch]; bCanHaveValue = m_bCanHaveValue[iMatch]; return(m_aOptions[iMatch]); } // Else the word doesn't match at all. throw new ArgumentException(StringLoader.Get("Err_UnknownOption", strOpt)); }
public CommandLine(String[] aArgs, String[] aValidOpts) { int i, iArg, iOpt; // Keep a list of valid option names. m_sValidOptions = new Abbrevs(aValidOpts); // Temporary lists of raw arguments and options and their // associated values. String[] aArgList = new String[aArgs.Length]; Option[] aOptList = new Option[aArgs.Length]; // Reset counters of raw arguments and option/value pairs found // so far. iArg = 0; iOpt = 0; // Iterate through words of command line. for (i = 0; i < aArgs.Length; i++) { // Check for option or raw argument. if (aArgs[i].StartsWith("/") || aArgs[i].StartsWith("-")) { String strOpt; String strVal = null; bool bRequiresValue; bool bCanHaveValue; // It's an option. Strip leading '/' or '-' and // anything after a value separator (':' or // '='). int iColon = aArgs[i].IndexOfAny(new char[] { ':', '=' }); if (iColon == -1) { strOpt = aArgs[i].Substring(1); } else { strOpt = aArgs[i].Substring(1, iColon - 1); } // Look it up in the table of valid options (to // check it exists, get the full option name and // to see if an associated value is expected). strOpt = m_sValidOptions.Lookup(strOpt, out bRequiresValue, out bCanHaveValue); // Check that the user hasn't specified a value separator for an option // that doesn't take a value. if (!bCanHaveValue && (iColon != -1)) { throw new ApplicationException(StringLoader.Get("Err_NoValueRequired", strOpt)); } // Check that the user has put a colon if the option requires a value. if (bRequiresValue && (iColon == -1)) { throw new ApplicationException(StringLoader.Get("Err_ValueRequired", strOpt)); } // Go look for a value if there is one. if (bCanHaveValue && iColon != -1) { if (iColon == (aArgs[i].Length - 1)) { // No value separator, or // separator is at end of // option; look for value in // next command line arg. if (i + 1 == aArgs.Length) { throw new ApplicationException(StringLoader.Get("Err_ValueRequired", strOpt)); } else { if ((aArgs[i + 1].StartsWith("/") || aArgs[i + 1].StartsWith("-"))) { throw new ApplicationException(StringLoader.Get("Err_ValueRequired", strOpt)); } strVal = aArgs[i + 1]; i++; } } else { // Value is in same command line // arg as the option, substring // it out. strVal = aArgs[i].Substring(iColon + 1); } } // Build the option value pair. aOptList[iOpt++] = new Option(strOpt, strVal); } else { // Command line word is a raw argument. aArgList[iArg++] = aArgs[i]; } } // Allocate the non-temporary arg and option lists at exactly // the right size. m_aArgList = new String[iArg]; m_aOptList = new Option[iOpt]; // Copy in the values we've calculated. Array.Copy(aArgList, m_aArgList, iArg); Array.Copy(aOptList, m_aOptList, iOpt); }
/// <summary> /// Generates localized Baml from translations /// </summary> /// <param name="options">LocBaml options</param> /// <param name="dictionaries">the translation dictionaries</param> internal static string Generate(LocBamlOptions options, TranslationDictionariesReader dictionaries) { // base on the input, we generate differently switch (options.InputType) { case FileType.BAML: { // input file name string bamlName = Path.GetFileName(options.Input); // output file name is output dir + input file name string outputFileName = GetOutputFileName(options); // construct the full path string fullPathOutput = Path.Combine(options.Output, outputFileName); options.Write(StringLoader.Get("GenerateBaml", fullPathOutput)); using (Stream input = File.OpenRead(options.Input)) { using (Stream output = new FileStream(fullPathOutput, FileMode.Create)) { BamlLocalizationDictionary dictionary = dictionaries[bamlName]; // if it is null, just create an empty dictionary. if (dictionary == null) { dictionary = new BamlLocalizationDictionary(); } string status = GenerateBamlStream(input, output, dictionary, options); if (status != null) { return(status); } } } options.WriteLine(StringLoader.Get("Done")); break; } case FileType.RESOURCES: { string outputFileName = GetOutputFileName(options); string fullPathOutput = Path.Combine(options.Output, outputFileName); using (Stream input = File.OpenRead(options.Input)) { using (Stream output = File.OpenWrite(fullPathOutput)) { // create a Resource reader on the input; IResourceReader reader = new ResourceReader(input); // create a writer on the output; IResourceWriter writer = new ResourceWriter(output); string status = GenerateResourceStream( options, // options options.Input, // resources name reader, // resource reader writer, // resource writer dictionaries); // translations reader.Close(); if (status != null) { return(status); } // now generate and close writer.Generate(); writer.Close(); } } options.WriteLine(StringLoader.Get("DoneGeneratingResource", outputFileName)); break; } case FileType.EXE: case FileType.DLL: { string status = GenerateAssembly(options, dictionaries); if (status != null) { return(status); } break; } default: { Debug.Assert(false, "Can't generate to this type"); return("Can't generate to a resource of the type EXE"); break; } } return(null); }
//-------------------------------------------------- // The function follows Managed code parser // implementation. in the future, maybe they should // share the same code //-------------------------------------------------- private static string GenerateAssembly(LocBamlOptions options, TranslationDictionariesReader dictionaries) { // there are many names to be used when generating an assembly string sourceAssemblyFullName = options.Input; // source assembly full path string outputAssemblyDir = options.Output; // output assembly directory string outputAssemblyLocalName = GetOutputFileName(options); // output assembly name string moduleLocalName = GetAssemblyModuleLocalName(options, outputAssemblyLocalName); // the module name within the assembly // get the source assembly Assembly srcAsm = Assembly.LoadFrom(sourceAssemblyFullName); // obtain the assembly name AssemblyName targetAssemblyNameObj = srcAsm.GetName(); // store the culture info of the source assembly CultureInfo srcCultureInfo = targetAssemblyNameObj.CultureInfo; // update it to use it for target assembly targetAssemblyNameObj.Name = Path.GetFileNameWithoutExtension(outputAssemblyLocalName); targetAssemblyNameObj.CultureInfo = options.CultureInfo; // we get a assembly builder AssemblyBuilder targetAssemblyBuilder = Thread.GetDomain().DefineDynamicAssembly( targetAssemblyNameObj, // name of the assembly AssemblyBuilderAccess.RunAndSave, // access rights outputAssemblyDir // storage dir ); // we create a module builder for embedded resource modules ModuleBuilder moduleBuilder = targetAssemblyBuilder.DefineDynamicModule( moduleLocalName, outputAssemblyLocalName ); options.WriteLine(StringLoader.Get("GenerateAssembly")); // now for each resource in the assembly foreach (string resourceName in srcAsm.GetManifestResourceNames()) { // get the resource location for the resource ResourceLocation resourceLocation = srcAsm.GetManifestResourceInfo(resourceName).ResourceLocation; // if this resource is in another assembly, we will skip it if ((resourceLocation & ResourceLocation.ContainedInAnotherAssembly) != 0) { continue; // in resource assembly, we don't have resource that is contained in another assembly } // gets the neutral resource name, giving it the source culture info string neutralResourceName = GetNeutralResModuleName(resourceName, srcCultureInfo); // gets the target resource name, by giving it the target culture info string targetResourceName = GetCultureSpecificResourceName(neutralResourceName, options.CultureInfo); // resource stream Stream resourceStream = srcAsm.GetManifestResourceStream(resourceName); // see if it is a .resources if (neutralResourceName.ToLower(CultureInfo.InvariantCulture).EndsWith(".resources")) { // now we think we have resource stream // get the resource writer IResourceWriter writer; // check if it is a embedded assembly if ((resourceLocation & ResourceLocation.Embedded) != 0) { // gets the resource writer from the module builder writer = moduleBuilder.DefineResource( targetResourceName, // resource name targetResourceName, // resource description ResourceAttributes.Public // visibility of this resource to other assembly ); } else { // it is a standalone resource, we get the resource writer from the assembly builder writer = targetAssemblyBuilder.DefineResource( targetResourceName, // resource name targetResourceName, // description targetResourceName, // file name to save to ResourceAttributes.Public // visibility of this resource to other assembly ); } // get the resource reader IResourceReader reader = new ResourceReader(resourceStream); // generate the resources string status = GenerateResourceStream(options, resourceName, reader, writer, dictionaries); if (status != null) { return(status); } // we don't call writer.Generate() or writer.Close() here // because the AssemblyBuilder will call them when we call Save() on it. } else { // else it is a stand alone untyped manifest resources. string extension = Path.GetExtension(targetResourceName); string fullFileName = Path.Combine(outputAssemblyDir, targetResourceName); // check if it is a .baml, case-insensitive if (string.Compare(extension, ".baml", true, CultureInfo.InvariantCulture) == 0) { // try to localized the baml // find the resource dictionary BamlLocalizationDictionary dictionary = dictionaries[resourceName]; // if it is null, just create an empty dictionary. if (dictionary != null) { // it is a baml stream using (Stream output = File.OpenWrite(fullFileName)) { options.Write(" "); options.WriteLine(StringLoader.Get("GenerateStandaloneBaml", fullFileName)); GenerateBamlStream(resourceStream, output, dictionary, options); options.WriteLine(StringLoader.Get("Done")); } } else { // can't find localization of it, just copy it GenerateStandaloneResource(fullFileName, resourceStream); } } else { // it is an untyped resource stream, just copy it GenerateStandaloneResource(fullFileName, resourceStream); } // now add this resource file into the assembly targetAssemblyBuilder.AddResourceFile( targetResourceName, // resource name targetResourceName, // file name ResourceAttributes.Public // visibility of the resource to other assembly ); } } // at the end, generate the assembly targetAssemblyBuilder.Save(outputAssemblyLocalName); options.WriteLine(StringLoader.Get("DoneGeneratingAssembly")); return(null); }
private static string GenerateResourceStream( LocBamlOptions options, // options from the command line string resourceName, // the name of the .resources file IResourceReader reader, // the reader for the .resources IResourceWriter writer, // the writer for the output .resources TranslationDictionariesReader dictionaries // the translations ) { options.WriteLine(StringLoader.Get("GenerateResource", resourceName)); // enumerate through each resource and generate it foreach (DictionaryEntry entry in reader) { string name = entry.Key as string; object resourceValue = null; // See if it looks like a Baml resource if (BamlStream.IsResourceEntryBamlStream(name, entry.Value)) { Stream targetStream = null; options.Write(" "); options.Write(StringLoader.Get("GenerateBaml", name)); // grab the localizations available for this Baml string bamlName = BamlStream.CombineBamlStreamName(resourceName, name); BamlLocalizationDictionary localizations = dictionaries[bamlName]; if (localizations != null) { targetStream = new MemoryStream(); // generate into a new Baml stream string status = GenerateBamlStream( (Stream)entry.Value, targetStream, localizations, options ); if (status != null) { options.WriteLine(status); return(status); } } options.WriteLine(StringLoader.Get("Done")); // sets the generated object to be the generated baml stream resourceValue = targetStream; } if (resourceValue == null) { // // The stream is not localized as Baml yet, so we will make a copy of this item into // the localized resources // // We will add the value as is if it is serializable. Otherwise, make a copy resourceValue = entry.Value; object[] serializableAttributes = resourceValue.GetType().GetCustomAttributes(typeof(SerializableAttribute), true); if (serializableAttributes.Length == 0) { // The item returned from resource reader is not serializable // If it is Stream, we can wrap all the values in a MemoryStream and // add to the resource. Otherwise, we had to skip this resource. Stream resourceStream = resourceValue as Stream; if (resourceStream != null) { Stream targetStream = new MemoryStream(); byte[] buffer = new byte[resourceStream.Length]; resourceStream.Read(buffer, 0, buffer.Length); targetStream = new MemoryStream(buffer); resourceValue = targetStream; } } } if (resourceValue != null) { writer.AddResource(name, resourceValue); } } return(null); }
//-------------------------------------------------- // The function follows Managed code parser // implementation. in the future, maybe they should // share the same code //-------------------------------------------------- private static void GenerateAssembly(LocBamlOptions options, TranslationDictionariesReader dictionaries) { // there are many names to be used when generating an assembly string sourceAssemblyFullName = options.Input; // source assembly full path string outputAssemblyDir = options.Output; // output assembly directory string outputAssemblyLocalName = GetOutputFileName(options); // output assembly name string moduleLocalName = GetAssemblyModuleLocalName(options, outputAssemblyLocalName); // the module name within the assmbly // get the source assembly byte[] sourceContents = File.ReadAllBytes(sourceAssemblyFullName); Assembly srcAsm = Assembly.Load(sourceContents); // obtain the assembly name AssemblyName targetAssemblyNameObj = srcAsm.GetName(); // store the culture info of the source assembly CultureInfo srcCultureInfo = targetAssemblyNameObj.CultureInfo; // update it to use it for target assembly targetAssemblyNameObj.Name = Path.GetFileNameWithoutExtension(outputAssemblyLocalName); targetAssemblyNameObj.CultureInfo = options.CultureInfo; // we get a assembly builder AssemblyBuilder targetAssemblyBuilder = Thread.GetDomain().DefineDynamicAssembly( targetAssemblyNameObj, // name of the assembly AssemblyBuilderAccess.RunAndSave, // access rights outputAssemblyDir // storage dir ); // we create a module builder for embeded resource modules ModuleBuilder moduleBuilder = targetAssemblyBuilder.DefineDynamicModule( moduleLocalName, outputAssemblyLocalName ); Dictionary <string, IResourceWriter> resourceWriters = new Dictionary <string, IResourceWriter>(); // If the output assembly already exists, copy the embedded resources to the new assembly string existingAssemblyName = Path.Combine(Directory.GetCurrentDirectory(), options.CultureInfo.Name, outputAssemblyLocalName); if (File.Exists(existingAssemblyName)) { // Use ReadAllBytes() so we don't hold a file handle open, which would prevent // us from overwriting the file at the end. Assembly existingAssembly = Assembly.Load(File.ReadAllBytes(existingAssemblyName)); string[] existingResourceNames = existingAssembly.GetManifestResourceNames(); foreach (string resourceName in existingResourceNames) { ManifestResourceInfo info = existingAssembly.GetManifestResourceInfo(resourceName); if ((info.ResourceLocation & ResourceLocation.Embedded) != ResourceLocation.Embedded) { continue; } IResourceWriter writer; if (!resourceWriters.TryGetValue(resourceName, out writer)) { writer = moduleBuilder.DefineResource( resourceName, // resource name resourceName, // resource description ResourceAttributes.Public // visibilty of this resource to other assembly ); resourceWriters.Add(resourceName, writer); } Stream resourceStream = existingAssembly.GetManifestResourceStream(resourceName); using (ResourceReader reader = new ResourceReader(resourceStream)) { foreach (DictionaryEntry entry in reader) { string key = entry.Key.ToString(); object value = entry.Value; if (key.EndsWith(".baml")) { // Skip it, we're going to get this from the untranslated assembly continue; } writer.AddResource(key, value); } } } } // Add assembly info, trying to preserver original values as close as possible CopyAssemblyVersion(targetAssemblyBuilder, srcAsm); options.WriteLine(StringLoader.Get("GenerateAssembly")); // now for each resource in the assembly foreach (string resourceName in srcAsm.GetManifestResourceNames()) { // get the resource location for the resource ResourceLocation resourceLocation = srcAsm.GetManifestResourceInfo(resourceName).ResourceLocation; // if this resource is in another assemlby, we will skip it if ((resourceLocation & ResourceLocation.ContainedInAnotherAssembly) != 0) { continue; // in resource assembly, we don't have resource that is contained in another assembly } // gets the neutral resource name, giving it the source culture info string neutralResourceName = GetNeutralResModuleName(resourceName, srcCultureInfo); // gets the target resource name, by giving it the target culture info string targetResourceName = GetCultureSpecificResourceName(neutralResourceName, options.CultureInfo); // resource stream Stream resourceStream = srcAsm.GetManifestResourceStream(resourceName); // see if it is a .resources if (neutralResourceName.ToLower(CultureInfo.InvariantCulture).EndsWith(".resources")) { // now we think we have resource stream // get the resource writer IResourceWriter writer; // check if it is a embeded assembly if (!resourceWriters.TryGetValue(targetResourceName, out writer)) { if ((resourceLocation & ResourceLocation.Embedded) != 0) { // gets the resource writer from the module builder writer = moduleBuilder.DefineResource( targetResourceName, // resource name targetResourceName, // resource description ResourceAttributes.Public // visibilty of this resource to other assembly ); } else { // it is a standalone resource, we get the resource writer from the assembly builder writer = targetAssemblyBuilder.DefineResource( targetResourceName, // resource name targetResourceName, // description targetResourceName, // file name to save to ResourceAttributes.Public // visibility of this resource to other assembly ); } resourceWriters.Add(targetResourceName, writer); } // get the resource reader IResourceReader reader = new ResourceReader(resourceStream); // generate the resources GenerateResourceStream(options, resourceName, reader, writer, dictionaries); // we don't call writer.Generate() or writer.Close() here // because the AssemblyBuilder will call them when we call Save() on it. } else { // else it is a stand alone untyped manifest resources. string extension = Path.GetExtension(targetResourceName); string fullFileName = Path.Combine(outputAssemblyDir, targetResourceName); // check if it is a .baml, case-insensitive if (string.Compare(extension, ".baml", true, CultureInfo.InvariantCulture) == 0) { // try to localized the the baml // find the resource dictionary BamlLocalizationDictionary dictionary = dictionaries[resourceName]; // if it is null, just create an empty dictionary. if (dictionary != null) { // it is a baml stream using (Stream output = File.OpenWrite(fullFileName)) { options.Write(" "); options.WriteLine(StringLoader.Get("GenerateStandaloneBaml", fullFileName)); GenerateBamlStream(resourceStream, output, dictionary, options); options.WriteLine(StringLoader.Get("Done")); } } else { // can't find localization of it, just copy it GenerateStandaloneResource(fullFileName, resourceStream); } } else { // it is an untyped resource stream, just copy it GenerateStandaloneResource(fullFileName, resourceStream); } // now add this resource file into the assembly targetAssemblyBuilder.AddResourceFile( targetResourceName, // resource name targetResourceName, // file name ResourceAttributes.Public // visibility of the resource to other assembly ); } } // at the end, generate the assembly targetAssemblyBuilder.Save(outputAssemblyLocalName); options.WriteLine(StringLoader.Get("DoneGeneratingAssembly")); }
/// <summary> /// return true if the operation succeeded. /// otherwise, return false /// </summary> internal string CheckAndSetDefault() { // we validate the options here and also set default // if we can // Rule #1: One and only one action at a time // i.e. Can't parse and generate at the same time // Must do one of them if ((ToParse && ToGenerate) || (!ToParse && !ToGenerate)) { return(StringLoader.Get("MustChooseOneAction")); } // Rule #2: Must have an input if (string.IsNullOrEmpty(Input)) { return(StringLoader.Get("InputFileRequired")); } else { if (!File.Exists(Input)) { return(StringLoader.Get("FileNotFound", Input)); } string extension = Path.GetExtension(Input); // Get the input file type. if (string.Compare(extension, "." + FileType.BAML.ToString(), true, CultureInfo.InvariantCulture) == 0) { InputType = FileType.BAML; } else if (string.Compare(extension, "." + FileType.RESOURCES.ToString(), true, CultureInfo.InvariantCulture) == 0) { InputType = FileType.RESOURCES; } else if (string.Compare(extension, "." + FileType.DLL.ToString(), true, CultureInfo.InvariantCulture) == 0) { InputType = FileType.DLL; } else if (string.Compare(extension, "." + FileType.EXE.ToString(), true, CultureInfo.InvariantCulture) == 0) { InputType = FileType.EXE; } else { return(StringLoader.Get("FileTypeNotSupported", extension)); } } if (ToGenerate) { // Rule #3: before generation, we must have Culture string if (CultureInfo == null && InputType != FileType.BAML) { // if we are not generating baml, return(StringLoader.Get("CultureNameNeeded", InputType.ToString())); } // Rule #4: before generation, we must have translation file if (string.IsNullOrEmpty(Translations)) { return(StringLoader.Get("TranslationNeeded")); } else { string extension = Path.GetExtension(Translations); if (!File.Exists(Translations)) { return(StringLoader.Get("TranslationNotFound", Translations)); } else { if (string.Compare(extension, "." + FileType.CSV.ToString(), true, CultureInfo.InvariantCulture) == 0) { TranslationFileType = FileType.CSV; } else { TranslationFileType = FileType.TXT; } } } } // Rule #5: If the output file name is empty, we act accordingly if (string.IsNullOrEmpty(Output)) { // Rule #5.1: If it is parse, we default to [input file name].csv if (ToParse) { string fileName = Path.GetFileNameWithoutExtension(Input); Output = fileName + "." + FileType.CSV.ToString(); TranslationFileType = FileType.CSV; } else { // Rule #5.2: If it is generating, and the output can't be empty return(StringLoader.Get("OutputDirectoryNeeded")); } } else { // output isn't null, we will determind the Output file type // Rule #6: if it is parsing. It will be .csv or .txt. if (ToParse) { string fileName; string outputDir; if (Directory.Exists(Output)) { // the output is actually a directory name fileName = string.Empty; outputDir = Output; } else { // get the extension fileName = Path.GetFileName(Output); outputDir = Path.GetDirectoryName(Output); } // Rule #6.1: if it is just the output directory // we append the input file name as the output + csv as default if (string.IsNullOrEmpty(fileName)) { TranslationFileType = FileType.CSV; Output = outputDir + Path.DirectorySeparatorChar + Path.GetFileName(Input) + "." + TranslationFileType.ToString(); } else { // Rule #6.2: if we have file name, check the extension. string extension = Path.GetExtension(Output); // ignore case and invariant culture if (string.Compare(extension, "." + FileType.CSV.ToString(), true, CultureInfo.InvariantCulture) == 0) { TranslationFileType = FileType.CSV; } else { // just consider the output as txt format if it doesn't have .csv extension TranslationFileType = FileType.TXT; } } } else { // it is to generate. And Output should point to the directory name. if (!Directory.Exists(Output)) { return(StringLoader.Get("OutputDirectoryError", Output)); } } } // Rule #7: if the input assembly path is not null if (AssemblyPaths != null && AssemblyPaths.Count > 0) { Assemblies = new Assembly[AssemblyPaths.Count]; for (int i = 0; i < Assemblies.Length; i++) { string errorMsg = null; try { // load the assembly Assemblies[i] = Assembly.LoadFrom((string)AssemblyPaths[i]); } catch (ArgumentException argumentError) { errorMsg = argumentError.Message; } catch (BadImageFormatException formatError) { errorMsg = formatError.Message; } catch (FileNotFoundException fileError) { errorMsg = fileError.Message; } catch (PathTooLongException pathError) { errorMsg = pathError.Message; } catch (SecurityException securityError) { errorMsg = securityError.Message; } if (errorMsg != null) { return(errorMsg); // return error message when loading this assembly } } } // if we come to this point, we are all fine, return null error message return(null); }
private static void PrintUsage() { Console.WriteLine(StringLoader.Get("Msg_Usage")); }
/// <summary> /// get CommandLineOptions, return error message /// </summary> private static void GetCommandLineOptions(string[] args, out LocBamlOptions options, out string errorMessage) { CommandLine commandLine; try{ // "*" means the option must have a value. no "*" means the option can't have a value commandLine = new CommandLine(args, new string[] { "parse", // /parse for update "generate", // /generate for generate "*out", // /out for output .csv|.txt when parsing, for output directory when generating "*culture", // /culture for culture name "*translation", // /translation for translation file, .csv|.txt "*asmpath", // /asmpath, for assembly path to look for references (TODO: add asmpath support) "nologo", // /nologo for not to print logo "help", // /help for help "verbose" // /verbose for verbose output } ); } catch (ArgumentException e) { errorMessage = e.Message; options = null; return; } if (commandLine.NumArgs + commandLine.NumOpts < 1) { PrintLogo(null); PrintUsage(); errorMessage = null; options = null; return; } options = new LocBamlOptions(); options.Input = commandLine.GetNextArg(); Option commandLineOption; while ((commandLineOption = commandLine.GetNextOption()) != null) { if (commandLineOption.Name == "parse") { options.ToParse = true; } else if (commandLineOption.Name == "generate") { options.ToGenerate = true; } else if (commandLineOption.Name == "nologo") { options.HasNoLogo = true; } else if (commandLineOption.Name == "help") { // we print usage and stop processing PrintUsage(); errorMessage = null; options = null; return; } else if (commandLineOption.Name == "verbose") { options.IsVerbose = true; } // the following ones need value else if (commandLineOption.Name == "out") { options.Output = commandLineOption.Value; } else if (commandLineOption.Name == "translation") { options.Translations = commandLineOption.Value; } else if (commandLineOption.Name == "asmpath") { if (options.AssemblyPaths == null) { options.AssemblyPaths = new ArrayList(); } options.AssemblyPaths.Add(commandLineOption.Value); } else if (commandLineOption.Name == "culture") { try { options.CultureInfo = new CultureInfo(commandLineOption.Value); } catch (ArgumentException e) { // Error errorMessage = e.Message; return; } } else { // something that we don't recognize errorMessage = StringLoader.Get("Err_InvalidOption", commandLineOption.Name); return; } } // we passed all the test till here. Now check the combinations of the options errorMessage = options.CheckAndSetDefault(); }
/// <summary> /// Constructor /// </summary> /// <param name="reader">resoure text reader that reads CSV or a tab-separated txt file</param> internal TranslationDictionariesReader(ResourceTextReader reader) { if (reader == null) { throw new ArgumentNullException("reader"); } // hash key is case insensitive strings _table = new Hashtable(); // we read each Row int rowNumber = 0; while (reader.ReadRow()) { rowNumber++; // field #1 is the baml name. string bamlName = reader.GetColumn(0); // it can't be null if (bamlName == null) { throw new ApplicationException(StringLoader.Get("EmptyRowEncountered")); } if (string.IsNullOrEmpty(bamlName)) { // allow for comment lines in csv file. // each comment line starts with ",". It will make the first entry as String.Empty. // and we will skip the whole line. continue; // if the first column is empty, take it as a comment line } // field #2: key to the localizable resource string key = reader.GetColumn(1); if (key == null) { throw new ApplicationException(StringLoader.Get("NullBamlKeyNameInRow")); } BamlLocalizableResourceKey resourceKey = LocBamlConst.StringToResourceKey(key); // get the dictionary BamlLocalizationDictionary dictionary = this[bamlName]; if (dictionary == null) { // we create one if it is not there yet. dictionary = new BamlLocalizationDictionary(); this[bamlName] = dictionary; } BamlLocalizableResource resource; // the rest of the fields are either all null, // or all non-null. If all null, it means the resource entry is deleted. // get the string category string categoryString = reader.GetColumn(2); if (categoryString == null) { // it means all the following fields are null starting from column #3. resource = null; } else { // the rest must all be non-null. // the last cell can be null if there is no content for (int i = 3; i < 6; i++) { if (reader.GetColumn(i) == null) { throw new Exception(StringLoader.Get("InvalidRow")); } } // now we know all are non-null. let's try to create a resource resource = new BamlLocalizableResource(); // field #3: Category resource.Category = (LocalizationCategory)StringCatConverter.ConvertFrom(categoryString); // field #4: Readable resource.Readable = (bool)BoolTypeConverter.ConvertFrom(reader.GetColumn(3)); // field #5: Modifiable resource.Modifiable = (bool)BoolTypeConverter.ConvertFrom(reader.GetColumn(4)); // field #6: Comments resource.Comments = reader.GetColumn(5); // field #7: Content resource.Content = reader.GetColumn(6); // in case content being the last column, consider null as empty. if (resource.Content == null) { resource.Content = string.Empty; } // field > #7: Ignored. } // at this point, we are good. // add to the dictionary. dictionary.Add(resourceKey, resource); } }
/// <summary> /// Write the localizable key-value pairs /// </summary> /// <param name="options"></param> internal static void Write(LocBamlOptions options) { Stream output = new FileStream(options.Output, FileMode.Create); InputBamlStreamList bamlStreamList = new InputBamlStreamList(options); using (ResourceTextWriter writer = new ResourceTextWriter(options.TranslationFileType, output)) { options.WriteLine(StringLoader.Get("WriteBamlValues")); for (int i = 0; i < bamlStreamList.Count; i++) { options.Write(" "); options.Write(StringLoader.Get("ProcessingBaml", bamlStreamList[i].Name)); // Search for comment file in the same directory. The comment file has the extension to be // "loc". string commentFile = Path.ChangeExtension(bamlStreamList[i].Name, "loc"); TextReader commentStream = null; try { if (File.Exists(commentFile)) { commentStream = new StreamReader(commentFile); } // create the baml localizer BamlLocalizer mgr = new BamlLocalizer( bamlStreamList[i].Stream, new BamlLocalizabilityByReflection(options.Assemblies), commentStream ); // extract localizable resource from the baml stream BamlLocalizationDictionary dict = mgr.ExtractResources(); // write out each resource foreach (DictionaryEntry entry in dict) { // column 1: baml stream name writer.WriteColumn(bamlStreamList[i].Name); BamlLocalizableResourceKey key = (BamlLocalizableResourceKey)entry.Key; BamlLocalizableResource resource = (BamlLocalizableResource)entry.Value; // column 2: localizable resource key writer.WriteColumn(LocBamlConst.ResourceKeyToString(key)); // column 3: localizable resource's category writer.WriteColumn(resource.Category.ToString()); // column 4: localizable resource's readability writer.WriteColumn(resource.Readable.ToString()); // column 5: localizable resource's modifiability writer.WriteColumn(resource.Modifiable.ToString()); // column 6: localizable resource's localization comments writer.WriteColumn(resource.Comments); // column 7: localizable resource's content writer.WriteColumn(resource.Content); // Done. finishing the line writer.EndLine(); } options.WriteLine(StringLoader.Get("Done")); } finally { if (commentStream != null) { commentStream.Close(); } } } // close all the baml input streams, output stream is closed by writer. bamlStreamList.Close(); } }
//-------------------------------------------------- // The function follows Managed code parser // implementation. in the future, maybe they should // share the same code //-------------------------------------------------- private static void GenerateAssembly(LocBamlOptions options, TranslationDictionariesReader dictionaries) { // there are many names to be used when generating an assembly string sourceAssemblyFullName = options.Input; // source assembly full path string outputAssemblyDir = options.Output; // output assembly directory string outputAssemblyLocalName = GetOutputFileName(options); // output assembly name string moduleLocalName = GetAssemblyModuleLocalName(options, outputAssemblyLocalName); // the module name within the assmbly // get the source assembly Assembly srcAsm = Assembly.LoadFrom(sourceAssemblyFullName); // obtain the assembly name AssemblyName targetAssemblyNameObj = srcAsm.GetName(); // store the culture info of the source assembly CultureInfo srcCultureInfo = targetAssemblyNameObj.CultureInfo; // update it to use it for target assembly targetAssemblyNameObj.Name = Path.GetFileNameWithoutExtension(outputAssemblyLocalName); targetAssemblyNameObj.CultureInfo = options.CultureInfo; // we get a assembly builder AssemblyBuilder targetAssemblyBuilder = Thread.GetDomain().DefineDynamicAssembly( targetAssemblyNameObj, // name of the assembly AssemblyBuilderAccess.RunAndSave, // access rights outputAssemblyDir // storage dir ); // Add assembly info, trying to preserver original values as close as possible Action <Type, string> AddCustomStringAttribute = (Type type, string content) => { if (string.IsNullOrEmpty(content)) { return; } ConstructorInfo ctor = type.GetConstructor(new Type[] { typeof(string) }); targetAssemblyBuilder.SetCustomAttribute(new CustomAttributeBuilder(ctor, new object[] { content })); }; bool hasInformationalVersionAttr = false; object[] attrs = srcAsm.GetCustomAttributes(false); foreach (var attr in attrs) { if (attr is AssemblyCompanyAttribute cmp) { AddCustomStringAttribute(attr.GetType(), cmp.Company); continue; } if (attr is AssemblyCopyrightAttribute copy) { AddCustomStringAttribute(attr.GetType(), copy.Copyright); continue; } if (attr is AssemblyDescriptionAttribute da) { AddCustomStringAttribute(attr.GetType(), da.Description); continue; } if (attr is AssemblyFileVersionAttribute fva) { AddCustomStringAttribute(attr.GetType(), fva.Version); if (!hasInformationalVersionAttr) { // Also set AssemblyInformationalVersionAttribute, if not set already. // The unmanaged ProductVersion is taken from that attribute. AddCustomStringAttribute(typeof(AssemblyInformationalVersionAttribute), fva.Version); } continue; } if (attr is AssemblyInformationalVersionAttribute iva) { AddCustomStringAttribute(attr.GetType(), iva.InformationalVersion); hasInformationalVersionAttr = true; continue; } if (attr is AssemblyProductAttribute pa) { AddCustomStringAttribute(attr.GetType(), pa.Product); continue; } if (attr is AssemblyTitleAttribute ta) { AddCustomStringAttribute(attr.GetType(), ta.Title); continue; } if (attr is AssemblyTrademarkAttribute tm) { AddCustomStringAttribute(attr.GetType(), tm.Trademark); continue; } if (attr is AssemblyVersionAttribute va) { AddCustomStringAttribute(attr.GetType(), va.Version); continue; } } targetAssemblyBuilder.DefineVersionInfoResource(); // we create a module builder for embeded resource modules ModuleBuilder moduleBuilder = targetAssemblyBuilder.DefineDynamicModule( moduleLocalName, outputAssemblyLocalName ); options.WriteLine(StringLoader.Get("GenerateAssembly")); // now for each resource in the assembly foreach (string resourceName in srcAsm.GetManifestResourceNames()) { // get the resource location for the resource ResourceLocation resourceLocation = srcAsm.GetManifestResourceInfo(resourceName).ResourceLocation; // if this resource is in another assemlby, we will skip it if ((resourceLocation & ResourceLocation.ContainedInAnotherAssembly) != 0) { continue; // in resource assembly, we don't have resource that is contained in another assembly } // gets the neutral resource name, giving it the source culture info string neutralResourceName = GetNeutralResModuleName(resourceName, srcCultureInfo); // gets the target resource name, by giving it the target culture info string targetResourceName = GetCultureSpecificResourceName(neutralResourceName, options.CultureInfo); // resource stream Stream resourceStream = srcAsm.GetManifestResourceStream(resourceName); // see if it is a .resources if (neutralResourceName.ToLower(CultureInfo.InvariantCulture).EndsWith(".resources")) { // now we think we have resource stream // get the resource writer IResourceWriter writer; // check if it is a embeded assembly if ((resourceLocation & ResourceLocation.Embedded) != 0) { // gets the resource writer from the module builder writer = moduleBuilder.DefineResource( targetResourceName, // resource name targetResourceName, // resource description ResourceAttributes.Public // visibilty of this resource to other assembly ); } else { // it is a standalone resource, we get the resource writer from the assembly builder writer = targetAssemblyBuilder.DefineResource( targetResourceName, // resource name targetResourceName, // description targetResourceName, // file name to save to ResourceAttributes.Public // visibility of this resource to other assembly ); } // get the resource reader IResourceReader reader = new ResourceReader(resourceStream); // generate the resources GenerateResourceStream(options, resourceName, reader, writer, dictionaries); // we don't call writer.Generate() or writer.Close() here // because the AssemblyBuilder will call them when we call Save() on it. } else { // else it is a stand alone untyped manifest resources. string extension = Path.GetExtension(targetResourceName); string fullFileName = Path.Combine(outputAssemblyDir, targetResourceName); // check if it is a .baml, case-insensitive if (string.Compare(extension, ".baml", true, CultureInfo.InvariantCulture) == 0) { // try to localized the the baml // find the resource dictionary BamlLocalizationDictionary dictionary = dictionaries[resourceName]; // if it is null, just create an empty dictionary. if (dictionary != null) { // it is a baml stream using (Stream output = File.OpenWrite(fullFileName)) { options.Write(" "); options.WriteLine(StringLoader.Get("GenerateStandaloneBaml", fullFileName)); GenerateBamlStream(resourceStream, output, dictionary, options); options.WriteLine(StringLoader.Get("Done")); } } else { // can't find localization of it, just copy it GenerateStandaloneResource(fullFileName, resourceStream); } } else { // it is an untyped resource stream, just copy it GenerateStandaloneResource(fullFileName, resourceStream); } // now add this resource file into the assembly targetAssemblyBuilder.AddResourceFile( targetResourceName, // resource name targetResourceName, // file name ResourceAttributes.Public // visibility of the resource to other assembly ); } } // at the end, generate the assembly targetAssemblyBuilder.Save(outputAssemblyLocalName); options.WriteLine(StringLoader.Get("DoneGeneratingAssembly")); }