diff --git a/Claunia.Localization.Core/Claunia.Localization.Core.csproj b/Claunia.Localization.Core/Claunia.Localization.Core.csproj
new file mode 100644
index 0000000..185a24c
--- /dev/null
+++ b/Claunia.Localization.Core/Claunia.Localization.Core.csproj
@@ -0,0 +1,7 @@
+
+
+
+ netstandard1.6
+
+
+
diff --git a/Claunia.Localization.Core/Language.cs b/Claunia.Localization.Core/Language.cs
new file mode 100644
index 0000000..15b76ad
--- /dev/null
+++ b/Claunia.Localization.Core/Language.cs
@@ -0,0 +1,32 @@
+namespace Claunia.Localization.Core
+{
+ /// Enumerates programming languages for parameters
+ public enum Language
+ {
+ C,
+ ObjectiveC,
+ CSharp,
+ Shell,
+ Python,
+ Lisp,
+ EmacsLisp,
+ PythonBraced,
+ Librep,
+ Scheme,
+ Smalltalk,
+ Java,
+ Awk,
+ ObjectPascal,
+ Ycp,
+ Tcl,
+ Perl,
+ Php,
+ GccInternal,
+ GfcInternal,
+ Qt,
+ QtPlural,
+ Kde,
+ Boost,
+ JavaScript
+ }
+}
\ No newline at end of file
diff --git a/Claunia.Localization.Core/Localization.cs b/Claunia.Localization.Core/Localization.cs
new file mode 100644
index 0000000..9469e25
--- /dev/null
+++ b/Claunia.Localization.Core/Localization.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Linq;
+
+namespace Claunia.Localization.Core
+{
+ ///
+ /// Represents the localization
+ ///
+ public class Localization
+ {
+ readonly ObservableCollection messages;
+ readonly ObservableCollection translators;
+
+ public Localization()
+ {
+ Creation = DateTime.UtcNow;
+ LastWritten = DateTime.UtcNow;
+ Project = new Project();
+ Project.ProjectModified += OnModified;
+ translators = new ObservableCollection();
+ Translators = new ReadOnlyObservableCollection(translators);
+ translators.CollectionChanged += OnModified;
+ messages = new ObservableCollection();
+ Messages = new ReadOnlyObservableCollection(messages);
+ messages.CollectionChanged += OnModified;
+ }
+
+ /// Date this localization has been created
+ public DateTime Creation { get; }
+ /// Date this localization has been modified
+ public DateTime LastWritten { get; private set; }
+ /// Project this localization belongs to
+ public Project Project { get; }
+ /// List of translators that have worked in this localization
+ public ReadOnlyObservableCollection Translators { get; }
+ ///
+ /// List of messages present in this localization
+ ///
+ public ReadOnlyObservableCollection Messages { get; }
+
+ void OnModified(object sender, EventArgs args)
+ {
+ LastWritten = DateTime.UtcNow;
+ }
+
+ ///
+ /// Adds a new translator to the localization
+ ///
+ /// Translator full name, english-form, in ASCII
+ /// Translator e-mail
+ /// Translator full name, in native form
+ /// The new translator
+ public Translator NewTranslator(string name, string email, string nativeName = null)
+ {
+ int id = translators.Max(t => t.Id) + 1;
+ Translator translator = new Translator(id) {Name = name, Email = email};
+ translator.Modified += OnModified;
+
+ translators.Add(translator);
+
+ return translator;
+ }
+
+ ///
+ /// Removes a translator from the localization
+ ///
+ /// Translator to remove
+ /// true if the translator has been removed successfully, false otherwise
+ public bool RemoveTranslator(Translator translator) => !(translator is null) && translators.Remove(translator);
+
+ ///
+ /// Removes a translator from the localization
+ ///
+ /// ID of the translator to remove
+ /// true if the translator has been removed successfully, false otherwise
+ public bool RemoveTranslator(int id)
+ {
+ Translator translator = translators.FirstOrDefault(t => t.Id == id);
+ return !(translator is null) && translators.Remove(translator);
+ }
+
+ public Message NewMessage()
+ {
+ Message message = new Message();
+ message.Modified += OnModified;
+
+ messages.Add(message);
+
+ return message;
+ }
+
+ public bool RemoveMessage(Message message) => !(message is null) && messages.Remove(message);
+
+ public bool RemoveMessage(string id)
+ {
+ Message message = messages.FirstOrDefault(t => t.Id == id);
+ return !(message is null) && messages.Remove(message);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Claunia.Localization.Core/LocalizedString.cs b/Claunia.Localization.Core/LocalizedString.cs
new file mode 100644
index 0000000..ebfbd45
--- /dev/null
+++ b/Claunia.Localization.Core/LocalizedString.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.ObjectModel;
+
+namespace Claunia.Localization.Core
+{
+ ///
+ /// Represent a localized string
+ ///
+ public sealed class LocalizedString
+ {
+ readonly ObservableCollection plurals;
+ string comments;
+ string locale;
+ internal EventHandler Modified;
+ string singular;
+ int? translator;
+
+ internal LocalizedString()
+ {
+ plurals = new ObservableCollection();
+ plurals.CollectionChanged += (sender, args) => Modified?.Invoke(this, EventArgs.Empty);
+ Plurals = new ReadOnlyObservableCollection(plurals);
+ }
+
+ ///
+ /// Contains the singular, or only, text for this string
+ ///
+ public string Singular
+ {
+ get => singular;
+ set
+ {
+ singular = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Which locale this string is in. Format is ll_CC with ll as ISO 639-2 language code and CC as ISO 3166 two letter
+ /// uppercase country code. _CC can be omitted.
+ ///
+ public string Locale
+ {
+ get => locale;
+ set
+ {
+ locale = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// ID of the translator, in this localization, that wrote this string
+ ///
+ public int? Translator
+ {
+ get => translator;
+ set
+ {
+ translator = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Translator comments
+ ///
+ public string Comments
+ {
+ get => comments;
+ set
+ {
+ comments = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Plural forms for this string
+ ///
+ public ReadOnlyObservableCollection Plurals { get; }
+
+ ///
+ /// Initializes a new plural in this message
+ ///
+ /// The newly initialized plural
+ public Plural NewPlural()
+ {
+ Plural parameter = new Plural();
+ parameter.Modified += Modified;
+ plurals.Add(parameter);
+
+ return parameter;
+ }
+
+ ///
+ /// Removes a plural
+ ///
+ /// The plural to remove
+ /// true if the plural string has been removed successfully, false otherwise
+ public bool RemovePlural(Plural parameter) => plurals.Remove(parameter);
+ }
+}
\ No newline at end of file
diff --git a/Claunia.Localization.Core/Message.cs b/Claunia.Localization.Core/Message.cs
new file mode 100644
index 0000000..1c6b8b4
--- /dev/null
+++ b/Claunia.Localization.Core/Message.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.ObjectModel;
+
+namespace Claunia.Localization.Core
+{
+ ///
+ /// Represent a message and its translations
+ ///
+ public sealed class Message
+ {
+ readonly ObservableCollection parameters;
+ readonly ObservableCollection translations;
+ string comments;
+ string context;
+ string id;
+ internal EventHandler Modified;
+ string previousContext;
+ string reference;
+
+ internal Message()
+ {
+ Source = new LocalizedString();
+ Source.Modified += Modified;
+ translations = new ObservableCollection();
+ translations.CollectionChanged += (sender, args) => Modified?.Invoke(this, EventArgs.Empty);
+ parameters = new ObservableCollection();
+ parameters.CollectionChanged += (sender, args) => Modified?.Invoke(this, EventArgs.Empty);
+ Translations = new ReadOnlyObservableCollection(translations);
+ Parameters = new ReadOnlyObservableCollection(parameters);
+ }
+
+ ///
+ /// Describes the context where this message occurs
+ ///
+ public string Context
+ {
+ get => context;
+ set
+ {
+ context = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Describes the context where the previous version of this message occurred
+ ///
+ public string PreviousContext
+ {
+ get => previousContext;
+ set
+ {
+ previousContext = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Comments added by the original writer of the message
+ ///
+ public string Comments
+ {
+ get => comments;
+ set
+ {
+ comments = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Source code line reference or equivalent
+ ///
+ public string Reference
+ {
+ get => reference;
+ set
+ {
+ reference = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Unique identifier of this message
+ ///
+ public string Id
+ {
+ get => id;
+ set
+ {
+ id = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Original locale of this message
+ ///
+ public LocalizedString Source { get; }
+ ///
+ /// List of translations
+ ///
+ public ReadOnlyObservableCollection Translations { get; }
+ ///
+ /// List of parameters
+ ///
+ public ReadOnlyObservableCollection Parameters { get; }
+
+ ///
+ /// Initializes a new localized string in this message
+ ///
+ /// The newly initialized localized string
+ public LocalizedString NewLocalizedString()
+ {
+ LocalizedString localizedString = new LocalizedString();
+ localizedString.Modified += Modified;
+ translations.Add(localizedString);
+
+ return localizedString;
+ }
+
+ ///
+ /// Removes a localized string
+ ///
+ /// The localized string to remove
+ /// true if the localized string has been removed successfully, false otherwise
+ public bool RemoveLocalizedString(LocalizedString localizedString) => translations.Remove(localizedString);
+
+ ///
+ /// Initializes a new parameter in this message
+ ///
+ /// The newly initialized parameter
+ public Parameter NewParameter()
+ {
+ Parameter parameter = new Parameter();
+ parameter.Modified += Modified;
+ parameters.Add(parameter);
+
+ return parameter;
+ }
+
+ ///
+ /// Removes a parameter
+ ///
+ /// The parameter to remove
+ /// true if the parameter string has been removed successfully, false otherwise
+ public bool RemoveParameter(Parameter parameter) => parameters.Remove(parameter);
+ }
+}
\ No newline at end of file
diff --git a/Claunia.Localization.Core/Parameter.cs b/Claunia.Localization.Core/Parameter.cs
new file mode 100644
index 0000000..75281f4
--- /dev/null
+++ b/Claunia.Localization.Core/Parameter.cs
@@ -0,0 +1,97 @@
+using System;
+
+namespace Claunia.Localization.Core
+{
+ ///
+ /// Explanation of a parameter in a software string to help the translator understand the context and for translation
+ /// helping software to be able to provide appropriate examples
+ ///
+ public class Parameter
+ {
+ string description;
+ int index;
+ Language language;
+ long? maximum;
+ long? minimum;
+ internal EventHandler Modified;
+ string type;
+
+ ///
+ /// Index of parameter in source message
+ ///
+ public int Index
+ {
+ get => index;
+ set
+ {
+ index = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Programming language for interpretation of messages
+ ///
+ public Language Language
+ {
+ get => language;
+ set
+ {
+ language = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Simple (single-word) description of parameter type
+ ///
+ public string Type
+ {
+ get => type;
+ set
+ {
+ type = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Minimum value the parameter can get
+ ///
+ public long? Minimum
+ {
+ get => minimum;
+ set
+ {
+ minimum = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Maximum value the parameter can get
+ ///
+ public long? Maximum
+ {
+ get => maximum;
+ set
+ {
+ maximum = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Description of the parameter
+ ///
+ public string Description
+ {
+ get => description;
+ set
+ {
+ description = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Claunia.Localization.Core/Plural.cs b/Claunia.Localization.Core/Plural.cs
new file mode 100644
index 0000000..e0b33eb
--- /dev/null
+++ b/Claunia.Localization.Core/Plural.cs
@@ -0,0 +1,40 @@
+using System;
+
+namespace Claunia.Localization.Core
+{
+ ///
+ /// Contains the plural forms of a string
+ ///
+ public class Plural
+ {
+ int items;
+ internal EventHandler Modified;
+ string text;
+
+ ///
+ /// Minimum number (inclusive) of items named to use this plural form
+ ///
+ public int Items
+ {
+ get => items;
+ set
+ {
+ items = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Plural form of the string
+ ///
+ public string Text
+ {
+ get => text;
+ set
+ {
+ text = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Claunia.Localization.Core/Project.cs b/Claunia.Localization.Core/Project.cs
new file mode 100644
index 0000000..ff668e3
--- /dev/null
+++ b/Claunia.Localization.Core/Project.cs
@@ -0,0 +1,51 @@
+using System;
+
+namespace Claunia.Localization.Core
+{
+ public class Project
+ {
+ string name;
+
+ internal EventHandler ProjectModified;
+ string url;
+ string version;
+ ///
+ /// Project name
+ ///
+ public string Name
+ {
+ get => name;
+ set
+ {
+ name = value;
+ ProjectModified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Project version
+ ///
+ public string Version
+ {
+ get => version;
+ set
+ {
+ version = value;
+ ProjectModified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Project URL
+ ///
+ public string Url
+ {
+ get => url;
+ set
+ {
+ url = value;
+ ProjectModified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Claunia.Localization.Core/Translator.cs b/Claunia.Localization.Core/Translator.cs
new file mode 100644
index 0000000..c3d05bc
--- /dev/null
+++ b/Claunia.Localization.Core/Translator.cs
@@ -0,0 +1,63 @@
+using System;
+
+namespace Claunia.Localization.Core
+{
+ public class Translator
+ {
+ string email;
+ internal EventHandler Modified;
+ string name;
+ string nativeName;
+
+ internal Translator(int id)
+ {
+ Id = id;
+ }
+
+ ///
+ /// Translator ID, unique in the corresponding localization, and sequential
+ ///
+ public int Id { get; }
+
+ ///
+ /// Translator e-mail
+ ///
+ public string Email
+ {
+ get => email;
+ set
+ {
+ email = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Translator name, in ASCII, english form
+ ///
+ public string Name
+ {
+ get => name;
+ set
+ {
+ name = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ ///
+ /// Translator full name in native form
+ ///
+ public string NativeName
+ {
+ get => nativeName ?? Name;
+ set
+ {
+ if(value == name) return;
+
+ nativeName = value;
+ Modified?.Invoke(this, EventArgs.Empty);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Claunia.Localization.sln b/Claunia.Localization.sln
index 8189da1..3c0961d 100644
--- a/Claunia.Localization.sln
+++ b/Claunia.Localization.sln
@@ -4,6 +4,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Claunia.Localization", "Cla
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Claunia.Localization.Desktop", "Claunia.Localization\Claunia.Localization.Desktop\Claunia.Localization.Desktop.csproj", "{258FEA96-4E93-4C8F-94EF-1319F93B350D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Claunia.Localization.Core", "Claunia.Localization.Core\Claunia.Localization.Core.csproj", "{CFDFDFC7-9662-4C50-B86A-9E579ACD3DF9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -20,5 +22,9 @@ Global
{258FEA96-4E93-4C8F-94EF-1319F93B350D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{258FEA96-4E93-4C8F-94EF-1319F93B350D}.Release|Any CPU.Build.0 = Release|Any CPU
{258FEA96-4E93-4C8F-94EF-1319F93B350D}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {CFDFDFC7-9662-4C50-B86A-9E579ACD3DF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CFDFDFC7-9662-4C50-B86A-9E579ACD3DF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CFDFDFC7-9662-4C50-B86A-9E579ACD3DF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CFDFDFC7-9662-4C50-B86A-9E579ACD3DF9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal