using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Globalization;
using Microsoft.Win32;
namespace FastReport.Utils
{
///
/// Used to get localized values from the language resource file.
///
///
/// The resource file used by default is english. To load another locale, call
/// the method. It should be done at application start
/// before you use any FastReport classes.
///
public static partial class Res
{
private static Dictionary LocalesCache { get; }
internal static CultureInfo CurrentCulture { get; private set; }
private static XmlDocument FLocale;
private static XmlDocument FBuiltinLocale;
private const string FBadResult = "NOT LOCALIZED!";
///
/// Gets or set the folder that contains localization files (*.frl).
///
public static string LocaleFolder
{
get
{
Report.EnsureInit();
string folder = Config.Root.FindItem("Language").GetProp("Folder");
// check the registry
#if !CROSSPLATFORM
if (String.IsNullOrEmpty(folder) && !Config.WebMode)
{
RegistryKey key = Registry.CurrentUser.OpenSubKey("Software").OpenSubKey("FastReports");
if (key != null)
{
key = key.OpenSubKey("FastReport.Net");
if (key != null)
folder = (string)key.GetValue("LocalizationFolder", "");
}
}
#endif
// get application folder
if (String.IsNullOrEmpty(folder))
folder = Config.ApplicationFolder;
return folder;
}
set
{
Config.Root.FindItem("Language").SetProp("Folder", value);
}
}
///
/// Returns the current UI locale name, for example "en".
///
public static string LocaleName
{
get
{
return FLocale.Root.GetProp("Name");
}
}
internal static string DefaultLocaleName
{
get { return Config.Root.FindItem("Language").GetProp("Name"); }
set { Config.Root.FindItem("Language").SetProp("Name", value); }
}
private static void LoadBuiltinLocale()
{
FLocale = new XmlDocument();
FBuiltinLocale = FLocale;
using (Stream stream = ResourceLoader.GetStream("en.xml"))
{
FLocale.Load(stream);
CultureInfo enCulture;
try
{
enCulture = CultureInfo.GetCultureInfo("en");
}
catch // InvariantGlobalization mod fix (#939)
{
enCulture = CultureInfo.InvariantCulture;
}
CurrentCulture = enCulture;
if (!LocalesCache.ContainsKey(enCulture))
LocalesCache.Add(enCulture, FLocale);
}
}
private static void SetCurrentCulture()
{
try
{
CurrentCulture = CultureInfo.GetCultureInfo(LocaleName);
}
catch
{
}
}
///
/// Loads the locale from a file.
///
/// The name of the file that contains localized strings.
public static void LoadLocale(string fileName)
{
Report.EnsureInit();
if (File.Exists(fileName))
{
FLocale = new XmlDocument();
FLocale.Load(fileName);
SetCurrentCulture();
}
else
LoadEnglishLocale();
}
///
/// Loads and caches the locale from information.
/// Notes: *.frl the localization file is looked for in
/// To work correctly, it is recommended to install FastReport.Localization package
///
///
public static void LoadLocale(CultureInfo culture)
{
if (culture == CultureInfo.InvariantCulture)
{
CurrentCulture = culture;
FLocale = FBuiltinLocale;
return;
}
if (LocalesCache.ContainsKey(culture))
{
CurrentCulture = culture;
FLocale = LocalesCache[culture];
return;
}
// if culture not found, we try find parent culture
var parent = culture.Parent;
if (parent != CultureInfo.InvariantCulture)
{
if (LocalesCache.ContainsKey(parent))
{
CurrentCulture = parent;
FLocale = LocalesCache[parent];
return;
}
// in some cultures, parent have self parent
if (parent.Parent != CultureInfo.InvariantCulture)
if (LocalesCache.ContainsKey(parent.Parent))
{
CurrentCulture = parent.Parent;
FLocale = LocalesCache[parent.Parent];
return;
}
}
string localeFolder = LocaleFolder;
string localeFile = string.Empty;
if (Directory.Exists(localeFolder))
{
localeFile = FindLocaleFile(ref culture, localeFolder);
}
// Find 'Localization' directory from FastReport.Localization package
if (string.IsNullOrEmpty(localeFile))
{
localeFolder = Path.Combine(Config.ApplicationFolder, "Localization");
if (Directory.Exists(localeFolder))
{
localeFile = FindLocaleFile(ref culture, localeFolder);
}
}
if (!string.IsNullOrEmpty(localeFile))
{
Report.EnsureInit();
var newLocale = new XmlDocument();
newLocale.Load(localeFile);
CurrentCulture = culture;
FLocale = newLocale;
LocalesCache.Add(culture, newLocale);
}
}
private static string FindLocaleFile(ref CultureInfo culture, string localeFolder)
{
var files = Directory.GetFiles(localeFolder, "*.frl");
CultureInfo parent = culture.Parent;
foreach (var file in files)
{
var filename = Path.GetFileNameWithoutExtension(file);
if (filename == culture.EnglishName)
{
return file;
}
else
{
if (filename == parent.EnglishName)
{
culture = parent;
return file;
}
}
}
return null;
}
///
/// Loads the locale from a stream.
///
/// The stream that contains localized strings.
public static void LoadLocale(Stream stream)
{
Report.EnsureInit();
FLocale = new XmlDocument();
FLocale.Load(stream);
SetCurrentCulture();
}
///
/// Loads the english locale.
///
public static void LoadEnglishLocale()
{
CurrentCulture = CultureInfo.GetCultureInfo("en");
FLocale = FBuiltinLocale;
}
internal static void LoadDefaultLocale()
{
if (!Directory.Exists(LocaleFolder))
return;
if (String.IsNullOrEmpty(DefaultLocaleName))
{
// locale is set to "Auto"
CultureInfo currentCulture = CultureInfo.CurrentCulture;
LoadLocale(currentCulture);
}
else
{
// locale is set to specific name
LoadLocale(Path.Combine(LocaleFolder, DefaultLocaleName + ".frl"));
}
}
///
/// Gets a string with specified ID.
///
/// The resource ID.
/// The localized string.
///
/// Since the locale file is xml-based, it may contain several xml node levels. For example,
/// the file contains the following items:
///
/// <Objects>
/// <Report Text="Report"/>
/// <Bands Text="Bands">
/// <ReportTitle Text="Report Title"/>
/// </Bands>
/// </Objects>
///
/// To get the localized "ReportTitle" value, you should pass the following ID
/// to this method: "Objects,Bands,ReportTitle".
///
public static string Get(string id)
{
string result = Get(id, FLocale);
// if no item found, try built-in (english) locale
if (string.IsNullOrEmpty(result))
{
if (FLocale != FBuiltinLocale)
{
result = Get(id, FBuiltinLocale);
if (string.IsNullOrEmpty(result))
result = id + " " + FBadResult;
}
else
result = id + " " + FBadResult;
}
return result;
}
private static string Get(string id, XmlDocument locale)
{
string[] categories = id.Split(',');
XmlItem xi = locale.Root;
foreach (string category in categories)
{
int i = xi.Find(category);
if (i == -1)
return null;
xi = xi[i];
}
return xi.GetProp("Text");
}
///
/// Get builtin string.
///
///
///
public static string GetBuiltin(string id)
{
return Get(id, FBuiltinLocale);
}
///
/// Replaces the specified locale string with the new value.
///
/// Comma-separated path to the existing locale string.
/// The new string.
///
/// Use this method if you want to replace some existing locale value with the new one.
///
///
///
/// Res.Set("Messages,SaveChanges", "My text that will appear when you close the designer");
///
///
public static void Set(string id, string value)
{
string[] categories = id.Split(',');
XmlItem xi = FLocale.Root;
foreach (string category in categories)
{
xi = xi.FindItem(category);
}
xi.SetProp("Text", value);
}
///
/// Tries to get a string with specified ID.
///
/// The resource ID.
/// The localized value, if specified ID exists; otherwise, the ID itself.
public static string TryGet(string id)
{
string result = Get(id);
if (result.IndexOf(FBadResult) != -1)
result = id;
return result;
}
///
/// Tries to get builtin string with specified ID.
///
///
///
public static string TryGetBuiltin(string id)
{
string result = GetBuiltin(id);
if (string.IsNullOrEmpty(result))
result = id;
return result;
}
///
/// Checks if specified ID exists.
///
/// The resource ID.
/// true if specified ID exists.
public static bool StringExists(string id)
{
return Get(id).IndexOf(FBadResult) == -1;
}
static Res()
{
LocalesCache = new Dictionary();
LoadBuiltinLocale();
ResDesignExt();
}
static partial void ResDesignExt();
}
///
/// Used to access to resource IDs inside the specified branch.
///
///
/// Using the method, you have to specify the full path to your resource.
/// Using this class, you can shorten the path:
///
/// // using the Res.Get method
/// miKeepTogether = new ToolStripMenuItem(Res.Get("ComponentMenu,HeaderBand,KeepTogether"));
/// miResetPageNumber = new ToolStripMenuItem(Res.Get("ComponentMenu,HeaderBand,ResetPageNumber"));
/// miRepeatOnEveryPage = new ToolStripMenuItem(Res.Get("ComponentMenu,HeaderBand,RepeatOnEveryPage"));
///
/// // using MyRes.Get method
/// MyRes res = new MyRes("ComponentMenu,HeaderBand");
/// miKeepTogether = new ToolStripMenuItem(res.Get("KeepTogether"));
/// miResetPageNumber = new ToolStripMenuItem(res.Get("ResetPageNumber"));
/// miRepeatOnEveryPage = new ToolStripMenuItem(res.Get("RepeatOnEveryPage"));
///
///
///
public class MyRes
{
private string category;
///
/// Gets a string with specified ID inside the main branch.
///
/// The resource ID.
/// The localized value.
public string Get(string id)
{
if (id != "")
return Res.Get(category + "," + id);
else
return Res.Get(category);
}
///
/// Initializes a new instance of the class with spevified branch.
///
/// The main resource branch.
public MyRes(string category)
{
this.category = category;
}
}
///
/// Localized CategoryAttribute class.
///
public class SRCategory : CategoryAttribute
{
///
protected override string GetLocalizedString(string value)
{
return Res.TryGet("Properties,Categories," + value);
}
///
/// Initializes a new instance of the SRCategory class.
///
/// The category name.
public SRCategory(string value)
: base(value)
{
}
}
}