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 )); }
internal TranslationDictionariesReader(List <BamlString> bamlStrings) { // hash key is case insensitive strings _table = new Hashtable(); // we read each Row int rowNumber = 0; foreach (BamlString bamlString in bamlStrings) { rowNumber++; // field #1 is the baml name. string bamlName = bamlString.BamlFile; // it can't be null if (bamlName == null) { throw new ApplicationException(StringLoader.Get("EmptyRowEncountered")); } // field #2: key to the localizable resource string key = bamlString.ResourceKey; 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. resource = new BamlLocalizableResource(); resource.Category = bamlString.Category; resource.Readable = (bool)BoolTypeConverter.ConvertFrom(bamlString.Readability); resource.Modifiable = (bool)BoolTypeConverter.ConvertFrom(bamlString.Modifiable); resource.Comments = bamlString.Comments; // in case content being the last column, consider null as empty. resource.Content = bamlString.Content ?? string.Empty; // at this point, we are good. // add to the dictionary. dictionary.Add(resourceKey, resource); } }
internal static void GenerateAssembly(ResourceGenerationOptions options) { TranslationDictionariesReader dictionaries = new TranslationDictionariesReader(options.BamlStrings); string sourceAssemblyFullName = options.AssemblyFileName; // source assembly full path string outputAssemblyLocalName = System.IO.Path.GetFileName(options.OutputFileName); // output assembly name string moduleLocalName = GetAssemblyModuleLocalName( options.CultureInfo, System.IO.Path.GetFileName(outputAssemblyLocalName)); // the module name within the assmbly // get the source assembly Assembly sourceAssembly = options.SourceAssembly; CultureInfo cultureInfo = options.CultureInfo; // obtain the assembly name AssemblyName targetAssemblyNameObj = sourceAssembly.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 = cultureInfo; // we get a assembly builder AssemblyBuilder targetAssemblyBuilder = options.AppDomain.DefineDynamicAssembly( targetAssemblyNameObj, // name of the assembly AssemblyBuilderAccess.RunAndSave, // access rights System.IO.Path.GetDirectoryName(options.OutputFileName) // storage dir ); // 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 sourceAssembly.GetManifestResourceNames()) { // get the resource location for the resource ResourceLocation resourceLocation = sourceAssembly.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, cultureInfo); // resource stream Stream resourceStream = sourceAssembly.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( System.IO.Path.GetDirectoryName(options.AssemblyFileName), 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")); }
private static void GenerateResourceStream( ResourceGenerationOptions 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 GenerateBamlStream( (Stream)entry.Value, targetStream, localizations, options ); } 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); } } }
/// <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> /// 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(InputFile)) { return(StringLoader.Get("InputFileRequired")); } else { if (!File.Exists(InputFile)) { return(StringLoader.Get("FileNotFound", InputFile)); } string extension = Path.GetExtension(InputFile); // 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(TranslationsFile)) { return(StringLoader.Get("TranslationNeeded")); } else { string extension = Path.GetExtension(TranslationsFile); if (!File.Exists(TranslationsFile)) { return(StringLoader.Get("TranslationNotFound", TranslationsFile)); } 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(OutputFileOrDirectory)) { // Rule #5.1: If it is parse, we default to [input file name].csv if (ToParse) { string fileName = Path.GetFileNameWithoutExtension(InputFile); OutputFileOrDirectory = 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(OutputFileOrDirectory)) { // the output is actually a directory name fileName = string.Empty; outputDir = OutputFileOrDirectory; } else { // get the extension fileName = Path.GetFileName(OutputFileOrDirectory); outputDir = Path.GetDirectoryName(OutputFileOrDirectory); } // 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; OutputFileOrDirectory = outputDir + Path.DirectorySeparatorChar + Path.GetFileName(InputFile) + "." + TranslationFileType.ToString(); } else { // Rule #6.2: if we have file name, check the extension. string extension = Path.GetExtension(OutputFileOrDirectory); // 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(OutputFileOrDirectory)) { return(StringLoader.Get("OutputDirectoryError", OutputFileOrDirectory)); } } } // 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); }