/// <summary>Extracts localization items from the <c>[KSPAction]</c> annotated fields.</summary> /// <param name="info">The type member to extract the strings for.</param> /// <returns>All the localization items for the member.</returns> static List <LocItem> EmitItemsForKspAction(MemberInfo info) { var res = new List <LocItem>(); var attrObj = info.GetCustomAttributes(false).OfType <KSPAction>().FirstOrDefault(); if (attrObj == null) { return(res); } var groupKey = "Type: " + info.DeclaringType.FullName; const string sortKey = "KSP Actions"; // Get guiName localization. var guiNameLoc = GetItemFromLocalizableObject(info, groupKey, sortKey); if (!guiNameLoc.HasValue && !string.IsNullOrEmpty(attrObj.guiName)) { // Fallback to the KSPAction values. guiNameLoc = new LocItem() { groupKey = groupKey, subgroupKey = sortKey, fullFilePath = info.DeclaringType.Assembly.Location, locTag = MakeTypeMemberLocalizationTag(info), locDefaultValue = attrObj.guiName, }; } if (guiNameLoc.HasValue) { res.Add(guiNameLoc.Value); } return(res); }
/// <summary>Extracts localization items from the <c>[KSPField]</c> annotated fields.</summary> /// <param name="info">The type member to extract the strings for.</param> /// <returns>All the localization items for the member.</returns> static List <LocItem> EmitItemsForKSPField(MemberInfo info) { var res = new List <LocItem>(); var attrObj = info.GetCustomAttributes(false).OfType <KSPField>().FirstOrDefault(); if (attrObj == null) { return(res); } var groupKey = "Type: " + info.DeclaringType.FullName; const string sortKey = "KSP Fields"; // Get guiName localization. var guiNameLoc = GetItemFromLocalizableObject(info, groupKey, sortKey); if (!guiNameLoc.HasValue && !string.IsNullOrEmpty(attrObj.guiName)) { // Fallback to the KSPField values. guiNameLoc = new LocItem() { groupKey = groupKey, sortKey = sortKey, fullFilePath = info.DeclaringType.Assembly.Location, locTag = MakeTypeMemberLocalizationTag(info), locDefaultValue = attrObj.guiName, }; } if (guiNameLoc.HasValue) { res.Add(guiNameLoc.Value); } // Get localization for the units if present. var guiUnitsLoc = GetItemFromLocalizableObject( info, groupKey, sortKey, spec: LocalizationLoader.KspFieldUnitsSpec); if (!guiUnitsLoc.HasValue && !string.IsNullOrEmpty(attrObj.guiUnits)) { // Fallback to the KSPField values. guiUnitsLoc = new LocItem() { groupKey = groupKey, sortKey = sortKey, fullFilePath = info.DeclaringType.Assembly.Location, locTag = MakeTypeMemberLocalizationTag(info, nameSuffix: "_Units"), locDefaultValue = attrObj.guiUnits, }; } if (guiUnitsLoc.HasValue) { res.Add(guiUnitsLoc.Value); } return(res); }
/// <summary>Extracts the strings that look like the localized tags.</summary> /// <remarks> /// This methods looks for the values that are localized in way it's done in the stock parts. /// </remarks> /// <param name="part">The part to extract the items for.</param> /// <param name="config"> /// The config node of the part. It's an extend version with the comments. /// </param> /// <returns>The list of extracted items.</returns> static List <LocItem> EmitItemsForNode(AvailablePart part, ConfigNode config) { var res = new List <LocItem>(); // Go through all the fields to detect if there are localized optional fields. foreach (var field in config.values.Cast <ConfigNode.Value>()) { if (string.IsNullOrEmpty(field.comment)) { continue; } var meta = MetaBlock.MakeFromString(field.comment); var comment = meta.inlineComment ?? ""; var match = Regex.Match(comment, @"^(#[a-zA-Z0-9_-]+)\s*=\s*(.+?)$"); if (!match.Success) { continue; // Not localized. } var locTag = match.Groups[1].Value; var locValue = field.value; if (field.value == locTag) { // In case of the tag is not get resolved, use the default template. DebugEx.Warning( "Field '{0}' in part {1} looks localized, but the tag '{2}' is not found" + " (suggested default value: '{3}')", field.name, part.name, locTag, match.Groups[2].Value); locValue = match.Groups[2].Value; } var item = new LocItem() { groupKey = "Part: " + part.name, fullFilePath = part.configFileFullName, locTag = locTag, locDefaultValue = locValue, }; res.Add(item); } // Scan the nested nodes. foreach (var nestedNode in config.GetNodes()) { res.AddRange(EmitItemsForNode(part, nestedNode)); } return(res); }
/// <summary>Extracts the strings that look like the localized tags.</summary> /// <remarks> /// This methods looks for the values that are localized in way it's done in the stock parts. /// </remarks> /// <param name="part">The part to extract the items for.</param> /// <param name="config"> /// The config node of the part. It's an extend version with the comments. /// </param> /// <returns>The list of extracted items.</returns> static List <LocItem> EmitItemsForNode(AvailablePart part, ConfigNode config) { var res = new List <LocItem>(); // Go thru all the fields to detect if there are localized optional fields. foreach (var field in config.values.Cast <ConfigNode.Value>()) { if (localizablePartFields.Contains(field.name) || string.IsNullOrEmpty(field.comment)) { continue; } var match = Regex.Match(field.comment, @"^\s*(#[a-zA-Z0-9_-]+)\s*=\s*(.+?)$"); if (!match.Success) { continue; // Not localized. } string locTag = match.Groups[1].Value; string locDefaultValue = match.Groups[2].Value; if (field.value != locTag) { Debug.LogWarningFormat( "A possible localized field has wrong syntax:" + " part={0}, field={1}, value={2}, comment={3}", part.name, field.name, field.value, field.comment); } var item = new LocItem() { groupKey = "Part: " + part.name, fullFilePath = part.configFileFullName, locTag = MakePartFieldLocalizationTag(part.name, field.name), locDefaultValue = locDefaultValue, }; res.Add(item); } // Scan the nested nodes. foreach (var nestedNode in config.GetNodes()) { res.AddRange(EmitItemsForNode(part, nestedNode)); } return(res); }
/// <summary>Extracts the localization items from the part's config.</summary> /// <param name="part">The part to extract items for.</param> /// <returns>All the localization items for the part.</returns> public static List <LocItem> EmitItemsForPart(AvailablePart part) { var res = new List <LocItem>(); // The part's config in the AvailablePart doesn't have all the fields and lacks the comments. // We do need the comments to resolve the field tag names, so load via a custom method. // In case of the custom loading method fails, use the stock one without the comments support. var config = (ConfigStore.LoadConfigWithComments(part.configFileFullName) ?? ConfigNode.Load(part.configFileFullName)).GetNode("PART"); if (config == null) { DebugEx.Error("Failed to load part's config: partName={0}, configUrl={1}", part.name, part.configFileFullName); return(res); } // Go through the fields we know must be localized. foreach (var fieldName in LocalizablePartFields) { var field = config.values.Cast <ConfigNode.Value>().FirstOrDefault(x => x.name == fieldName); if (field == null) { DebugEx.Warning("Field '{0}' is not found in the part {1} config", fieldName, part.name); continue; } config.values.Remove(field); // Don't handle it down the stream. string locTag = null; string locDefaultValue = null; var meta = MetaBlock.MakeFromString(field.comment); if (LocalizationManager.IsLocalizationTag(meta.inlineComment, firstWordOnly: true)) { var match = Regex.Match(meta.inlineComment, @"^(#[a-zA-Z0-9_-]+)\s*=\s*(.+?)$"); if (match.Success) { locTag = match.Groups[1].Value; if (field.value == locTag) { // Part's tag localization failed, use the default text. locDefaultValue = match.Groups[2].Value; } } else { DebugEx.Warning("Cannot resolve default localization tag in field {0} for part {1}: {2}", fieldName, config.GetValue("name"), meta.inlineComment); } } // ReSharper disable once UseStringInterpolation var fieldOrderStr = string.Format( "\0x00_{0:000}_{1}", // Use zero prefix to place it before anything else. Controller.partFieldsSorting.IndexOf(fieldName, StringComparison.Ordinal), fieldName); var item = new LocItem() { groupKey = "Part: " + part.name, sortKey = fieldOrderStr, fullFilePath = part.configFileFullName, locTag = locTag ?? MakePartFieldLocalizationTag(config.GetValue("name"), fieldName), locDefaultValue = locDefaultValue ?? field.value, }; res.Add(item); } res.AddRange(EmitItemsForNode(part, config)); return(res); }
/// <summary>Extracts localization items from the <c>[KSPField]</c> annotated fields.</summary> /// <param name="info">The type member to extract the strings for.</param> /// <returns>All the localization items for the member.</returns> static List <LocItem> EmitItemsForKspField(MemberInfo info) { var res = new List <LocItem>(); var attrObj = info.GetCustomAttributes(false).OfType <KSPField>().FirstOrDefault(); if (attrObj == null) { return(res); } var groupKey = "Type: " + info.DeclaringType.FullName; const string subgroupKey = "KSP Fields"; // Get guiName localization. var guiNameLoc = GetItemFromLocalizableObject(info, groupKey, subgroupKey); if (!guiNameLoc.HasValue && !string.IsNullOrEmpty(attrObj.guiName)) { // Fallback to the KSPField values. guiNameLoc = new LocItem() { groupKey = groupKey, subgroupKey = subgroupKey, fullFilePath = info.DeclaringType.Assembly.Location, locTag = MakeTypeMemberLocalizationTag(info), locDefaultValue = attrObj.guiName, }; } if (guiNameLoc.HasValue) { res.Add(guiNameLoc.Value); } // Get localization for the units if present. var guiUnitsLoc = GetItemFromLocalizableObject( info, groupKey, subgroupKey, spec: StdSpecTags.Units); if (!guiUnitsLoc.HasValue && !string.IsNullOrEmpty(attrObj.guiUnits)) { // Fallback to the KSPField values. guiUnitsLoc = new LocItem() { groupKey = groupKey, subgroupKey = subgroupKey, fullFilePath = info.DeclaringType.Assembly.Location, locTag = MakeTypeMemberLocalizationTag(info, nameSuffix: "_Units"), locDefaultValue = attrObj.guiUnits, }; } if (guiUnitsLoc.HasValue) { res.Add(guiUnitsLoc.Value); } // Get localizations for UI_Control var uiControlAttr = info.GetCustomAttributes(false).OfType <UI_Control>().FirstOrDefault(); if (uiControlAttr == null) { return(res); } var uiToggleAttr = uiControlAttr as UI_Toggle; if (uiToggleAttr != null) { var guiEnabledLoc = GetItemFromLocalizableObject( info, groupKey, subgroupKey, spec: StdSpecTags.ToggleEnabled); if (!guiEnabledLoc.HasValue && !string.IsNullOrEmpty(uiToggleAttr.displayEnabledText)) { guiEnabledLoc = new LocItem() { groupKey = groupKey, subgroupKey = subgroupKey, fullFilePath = info.DeclaringType.Assembly.Location, locTag = MakeTypeMemberLocalizationTag(info, nameSuffix: "_ToggleEnabled"), locDefaultValue = uiToggleAttr.displayEnabledText, }; } if (guiEnabledLoc.HasValue) { res.Add(guiEnabledLoc.Value); } var guiDisabledLoc = GetItemFromLocalizableObject( info, groupKey, subgroupKey, spec: StdSpecTags.ToggleDisabled); if (!guiDisabledLoc.HasValue && !string.IsNullOrEmpty(uiToggleAttr.displayDisabledText)) { guiDisabledLoc = new LocItem() { groupKey = groupKey, subgroupKey = subgroupKey, fullFilePath = info.DeclaringType.Assembly.Location, locTag = MakeTypeMemberLocalizationTag(info, nameSuffix: "_ToggleDisabled"), locDefaultValue = uiToggleAttr.displayDisabledText, }; } if (guiDisabledLoc.HasValue) { res.Add(guiDisabledLoc.Value); } } return(res); }
/// <summary>Extracts the localization items from the part's config.</summary> /// <param name="part">The part to extract items for.</param> /// <returns>All the localization items for the part.</returns> public static List <LocItem> EmitItemsForPart(AvailablePart part) { var res = new List <LocItem>(); // The part's config in the AvailablePart doesn't have all the fields and lacks the comments. // We do need the comments to resolve the field tag names, so load via a custom method. // In case of the custom loading method fails, use the stock one without the comments support. var config = (ConfigStore.LoadConfigWithComments(part.configFileFullName) ?? ConfigNode.Load(part.configFileFullName)).GetNode("PART"); if (config == null) { Debug.LogErrorFormat("Failed to load part's config: partName={0}, configUrl={1}", part.name, part.configFileFullName); return(res); } // Go thru the fields we know must be localized. foreach (var fieldName in localizablePartFields) { var field = config.values.Cast <ConfigNode.Value>() .FirstOrDefault(x => x.name == fieldName); if (field == null) { Debug.LogWarningFormat("Field '{0}' is not found in the part {1} config", fieldName, part.name); continue; } string locTag = null; string locDefaultValue = null; if (!string.IsNullOrEmpty(field.comment) && field.comment.StartsWith("#", StringComparison.Ordinal)) { var match = Regex.Match(field.comment, @"^(#[a-zA-Z0-9_-]+)\s*=\s*(.+?)$"); if (match.Success) { locTag = match.Groups[1].Value; if (field.value == locTag) { // Part's tag localization failed, use the default text. locDefaultValue = match.Groups[2].Value; } } else { Debug.LogWarningFormat( "Cannot resolve defult localization tag in field {0} for part {1}: {2}", fieldName, config.GetValue("name"), field.comment); } } var item = new LocItem() { groupKey = "Part: " + part.name, fullFilePath = part.configFileFullName, locTag = locTag ?? MakePartFieldLocalizationTag(config.GetValue("name"), fieldName), locDefaultValue = locDefaultValue ?? field.value, }; res.Add(item); } res.AddRange(EmitItemsForNode(part, config)); return(res); }