/// <summary> /// Adds local text translations defined implicitly by Description attributes in /// enumeration classes. Only enum values that has Description attribute are added as /// local text. By default, enums are registered in format: /// "Enums.{EnumerationTypeFullName}.{EnumValueName}". EnumerationTypeFullName, is /// fullname of the enumeration type. This can be overridden by attaching a EnumKey /// attribute. /// </summary> /// <param name="typeSource">Type source to search for enumeration classes in</param> /// <param name="languageID">Language ID texts will be added (default is invariant language)</param> /// <param name="registry">Registry</param> public static void AddEnumTexts(this ILocalTextRegistry registry, ITypeSource typeSource, string languageID = LocalText.InvariantLanguageID) { if (typeSource == null) { throw new ArgumentNullException("assemblies"); } var provider = registry ?? throw new ArgumentNullException(nameof(registry)); foreach (var type in typeSource.GetTypes()) { if (type.IsEnum) { var enumKey = EnumMapper.GetEnumTypeKey(type); foreach (var name in Enum.GetNames(type)) { var member = type.GetMember(name); if (member.Length == 0) { continue; } var descAttr = member[0].GetCustomAttribute <DescriptionAttribute>(); if (descAttr != null) { provider.Add(languageID, "Enums." + enumKey + "." + name, descAttr.Description); } } } } }
public static void AddFromFilesInFolder(string path, ILocalTextRegistry registry = null) #endif { if (path == null) throw new ArgumentNullException("path"); if (!Directory.Exists(path)) return; var files = Directory.GetFiles(path, "*.json", SearchOption.AllDirectories); Array.Sort(files); foreach (var file in files) { var texts = JsonConfigHelper.LoadConfig<Dictionary<string, JToken>>(file); var langID = Path.GetFileNameWithoutExtension(Path.GetFileName(file)); var idx = langID.LastIndexOf("."); if (idx >= 0) langID = langID.Substring(idx + 1); if (langID.ToLowerInvariant() == "invariant") langID = ""; AddFromNestedDictionary(texts, "", langID, registry); } }
public static void Initialize(IEnumerable<Assembly> assemblies, string languageID = LocalText.InvariantLanguageID, ILocalTextRegistry registry = null) #endif { assemblies = assemblies ?? ExtensibilityHelper.SelfAssemblies; if (assemblies == null) throw new ArgumentNullException("assemblies"); var provider = registry ?? Dependency.Resolve<ILocalTextRegistry>(); foreach (var assembly in assemblies) { foreach (var type in assembly.GetTypes()) { if (type.GetIsEnum()) { var enumKey = EnumMapper.GetEnumTypeKey(type); foreach (var name in Enum.GetNames(type)) { var member = type.GetMember(name); if (member.Length == 0) continue; var descAttr = member[0].GetCustomAttribute<DescriptionAttribute>(); if (descAttr != null) provider.Add(languageID, "Enums." + enumKey + "." + name, descAttr.Description); } } } } }
private static void Initialize(ILocalTextRegistry registry, Type type, string languageID, string prefix) { var provider = registry ?? Dependency.Resolve<ILocalTextRegistry>(); foreach (var member in type.GetMembers(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)) { var fi = member as FieldInfo; if (fi != null && fi.FieldType == typeof(LocalText)) { var value = fi.GetValue(null) as LocalText; if (value != null) { var initialized = value as InitializedLocalText; if (initialized != null) { provider.Add(languageID, initialized.Key, initialized.InitialText); } else { provider.Add(languageID, prefix + fi.Name, value.Key); fi.SetValue(null, new InitializedLocalText(prefix + fi.Name, value.Key)); } } } } foreach (var nested in type.GetNestedTypes(BindingFlags.Public | BindingFlags.DeclaredOnly)) { var name = nested.Name; if (name.EndsWith("_")) name = name.Substring(0, name.Length - 1); Initialize(registry, nested, languageID, prefix + name + "."); } }
public TranslationController(IWebHostEnvironment hostEnvironment, ILocalTextRegistry localTextRegistry, ITypeSource typeSource) { HostEnvironment = hostEnvironment ?? throw new ArgumentNullException(nameof(hostEnvironment)); LocalTextRegistry = localTextRegistry ?? throw new ArgumentNullException(nameof(localTextRegistry)); TypeSource = typeSource ?? throw new ArgumentNullException(nameof(typeSource)); }
/// <summary> /// Gets permission keys and adds texts if any from static nested permission key classes marked with NestedPermissionKeys attribute. /// </summary> public static HashSet <string> AddNestedPermissions(this ILocalTextRegistry registry, IEnumerable <Assembly> assemblies = null) { assemblies = assemblies ?? ExtensibilityHelper.SelfAssemblies; if (assemblies == null) { throw new ArgumentNullException("assemblies"); } var permissions = new HashSet <string>(StringComparer.OrdinalIgnoreCase); foreach (var assembly in assemblies) { foreach (var type in assembly.GetTypes()) { var attr = type.GetCustomAttribute <NestedPermissionKeysAttribute>(); if (attr != null) { AddKeysFrom(permissions, registry, type, attr.LanguageID ?? LocalText.InvariantLanguageID); } } } return(permissions); }
/// <summary> /// Adds translations from JSON files at specified path. File names in this directory should be in format /// {anyprefix}.{languageID}.json where {languageID} is a language code like 'en', 'en-GB' etc. /// </summary> /// <param name="path">Path containing JSON files</param> /// <param name="registry">Registry</param> public static void AddJsonTexts(this ILocalTextRegistry registry, string path) { if (path == null) { throw new ArgumentNullException("path"); } if (!Directory.Exists(path)) { return; } var files = Directory.GetFiles(path, "*.json", SearchOption.AllDirectories); Array.Sort(files); foreach (var file in files) { var texts = JsonConvert.DeserializeObject <Dictionary <string, JToken> >(File.ReadAllText(file).TrimToNull() ?? "{}"); var langID = Path.GetFileNameWithoutExtension(Path.GetFileName(file)); var idx = langID.LastIndexOf("."); if (idx >= 0) { langID = langID[(idx + 1)..];
public LocalTextScript(ILocalTextRegistry registry, string package, string includes, string languageId, bool isPending) { this.registry = registry ?? throw new ArgumentNullException(nameof(registry)); this.includes = includes; this.languageId = languageId; this.isPending = isPending; scriptName = GetScriptName(package ?? throw new ArgumentNullException(nameof(package)), languageId, isPending); }
public TranslationRepository(IRequestContext context, IWebHostEnvironment hostEnvironment, ILocalTextRegistry localTextRegistry, ITypeSource typeSource) : base(context) { HostEnvironment = hostEnvironment; LocalTextRegistry = localTextRegistry; TypeSource = typeSource; }
private static void AddKeysFrom(HashSet<string> permissions, ILocalTextRegistry registry, Type type, string languageID) { var thisKeys = new List<string>(); foreach (var member in type.GetFields(BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic)) { if (member.FieldType != typeof(String)) continue; var key = member.GetValue(null) as string; if (key == null) continue; DescriptionAttribute descr; if (key.EndsWith(":")) { if (registry != null) { descr = member.GetCustomAttribute<DescriptionAttribute>(); registry.Add(languageID, "Permission." + key, descr != null ? descr.Description : member.Name); } continue; } thisKeys.Add(key); if (permissions != null) permissions.Add(key); if (registry != null) { descr = member.GetCustomAttribute<DescriptionAttribute>(); registry.Add(languageID, "Permission." + key, descr != null ? descr.Description : member.Name); } } if (registry != null && thisKeys.Count > 0) { var displayName = type.GetCustomAttribute<DisplayNameAttribute>(); var lastColonIndex = thisKeys[0].LastIndexOf(":"); if (displayName != null && lastColonIndex > 0 && lastColonIndex < thisKeys[0].Length - 1 && thisKeys.TrueForAll(x => x.LastIndexOf(":") == lastColonIndex)) { registry.Add(languageID, "Permission." + thisKeys[0].Substring(0, lastColonIndex + 1), displayName.DisplayName); } } foreach (var nested in type.GetNestedTypes(BindingFlags.Public | BindingFlags.DeclaredOnly)) AddKeysFrom(permissions, registry, nested, languageID); }
public static void InitializeLocalTexts(ILocalTextRegistry textRegistry, IWebHostEnvironment env) { textRegistry.AddNestedTexts(); textRegistry.AddNestedPermissions(); textRegistry.AddEnumTexts(); textRegistry.AddRowTexts(); textRegistry.AddJsonTexts(Path.Combine(env.WebRootPath, "Scripts/serenity/texts".Replace('/', Path.DirectorySeparatorChar))); textRegistry.AddJsonTexts(Path.Combine(env.WebRootPath, "Scripts/site/texts".Replace('/', Path.DirectorySeparatorChar))); textRegistry.AddJsonTexts(Path.Combine(env.ContentRootPath, "App_Data/texts".Replace('/', Path.DirectorySeparatorChar))); }
/// <summary> /// Adds translation from a hierarchical local text dictionary parsed from JSON file. /// </summary> /// <param name="nested">Object parsed from local text JSON string</param> /// <param name="prefix">Prefix to prepend before local text keys</param> /// <param name="languageID">Language ID</param> /// <param name="registry">Registry</param> public static void AddFromNestedDictionary(IDictionary<string, JToken> nested, string prefix, string languageID, ILocalTextRegistry registry = null) { if (nested == null) throw new ArgumentNullException("nested"); var target = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); ProcessNestedDictionary(nested, prefix, target); registry = registry ?? Dependency.Resolve<ILocalTextRegistry>(); foreach (var pair in target) registry.Add(languageID, pair.Key, pair.Value); }
/// <summary> /// Adds the row texts. /// </summary> /// <param name="registry">The registry.</param> /// <param name="rowInstances">The row instances.</param> /// <param name="languageID">The language identifier.</param> /// <exception cref="ArgumentNullException"> /// registry /// or /// rowInstances /// </exception> public static void AddRowTexts(this ILocalTextRegistry registry, IEnumerable <IRow> rowInstances, string languageID = LocalText.InvariantLanguageID) { var provider = registry ?? throw new ArgumentNullException(nameof(registry)); if (rowInstances == null) { throw new ArgumentNullException(nameof(rowInstances)); } foreach (var row in rowInstances) { var fields = row.Fields; var prefix = fields.LocalTextPrefix; foreach (var field in row.Fields) { LocalText lt = field.Caption; if (lt != null && !lt.Key.IsEmptyOrNull()) { if (lt is InitializedLocalText initialized) { provider.Add(languageID, initialized.Key, initialized.InitialText); } else { if (!lt.Key.StartsWith("Db.")) { var key = "Db." + prefix + "." + (field.PropertyName ?? field.Name); provider.Add(languageID, key, lt.Key); field.Caption = new InitializedLocalText(key, lt.Key); } } } } var displayName = row.GetType().GetCustomAttribute <DisplayNameAttribute>(); if (displayName != null) { provider.Add(languageID, "Db." + prefix + ".EntityPlural", displayName.DisplayName); } var instanceName = row.GetType().GetCustomAttribute <InstanceNameAttribute>(); if (instanceName != null) { provider.Add(languageID, "Db." + prefix + ".EntitySingular", instanceName.InstanceName); } } }
/// <summary> /// Adds translations from static nested local text classes marked with NestedLocalTextAttribute. /// </summary> public static void AddNestedTexts(this ILocalTextRegistry registry, ITypeSource typeSource) { if (typeSource == null) { throw new ArgumentNullException(nameof(typeSource)); } foreach (var type in typeSource.GetTypesWithAttribute(typeof(NestedLocalTextsAttribute))) { var attr = type.GetCustomAttribute <NestedLocalTextsAttribute>(); if (attr != null) { Initialize(registry, type, attr.LanguageID ?? LocalText.InvariantLanguageID, attr.Prefix ?? ""); } } }
public static void Initialize(IEnumerable<Assembly> assemblies, ILocalTextRegistry registry = null) #endif { assemblies = assemblies ?? ExtensibilityHelper.SelfAssemblies; if (assemblies == null) throw new ArgumentNullException("assemblies"); foreach (var assembly in assemblies) foreach (var type in assembly.GetTypes()) { var attr = type.GetCustomAttribute<NestedLocalTextsAttribute>(); if (attr != null) { Initialize(registry, type, attr.LanguageID ?? LocalText.InvariantLanguageID, attr.Prefix ?? ""); } } }
public static void Initialize(string languageID = LocalText.InvariantLanguageID, ILocalTextRegistry registry = null) #endif { var provider = registry ?? Dependency.Resolve<ILocalTextRegistry>(); foreach (var row in RowRegistry.EnumerateRows()) { var fields = row.GetFields(); var prefix = fields.LocalTextPrefix; foreach (var field in row.GetFields()) { LocalText lt = field.Caption; if (lt != null && !lt.Key.IsEmptyOrNull()) { var initialized = lt as InitializedLocalText; if (initialized != null) { provider.Add(languageID, initialized.Key, initialized.InitialText); } else { if (!lt.Key.StartsWith("Db.")) { var key = "Db." + prefix + "." + (field.PropertyName ?? field.Name); provider.Add(languageID, key, lt.Key); field.Caption = new InitializedLocalText(key, lt.Key); } } } } var displayName = row.GetType().GetCustomAttribute<DisplayNameAttribute>(); if (displayName != null) provider.Add(languageID, "Db." + prefix + ".EntityPlural", displayName.DisplayName); var instanceName = row.GetType().GetCustomAttribute<InstanceNameAttribute>(); if (instanceName != null) provider.Add(languageID, "Db." + prefix + ".EntitySingular", instanceName.InstanceName); } }
public static string GetLocalTextPackageScript(ILocalTextRegistry registry, LocalTextPackages packages, string package, string languageId, bool isPending) { if (package == null) { throw new ArgumentNullException(nameof(package)); } if (packages == null) { throw new ArgumentNullException(nameof(packages)); } if (!packages.TryGetValue(package, out string includes)) { includes = null; } return(GetLocalTextPackageScript(registry, includes, languageId, isPending)); }
public static void Initialize(IEnumerable <Assembly> assemblies, ILocalTextRegistry registry = null) #endif { assemblies = assemblies ?? ExtensibilityHelper.SelfAssemblies; if (assemblies == null) { throw new ArgumentNullException("assemblies"); } foreach (var assembly in assemblies) { foreach (var type in assembly.GetTypes()) { var attr = type.GetCustomAttribute <NestedLocalTextsAttribute>(); if (attr != null) { Initialize(registry, type, attr.LanguageID ?? LocalText.InvariantLanguageID, attr.Prefix ?? ""); } } } }
/// <summary> /// Gets permission keys and adds texts if any from static nested permission key /// classes marked with NestedPermissionKeys attribute. /// </summary> public static HashSet <string> AddNestedPermissions(this ILocalTextRegistry registry, ITypeSource typeSource) { if (typeSource == null) { throw new ArgumentNullException(nameof(typeSource)); } var permissions = new HashSet <string>(StringComparer.OrdinalIgnoreCase); foreach (var type in typeSource.GetTypesWithAttribute(typeof(NestedPermissionKeysAttribute))) { var attr = type.GetCustomAttribute <NestedPermissionKeysAttribute>(); if (attr != null) { AddKeysFrom(permissions, registry, type, attr.LanguageID ?? LocalText.InvariantLanguageID); } } return(permissions); }
private static void Initialize(ILocalTextRegistry registry, Type type, string languageID, string prefix) { if (registry == null) { throw new ArgumentNullException(nameof(registry)); } var provider = registry; foreach (var member in type.GetMembers( BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)) { var fi = member as FieldInfo; if (fi != null && fi.FieldType == typeof(LocalText)) { if (fi.GetValue(null) is LocalText value) { if (value is InitializedLocalText initialized) { provider.Add(languageID, initialized.Key, initialized.InitialText); } else { provider.Add(languageID, prefix + fi.Name, value.Key); fi.SetValue(null, new InitializedLocalText(prefix + fi.Name, value.Key)); } } } } foreach (var nested in type.GetNestedTypes(BindingFlags.Public | BindingFlags.DeclaredOnly)) { var name = nested.Name; if (name.EndsWith("_")) { name = name[0..^ 1];
private static void Initialize(ILocalTextRegistry registry, Type type, string languageID, string prefix) { var provider = registry ?? Dependency.Resolve <ILocalTextRegistry>(); foreach (var member in type.GetMembers(BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly)) { var fi = member as FieldInfo; if (fi != null && fi.FieldType == typeof(LocalText)) { var value = fi.GetValue(null) as LocalText; if (value != null) { var initialized = value as InitializedLocalText; if (initialized != null) { provider.Add(languageID, initialized.Key, initialized.InitialText); } else { provider.Add(languageID, prefix + fi.Name, value.Key); fi.SetValue(null, new InitializedLocalText(prefix + fi.Name, value.Key)); } } } } foreach (var nested in type.GetNestedTypes(BindingFlags.Public | BindingFlags.DeclaredOnly)) { var name = nested.Name; if (name.EndsWith("_")) { name = name.Substring(0, name.Length - 1); } Initialize(registry, nested, languageID, prefix + name + "."); } }
public static void AddFromFilesInFolder(string path, ILocalTextRegistry registry = null) #endif { if (path == null) { throw new ArgumentNullException("path"); } if (!Directory.Exists(path)) { return; } var files = Directory.GetFiles(path, "*.json", SearchOption.AllDirectories); Array.Sort(files); foreach (var file in files) { var texts = JsonConfigHelper.LoadConfig <Dictionary <string, JToken> >(file); var langID = Path.GetFileNameWithoutExtension(Path.GetFileName(file)); var idx = langID.LastIndexOf("."); if (idx >= 0) { langID = langID.Substring(idx + 1); } if (langID.ToLowerInvariant() == "invariant") { langID = ""; } AddFromNestedDictionary(texts, "", langID, registry); } }
public static void AddEnumTexts(this ILocalTextRegistry registry, System.Collections.Generic.IEnumerable <System.Reflection.Assembly> assemblies = null, string languageID = LocalText.InvariantLanguageID) { Serenity.Localization.EnumLocalTextRegistration.Initialize(assemblies, languageID); }
/// <summary> /// Adds translations from JSON files at specified path. File names in this directory should be in format /// {anyprefix}.{languageID}.json where {languageID} is a language code like 'en', 'en-GB' etc. /// </summary> /// <param name="path">Path containing JSON files</param> /// <param name="registry">Registry</param> #if !NET45 public static void AddJsonTexts(this ILocalTextRegistry registry, string path)
/// <summary> /// Adds translation from a hierarchical local text dictionary parsed from JSON file. /// </summary> /// <param name="nested">Object parsed from local text JSON string</param> /// <param name="prefix">Prefix to prepend before local text keys</param> /// <param name="languageID">Language ID</param> /// <param name="registry">Registry</param> public static void AddFromNestedDictionary(IDictionary <string, JToken> nested, string prefix, string languageID, ILocalTextRegistry registry = null) { if (nested == null) { throw new ArgumentNullException("nested"); } var target = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); ProcessNestedDictionary(nested, prefix, target); registry = registry ?? Dependency.Resolve <ILocalTextRegistry>(); foreach (var pair in target) { registry.Add(languageID, pair.Key, pair.Value); } }
public static void Initialize(IEnumerable <Assembly> assemblies, string languageID = LocalText.InvariantLanguageID, ILocalTextRegistry registry = null) #endif { assemblies = assemblies ?? ExtensibilityHelper.SelfAssemblies; if (assemblies == null) { throw new ArgumentNullException("assemblies"); } var provider = registry ?? Dependency.Resolve <ILocalTextRegistry>(); foreach (var assembly in assemblies) { foreach (var type in assembly.GetTypes()) { if (type.IsEnum) { var enumKey = EnumMapper.GetEnumTypeKey(type); foreach (var name in Enum.GetNames(type)) { var member = type.GetMember(name); if (member.Length == 0) { continue; } var descAttr = member[0].GetCustomAttribute <DescriptionAttribute>(); if (descAttr != null) { provider.Add(languageID, "Enums." + enumKey + "." + name, descAttr.Description); } } } } } }
/// <summary> /// Adds local text translations defined implicitly by Description attributes in /// enumeration classes. Only enum values that has Description attribute are added as /// local text. By default, enums are registered in format: /// "Enums.{EnumerationTypeFullName}.{EnumValueName}". EnumerationTypeFullName, is /// fullname of the enumeration type. This can be overridden by attaching a EnumKey /// attribute. /// </summary> /// <param name="assemblies">Assemblies to search for enumeration classes in</param> /// <param name="languageID">Language ID texts will be added (default is invariant language)</param> /// <param name="registry">Registry</param> #if COREFX public static void AddEnumTexts(this ILocalTextRegistry registry, IEnumerable <Assembly> assemblies = null, string languageID = LocalText.InvariantLanguageID)
/// <summary> /// Adds translations from static nested local text classes marked with NestedLocalTextAttribute. /// </summary> #if COREFX public static void AddNestedTexts(this ILocalTextRegistry registry, IEnumerable <Assembly> assemblies = null)
public static void AddJsonTexts(this ILocalTextRegistry registry, string path) { Serenity.Localization.JsonLocalTextRegistration.AddFromFilesInFolder(path, registry); }
public static void AddRowTexts(this ILocalTextRegistry registry, string languageID = LocalText.InvariantLanguageID)
public static void Initialize(string languageID = LocalText.InvariantLanguageID, ILocalTextRegistry registry = null) #endif { var provider = registry ?? Dependency.Resolve <ILocalTextRegistry>(); foreach (var row in RowRegistry.EnumerateRows()) { var fields = row.GetFields(); var prefix = fields.LocalTextPrefix; foreach (var field in row.GetFields()) { LocalText lt = field.Caption; if (lt != null && !lt.Key.IsEmptyOrNull()) { var initialized = lt as InitializedLocalText; if (initialized != null) { provider.Add(languageID, initialized.Key, initialized.InitialText); } else { if (!lt.Key.StartsWith("Db.")) { var key = "Db." + prefix + "." + (field.PropertyName ?? field.Name); provider.Add(languageID, key, lt.Key); field.Caption = new InitializedLocalText(key, lt.Key); } } } } var displayName = row.GetType().GetCustomAttribute <DisplayNameAttribute>(); if (displayName != null) { provider.Add(languageID, "Db." + prefix + ".EntityPlural", displayName.DisplayName); } var instanceName = row.GetType().GetCustomAttribute <InstanceNameAttribute>(); if (instanceName != null) { provider.Add(languageID, "Db." + prefix + ".EntitySingular", instanceName.InstanceName); } } }
public FallbackLocalTextRegistry(ILocalTextRegistry localTextRegistry) { Check.NotNull(localTextRegistry, "localTextRegistry"); this.localTextRegistry = localTextRegistry; }
/// <summary> /// Creates a new default text localizer instance /// </summary> /// <param name="registry"></param> public DefaultTextLocalizer(ILocalTextRegistry registry) { this.registry = registry ?? throw new ArgumentNullException(nameof(registry)); }
public static void AddNestedTexts(this ILocalTextRegistry registry, System.Collections.Generic.IEnumerable <System.Reflection.Assembly> assemblies = null) { Serenity.Localization.NestedLocalTextRegistration.Initialize(assemblies, registry); }
public static string GetLocalTextPackageScript(ILocalTextRegistry registry, string includes, string languageId, bool isPending) { if (registry == null) { throw new ArgumentNullException(nameof(registry)); } var list = new List <KeyValuePair <string, string> >(); if (!string.IsNullOrEmpty(includes)) { var regex = new Regex(includes, RegexOptions.Compiled | RegexOptions.IgnoreCase); var texts = registry is LocalTextRegistry ltr?ltr.GetAllAvailableTextsInLanguage(languageId, isPending) : new Dictionary <string, string>(); foreach (var pair in texts) { if (regex.IsMatch(pair.Key)) { list.Add(pair); } } } list.Sort((i1, i2) => string.CompareOrdinal(i1.Key, i2.Key)); StringBuilder jwBuilder = new("Q.LT.add("); JsonWriter jw = new JsonTextWriter(new StringWriter(jwBuilder)); jw.WriteStartObject(); List <string> stack = new(); int stackCount = 0; for (int i = 0; i < list.Count; i++) { var pair = list[i]; // we can't handle keys that ends with '.' for now (possible clash between "site." and "site") if (pair.Key.Length == 0 || pair.Key[^ 1] == '.') { continue; } var parts = pair.Key.Split('.'); if (parts.Length == 0) { continue; } int same = 0; if (stackCount > 0) { while (same < stackCount && same < parts.Length && stack[same] == parts[same]) { same++; } for (int level = same; level < stackCount; level++) { jw.WriteEndObject(); } stackCount = same; } for (int level = same; level < parts.Length - 1; level++) { string part = parts[level]; if (stack.Count > level) { stack[level] = part; } else { stack.Add(part); } jw.WritePropertyName(part); jw.WriteStartObject(); } stackCount = parts.Length - 1; if (same != parts.Length) { string part = parts[^ 1];
public static void AddRowTexts(this ILocalTextRegistry registry, string languageID = LocalText.InvariantLanguageID) { Serenity.Localization.EntityLocalTexts.Initialize(languageID, registry); }