diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java
index a826512ce7c45c07eb7ccc953af5395cbf0f9364..8ac92e354799a9f28186c5f5f63bb4f78422675f 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/Dict.java
@@ -459,6 +459,8 @@ public abstract class Dict
 
     public static final String VOCABULARY_TERMS_EMPTY = "vocabulary_terms_empty";
 
+    public static final String VOCABULARY_TERM_CODE_EMPTY = "vocabulary_term_code_empty";
+
     public static final String MISSING_VOCABULARY_TERMS = "missing_vocabulary_terms";
 
     public static final String VOCABULARY_SHOW_AVAILABLE_TERMS_IN_CHOOSERS = "vocabulary_show_available_terms_in_choosers";
@@ -841,13 +843,15 @@ public abstract class Dict
 
     public static final String TERMS = "terms";
 
-    public static final String VOCABULARY_TERMS_DESCRIPTION = "descriptions";
+    public static final String TERM_CODE = "term_code";
+
+    public static final String VOCABULARY_TERM_DESCRIPTION = "description";
 
-    public static final String VOCABULARY_TERMS_DESCRIPTION_EMPTY = "vocabulary_terms_description_empty";
+    public static final String VOCABULARY_TERM_DESCRIPTION_EMPTY = "vocabulary_term_description_empty";
 
-    public static final String VOCABULARY_TERMS_LABEL = "labels";
+    public static final String VOCABULARY_TERM_LABEL = "label";
 
-    public static final String VOCABULARY_TERMS_LABEL_EMPTY = "vocabulary_terms_label_empty";
+    public static final String VOCABULARY_TERM_LABEL_EMPTY = "vocabulary_term_label_empty";
 
     public static final String VOCABULARY_TERMS_BROWSER = "VOCABULARY_TERMS_BROWSER";
 
@@ -865,6 +869,8 @@ public abstract class Dict
 
     public static final String ADD_VOCABULARY_TERMS_TITLE = "add_vocabulary_terms_title";
 
+    public static final String ADD_VOCABULARY_TERM_TITLE = "add_vocabulary_term_title";
+
     public static final String ADD_VOCABULARY_TERMS_OK_BUTTON = "add_vocabulary_terms_ok_button";
 
     public static final String UPDATE_VOCABULARY_TERMS_BUTTON = "update_vocabulary_terms_button";
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/LabelAndDescriptionTermsValidator.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/LabelAndDescriptionTermsValidator.java
deleted file mode 100644
index 3343dcd19e4d0eada7d9a4b100e3d13f1175be1c..0000000000000000000000000000000000000000
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/LabelAndDescriptionTermsValidator.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.vocabulary;
-
-import java.util.List;
-
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm;
-
-import com.extjs.gxt.ui.client.widget.form.Field;
-import com.extjs.gxt.ui.client.widget.form.TextArea;
-import com.extjs.gxt.ui.client.widget.form.Validator;
-
-/**
- * A {@link Validator} implementation which validates vocabulary terms in a text area.
- * 
- * @author Christian Ribeaud
- */
-final class LabelAndDescriptionTermsValidator implements Validator
-{
-    private final String failureMessage;
-
-    private final TextArea termsField;
-
-    public LabelAndDescriptionTermsValidator(TextArea termsField, String failureMessage)
-    {
-        this.termsField = termsField;
-        this.failureMessage = failureMessage;
-    }
-
-    @Override
-    final public String validate(Field<?> field, String value)
-    {
-
-        final List<VocabularyTerm> terms = VocabularyTermValidator.getTerms(termsField.getValue());
-        final String[] labelsOrDescriptions = value.split(",");
-        if (labelsOrDescriptions.length == 0 || labelsOrDescriptions.length == terms.size())
-        {
-            return null;
-        } else
-        {
-            return failureMessage;
-        }
-    }
-}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/VocabularyTermGrid.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/VocabularyTermGrid.java
index d77c792f15e1144455d49aceeef71e71ebdf8a8e..65062f9c4f7ae9d3874bcd6339d8cf54a72d6379 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/VocabularyTermGrid.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/VocabularyTermGrid.java
@@ -498,7 +498,7 @@ public class VocabularyTermGrid extends TypedTableGrid<VocabularyTermWithStats>
     private Window createAddNewTermsDialog()
     {
         final String title = viewContext
-                .getMessage(Dict.ADD_VOCABULARY_TERMS_TITLE);
+                .getMessage(Dict.ADD_VOCABULARY_TERM_TITLE);
 
         return new AbstractRegistrationDialog(viewContext, title,
                 postRegistrationCallback)
@@ -541,53 +541,40 @@ public class VocabularyTermGrid extends TypedTableGrid<VocabularyTermWithStats>
                 @Override
                 protected void register(AsyncCallback<Void> registrationCallback)
                 {
-                    List<VocabularyTerm> newVocabularyTermCodes = extractNewVocabularyTermCodes();
-
-                    String[] labels = extractNewVocabularyTermLabels();
-                    for (int i = 0; i < labels.length; i++)
-                    {
-                        newVocabularyTermCodes.get(i).setLabel(labels[i]);
-                    }
-
-                    String[] descriptions = extractNewVocabularyTermDescriptions();
-                    for (int i = 0; i < descriptions.length; i++)
-                    {
-                        newVocabularyTermCodes.get(i).setDescription(descriptions[i]);
-                    }
-
+                    VocabularyTerm newVocabularyTerm = VocabularyTermSingleCodeValidator.getTerm(newTermCodesArea.getValue());
+                    newVocabularyTerm.setLabel(newTermsLabelsArea.getValue());
+                    newVocabularyTerm.setDescription(newTermDescriptionsArea.getValue());
                     Long previousTermOrdinal = extractPreviousTermOrdinal();
 
+                    List<VocabularyTerm> newTerms = new ArrayList<VocabularyTerm>();
+                    newTerms.add(newVocabularyTerm);
                     viewContext.getService().addVocabularyTerms(
-                            TechId.create(vocabulary), newVocabularyTermCodes,
+                            TechId.create(vocabulary), newTerms,
                             previousTermOrdinal, registrationCallback);
                 }
 
                 private TextArea createNewTermCodesArea()
                 {
                     final TextArea result = new TextArea();
-                    result.setFieldLabel(viewContext.getMessage(Dict.TERMS));
-                    result.setEmptyText(viewContext.getMessage(Dict.VOCABULARY_TERMS_EMPTY));
-                    result.setValidator(new VocabularyTermValidator(viewContext));
+                    result.setFieldLabel(viewContext.getMessage(Dict.TERM_CODE));
+                    result.setEmptyText(viewContext.getMessage(Dict.VOCABULARY_TERM_CODE_EMPTY));
+                    result.setValidator(new VocabularyTermSingleCodeValidator(viewContext));
                     return result;
                 }
 
                 private TextArea createNewTermDescriptionArea()
                 {
                     final TextArea result = new TextArea();
-                    result.setFieldLabel(viewContext.getMessage(Dict.VOCABULARY_TERMS_DESCRIPTION));
-                    result.setEmptyText(viewContext.getMessage(Dict.VOCABULARY_TERMS_DESCRIPTION_EMPTY));
-                    result.setValidator(new LabelAndDescriptionTermsValidator(newTermCodesArea,
-                            "You need to have the same number of codes and descriptions."));
+                    result.setFieldLabel(viewContext.getMessage(Dict.VOCABULARY_TERM_DESCRIPTION));
+                    result.setEmptyText(viewContext.getMessage(Dict.VOCABULARY_TERM_DESCRIPTION_EMPTY));
                     return result;
                 }
 
                 private TextArea createNewTermLabelArea()
                 {
                     final TextArea result = new TextArea();
-                    result.setFieldLabel(viewContext.getMessage(Dict.VOCABULARY_TERMS_LABEL));
-                    result.setEmptyText(viewContext.getMessage(Dict.VOCABULARY_TERMS_LABEL_EMPTY));
-                    result.setValidator(new LabelAndDescriptionTermsValidator(newTermCodesArea,
-                            "You need to have the same number of codes and labels."));
+                    result.setFieldLabel(viewContext.getMessage(Dict.VOCABULARY_TERM_LABEL));
+                    result.setEmptyText(viewContext.getMessage(Dict.VOCABULARY_TERM_LABEL_EMPTY));
                     return result;
                 }
 
@@ -603,37 +590,6 @@ public class VocabularyTermGrid extends TypedTableGrid<VocabularyTermWithStats>
                     return result;
                 }
 
-                private List<VocabularyTerm> extractNewVocabularyTermCodes()
-                {
-                    return VocabularyTermValidator.getTerms(newTermCodesArea
-                            .getValue());
-                }
-
-                private String[] extractNewVocabularyTermLabels()
-                {
-                    String value = newTermsLabelsArea.getValue();
-                    if (value != null)
-                    {
-                        return value.split(",");
-                    } else
-                    {
-                        return new String[] {};
-                    }
-
-                }
-
-                private String[] extractNewVocabularyTermDescriptions()
-                {
-                    String value = newTermDescriptionsArea.getValue();
-                    if (value != null)
-                    {
-                        return value.split(",");
-                    } else
-                    {
-                        return new String[] {};
-                    }
-                }
-
                 /**
                  * extracts ordinal of a term after which new terms will be added
                  */
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/VocabularyTermSingleCodeValidator.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/VocabularyTermSingleCodeValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8f70b290d058f343583178994a515e44d4769f8
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/client/application/ui/vocabulary/VocabularyTermSingleCodeValidator.java
@@ -0,0 +1,80 @@
+package ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.vocabulary;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import ch.systemsx.cisd.common.shared.basic.string.StringUtils;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.Dict;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.ui.field.CodeField.CodeFieldKind;
+import ch.systemsx.cisd.openbis.generic.client.web.client.application.util.IMessageProvider;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.VocabularyTerm;
+
+import com.extjs.gxt.ui.client.widget.form.Field;
+import com.extjs.gxt.ui.client.widget.form.Validator;
+
+/**
+ * A {@link Validator} implementation which validates a vocabulary term code in a text area.
+ * 
+ * @author Juan Fuentes
+ */
+final class VocabularyTermSingleCodeValidator implements Validator
+{
+    private final IMessageProvider messageProvider;
+
+    private final Set<String> existingTerms;
+
+    VocabularyTermSingleCodeValidator(final IMessageProvider messageProvider)
+    {
+        this(messageProvider, Collections.<VocabularyTerm> emptyList());
+    }
+
+    public VocabularyTermSingleCodeValidator(final IMessageProvider messageProvider,
+            List<VocabularyTerm> terms)
+    {
+        this.messageProvider = messageProvider;
+        existingTerms = new HashSet<String>();
+        for (VocabularyTerm vocabularyTerm : terms)
+        {
+            existingTerms.add(vocabularyTerm.getCode());
+        }
+    }
+
+    final static VocabularyTerm getTerm(final String value)
+    {
+        final List<VocabularyTerm> terms = new ArrayList<VocabularyTerm>();
+        if (StringUtils.isBlank(value) == false)
+        {
+            VocabularyTerm term = new VocabularyTerm();
+            term.setCode(value);
+            terms.add(term);
+            return term;
+        } else
+        {
+            return null;
+        }
+    }
+
+    @Override
+    final public String validate(Field<?> field, String value)
+    {
+        final VocabularyTerm term = VocabularyTermSingleCodeValidator.getTerm(value);
+        if (term == null)
+        {
+            return messageProvider.getMessage(Dict.MISSING_VOCABULARY_TERMS);
+        }
+        CodeFieldKind codeKind = CodeFieldKind.CODE_WITH_COLON;
+        if (term.getCode().matches(codeKind.getPattern()) == false)
+        {
+            return messageProvider.getMessage(Dict.INVALID_CODE_MESSAGE,
+                    codeKind.getAllowedCharacters());
+        }
+        if (existingTerms.contains(term.getCode().toUpperCase()))
+        {
+            return messageProvider.getMessage(Dict.VOCABULARY_TERMS_VALIDATION_MESSAGE, term);
+        }
+        return null;
+    }
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js
index 4a574bef426fdc1d78b0d693c641d80914ca25ca..49cdc7aa01daede29927c44d38a6166669a6932f 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js
@@ -390,6 +390,7 @@ var common = {
   vocabulary_terms_file_format: "<pre>code    [label]    [description]</pre> ([...] means that column is optional)",
   vocabulary_terms_url_template: "URL Template",
   vocabulary_terms_empty: "Space or comma separated list of terms.",
+  vocabulary_term_code_empty: "The term code.",
   vocabulary_show_available_terms_in_choosers: "Show available terms in choosers",
   confirm_vocabulary_show_available_terms_in_chooosers_msg: "If this vocabulary has many terms (more than 100) application will slow down.<br><br>Are you sure?",
   vocabulary_show_available_terms_in_choosers: "Show available terms in choosers",
@@ -693,10 +694,11 @@ var common = {
  is_managed_internally: "Managed Internally?",
  url_template: "URL Template",
  terms: "Terms",
- descriptions: "Descriptions",
- vocabulary_terms_description_empty: "Comma separated list of descriptions or none.",
- labels: "Labels",
- vocabulary_terms_label_empty: "Comma separated list of labels or none.",
+ term_code: "Code",
+ description: "Description",
+ vocabulary_term_description_empty: "The description for the term.",
+ label: "Label",
+ vocabulary_term_label_empty: "The label for the term.",
  VOCABULARY_TERMS_BROWSER: "Vocabulary Terms of {0}",
  TERM_FOR_SAMPLES_USAGE: "Usages for Samples",
  TERM_FOR_DATA_SET_USAGE: "Usages for Data Sets",
@@ -705,6 +707,7 @@ var common = {
  TERM_TOTAL_USAGE: "Total Usages Number",
  add_vocabulary_terms_button: "Add",
  add_vocabulary_terms_title: "Add Terms",
+ add_vocabulary_term_title: "Add Term",
  add_vocabulary_terms_ok_button: "OK",
  update_vocabulary_terms_button: "Batch Update",
  update_vocabulary_terms_title: "Update Vocabulary Terms of {0}",