diff --git a/src/main/java/de/marhali/easyi18n/model/translation/Translation.java b/src/main/java/de/marhali/easyi18n/model/translation/Translation.java
new file mode 100644
index 0000000..e5e59cd
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/model/translation/Translation.java
@@ -0,0 +1,175 @@
+package de.marhali.easyi18n.model.translation;
+
+import de.marhali.easyi18n.model.translation.variant.Plural;
+
+import de.marhali.easyi18n.model.translation.variant.ContextMap;
+import de.marhali.easyi18n.model.translation.variant.LocaleMap;
+import de.marhali.easyi18n.model.translation.variant.PluralMap;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Represents the set values behind a specific translation.
+ * Consideration is given to context, pluralization and locale.
+ *
+ * Data structure can be imagined like a layered map of: context => plural => locale
+ *
+ * @author marhali
+ */
+public class Translation {
+
+ private @Nullable String description;
+ private @NotNull ContextMap contexts;
+ private @Nullable Object misc;
+
+ public Translation(@Nullable String description, @NotNull ContextMap contexts, @Nullable Object misc) {
+ this.description = description;
+ this.contexts = contexts;
+ this.misc = misc;
+ }
+
+ public Translation() {
+ this(null, new ContextMap(), null);
+ }
+
+ /**
+ * Retrieve additional description for this translation
+ * @return Description
+ */
+ public @Nullable String getDescription() {
+ return description;
+ }
+
+ /**
+ * Override or set description for this translation
+ * @param description Description
+ */
+ public void setDescription(@Nullable String description) {
+ this.description = description;
+ }
+
+ /**
+ * Retrieve all contexts for this translation
+ * @return Map of specified contexts
+ */
+ public @NotNull ContextMap getContexts() {
+ return contexts;
+ }
+
+ /**
+ * Check whether a specific context has been set for this translation
+ * @param context Context to check
+ * @return True if context has been configured otherwise false
+ */
+ public boolean hasContext(@NotNull String context) {
+ return contexts.containsKey(context);
+ }
+
+ /**
+ * Retrieve all plurals for a specific context.
+ * @param context Context to apply
+ * @return Map of specified plurals
+ */
+ public @Nullable PluralMap getPlurals(@NotNull String context) {
+ return contexts.get(context);
+ }
+
+ /**
+ * Retrieve all locale translations for a specific context & pluralization
+ * @param context Context to apply
+ * @param plural Pluralization to apply
+ * @return Map of specified locales
+ */
+ public @Nullable LocaleMap getLocales(@NotNull String context, @NotNull Plural plural) {
+ return contexts.getOrDefault(context, new PluralMap()).get(plural);
+ }
+
+ /**
+ * Retrieve a specific locale translation for a specific context, pluralization and locale
+ * @param context Context to apply
+ * @param plural Pluralization to apply
+ * @param locale Locale to apply
+ * @return Translated locale value for the specified variant
+ */
+ public @Nullable String getValue(@NotNull String context, @NotNull Plural plural, @NotNull String locale) {
+ return contexts.getOrDefault(context, new PluralMap()).getOrDefault(plural, new LocaleMap()).get(locale);
+ }
+
+ /**
+ * Override or set context map.
+ * @param contexts New contexts
+ */
+ public void set(@NotNull ContextMap contexts) {
+ this.contexts = contexts;
+ }
+
+ /**
+ * Override or set a specific context
+ * @param context Context to use
+ * @param plurals New plurals map
+ */
+ public void set(@NotNull String context, @NotNull PluralMap plurals) {
+ contexts.put(context, plurals);
+ }
+
+ /**
+ * Override or set locales for a specific context & pluralization
+ * @param context Context to use
+ * @param plural Pluralization to use
+ * @param locales New locales map
+ */
+ public void set(@NotNull String context, @NotNull Plural plural, @NotNull LocaleMap locales) {
+ PluralMap plurals = getPlurals(context);
+
+ if(plurals == null) {
+ plurals = new PluralMap();
+ }
+
+ plurals.put(plural, locales);
+ set(context, plurals);
+ }
+
+ /**
+ * Override or update a specific translation variant
+ * @param context Context to use
+ * @param plural Pluralization to use
+ * @param locale Locale to use
+ * @param value New value to set
+ */
+ public void set(@NotNull String context, @NotNull Plural plural, @NotNull String locale, @NotNull String value) {
+ LocaleMap locales = getLocales(context, plural);
+
+ if(locales == null) {
+ locales = new LocaleMap();
+ }
+
+ locales.put(locale, value);
+ set(context, plural, locales);
+ }
+
+ /**
+ * I18n support data
+ * @return Data
+ */
+ public @Nullable Object getMisc() {
+ return misc;
+ }
+
+ /**
+ * Set or update I18n support data
+ * @param misc New Data
+ */
+ public void setMisc(@Nullable Object misc) {
+ this.misc = misc;
+ }
+
+ @Override
+ public String toString() {
+ return "Translation{" +
+ "description='" + description + '\'' +
+ ", contexts=" + contexts +
+ ", misc=" + misc +
+ '}';
+ }
+}
diff --git a/src/main/java/de/marhali/easyi18n/model/translation/variant/ContextMap.java b/src/main/java/de/marhali/easyi18n/model/translation/variant/ContextMap.java
new file mode 100644
index 0000000..0f4e10e
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/model/translation/variant/ContextMap.java
@@ -0,0 +1,11 @@
+package de.marhali.easyi18n.model.translation.variant;
+
+import java.util.HashMap;
+
+/**
+ * Maps context with pluralization.
+ * @author marhali
+ */
+public class ContextMap extends HashMap {
+ public static final String DEFAULT = "default";
+}
diff --git a/src/main/java/de/marhali/easyi18n/model/translation/variant/LocaleMap.java b/src/main/java/de/marhali/easyi18n/model/translation/variant/LocaleMap.java
new file mode 100644
index 0000000..76a7cf4
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/model/translation/variant/LocaleMap.java
@@ -0,0 +1,17 @@
+package de.marhali.easyi18n.model.translation.variant;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Maps locale type with specified value
+ * @author marhali
+ */
+public class LocaleMap extends HashMap {
+
+ public LocaleMap() {}
+
+ public LocaleMap(Map extends String, ? extends String> m) {
+ super(m);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/de/marhali/easyi18n/model/translation/variant/Plural.java b/src/main/java/de/marhali/easyi18n/model/translation/variant/Plural.java
new file mode 100644
index 0000000..7de4543
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/model/translation/variant/Plural.java
@@ -0,0 +1,11 @@
+package de.marhali.easyi18n.model.translation.variant;
+
+/**
+ * Represents all possible pluralization forms a translation can support.
+ * @author marhali
+ */
+public enum Plural {
+ ZERO, ONE, TWO, FEW, MANY, OTHER;
+
+ public static final Plural DEFAULT = Plural.ONE;
+}
diff --git a/src/main/java/de/marhali/easyi18n/model/translation/variant/PluralMap.java b/src/main/java/de/marhali/easyi18n/model/translation/variant/PluralMap.java
new file mode 100644
index 0000000..a79047f
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/model/translation/variant/PluralMap.java
@@ -0,0 +1,9 @@
+package de.marhali.easyi18n.model.translation.variant;
+
+import java.util.HashMap;
+
+/**
+ * Maps pluralization with locale values.
+ * @author marhali
+ */
+public class PluralMap extends HashMap {}
diff --git a/src/test/java/de/marhali/easyi18n/TranslationTest.java b/src/test/java/de/marhali/easyi18n/TranslationTest.java
new file mode 100644
index 0000000..7e8f9ad
--- /dev/null
+++ b/src/test/java/de/marhali/easyi18n/TranslationTest.java
@@ -0,0 +1,43 @@
+package de.marhali.easyi18n;
+
+import de.marhali.easyi18n.model.translation.variant.Plural;
+import de.marhali.easyi18n.model.translation.variant.ContextMap;
+import de.marhali.easyi18n.model.translation.variant.LocaleMap;
+import de.marhali.easyi18n.model.translation.Translation;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Map;
+
+/**
+ * Unit tests for {@link Translation}.
+ * @author marhali
+ */
+public class TranslationTest {
+
+ @Test
+ public void add() {
+ Translation value = new Translation();
+ value.set(ContextMap.DEFAULT, Plural.DEFAULT, "en", "hello");
+ Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.DEFAULT, "en"), "hello");
+ }
+
+ @Test
+ public void override() {
+ Translation value = new Translation();
+ value.set(ContextMap.DEFAULT, Plural.DEFAULT, new LocaleMap(Map.of("en", "hello", "de", "hallo")));
+ value.set(ContextMap.DEFAULT, Plural.DEFAULT, "en", "new hello");
+ Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.DEFAULT, "en"), "new hello");
+ Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.DEFAULT, "de"), "hallo");
+ }
+
+ @Test
+ public void plurals() {
+ Translation value = new Translation();
+ value.set(ContextMap.DEFAULT, Plural.ONE, "en", "boyfriend");
+ value.set(ContextMap.DEFAULT, Plural.MANY, "en", "boyfriends");
+ Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.ONE, "en"), "boyfriend");
+ Assert.assertEquals(value.getValue(ContextMap.DEFAULT, Plural.MANY, "en"), "boyfriends");
+ }
+}