diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php
index 94b9b786e76c6cff6afede8f44f12ba509939cdd..31e4426d012afebc9a5e48d9f524863e9bb832f6 100644
--- a/include/class.dynamic_forms.php
+++ b/include/class.dynamic_forms.php
@@ -103,7 +103,7 @@ class DynamicForm extends VerySimpleModel {
         if (!$this->_form || $source) {
             $fields = $this->getFields($this->_has_data);
             $this->_form = new Form($fields, $source, array(
-                'title'=>$this->title, 'instructions'=>$this->instructions));
+                'title'=>$this->getLocal('title'), 'instructions'=>$this->getLocal('instructions')));
         }
         return $this->_form;
     }
@@ -124,6 +124,15 @@ class DynamicForm extends VerySimpleModel {
         }
     }
 
+    function getTranslateTag($subtag) {
+        return _H(sprintf('form.%s.%s', $subtag, $this->id));
+    }
+    function getLocal($subtag) {
+        $tag = $this->getTranslateTag($subtag);
+        $T = CustomDataTranslation::translate($tag);
+        return $T != $tag ? $T : $this->get($subtag);
+    }
+
     function save($refetch=false) {
         if (count($this->dirty))
             $this->set('updated', new SqlFunction('NOW'));
@@ -490,6 +499,14 @@ class DynamicFormField extends VerySimpleModel {
     function  isEditable() {
         return (($this->get('edit_mask') & 32) == 0);
     }
+    function getTranslateTag($subtag) {
+        return _H(sprintf('field.%s.%s', $subtag, $this->id));
+    }
+    function getLocal($subtag, $default=false) {
+        $tag = $this->getTranslateTag($subtag);
+        $T = CustomDataTranslation::translate($tag);
+        return $T != $tag ? $T : ($default ?: $this->get($subtag));
+    }
 
     function allRequirementModes() {
         return array(
diff --git a/include/class.forms.php b/include/class.forms.php
index 73afeaa0eca493abcfe50d8d415af01bbd03f89d..75d3bfc83914e8689edb856a48c16816164b7f84 100644
--- a/include/class.forms.php
+++ b/include/class.forms.php
@@ -609,7 +609,7 @@ class FormField {
         if (!$this->_cform) {
             $type = static::getFieldType($this->get('type'));
             $clazz = $type[1];
-            $T = new $clazz(array('type'=>$this->get('type')));
+            $T = new $clazz($this->ht);
             $config = $this->getConfiguration();
             $this->_cform = new Form($T->getConfigurationOptions(), $source);
             if (!$source) {
@@ -647,6 +647,15 @@ class FormField {
 
         return $name;
     }
+
+    function getTranslateTag($subtag) {
+        return _H(sprintf('field.%s.%s', $subtag, $this->get('id')));
+    }
+    function getLocal($subtag, $default=false) {
+        $tag = $this->getTranslateTag($subtag);
+        $T = CustomDataTranslation::translate($tag);
+        return $T != $tag ? $T : ($default ?: $this->get($subtag));
+    }
 }
 
 class TextboxField extends FormField {
@@ -689,12 +698,16 @@ class TextboxField extends FormField {
                 })),
             'validator-error' => new TextboxField(array(
                 'id'=>4, 'label'=>__('Validation Error'), 'default'=>'',
-                'configuration'=>array('size'=>40, 'length'=>60),
+                'configuration'=>array('size'=>40, 'length'=>60,
+                    'translatable'=>$this->getTranslateTag('validator-error')
+                ),
                 'hint'=>__('Message shown to user if the input does not match the validator'))),
             'placeholder' => new TextboxField(array(
                 'id'=>5, 'label'=>__('Placeholder'), 'required'=>false, 'default'=>'',
                 'hint'=>__('Text shown in before any input from the user'),
-                'configuration'=>array('size'=>40, 'length'=>40),
+                'configuration'=>array('size'=>40, 'length'=>40,
+                    'translatable'=>$this->getTranslateTag('placeholder')
+                ),
             )),
         );
     }
@@ -732,7 +745,7 @@ class TextboxField extends FormField {
         $func = $validators[$valid];
         $error = $func[1];
         if ($config['validator-error'])
-            $error = $config['validator-error'];
+            $error = $this->getLocal('validator-error', $config['validator-error']);
         if (is_array($func) && is_callable($func[0]))
             if (!call_user_func($func[0], $value))
                 $this->_errors[] = $error;
@@ -1709,13 +1722,17 @@ class TextboxWidget extends Widget {
             $autocomplete = 'autocomplete="'.($config['autocomplete']?'on':'off').'"';
         if (isset($config['disabled']))
             $disabled = 'disabled="disabled"';
+        if (isset($config['translatable']) && $config['translatable'])
+            $translatable = 'data-translate-tag="'.$config['translatable'].'"';
+        $placeholder = sprintf('placeholder="%s"', $this->field->getLocal('placeholder',
+            $config['placeholder']));
         ?>
         <span style="display:inline-block">
         <input type="<?php echo static::$input_type; ?>"
             id="<?php echo $this->id; ?>"
             <?php echo implode(' ', array_filter(array(
-                $size, $maxlength, $classes, $autocomplete, $disabled)))
-                .' placeholder="'.$config['placeholder'].'"'; ?>
+                $size, $maxlength, $classes, $autocomplete, $disabled,
+                $translatable, $placeholder))); ?>
             name="<?php echo $this->name; ?>"
             value="<?php echo Format::htmlchars($this->value); ?>"/>
         </span>
diff --git a/include/staff/department.inc.php b/include/staff/department.inc.php
index 502c309208cd69490a824064585a34bb06ae20df..7681645200ae9f88d6e3171cf987bc91342ba8f5 100644
--- a/include/staff/department.inc.php
+++ b/include/staff/department.inc.php
@@ -295,6 +295,3 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <input type="button" name="cancel" value="<?php echo __('Cancel');?>" onclick='window.location.href="departments.php"'>
 </p>
 </form>
-<script type="text/javascript">
-    $('input[data-translate-tag]').translatable();
-</script>
diff --git a/include/staff/dynamic-form.inc.php b/include/staff/dynamic-form.inc.php
index 4b1b852a09eedad808c779ba32e58c6d6b4c4bec..2e429debbe199b87459516ecc21b0d4695b515b6 100644
--- a/include/staff/dynamic-form.inc.php
+++ b/include/staff/dynamic-form.inc.php
@@ -7,6 +7,8 @@ if($form && $_REQUEST['a']!='add') {
     $url = "?id=".urlencode($_REQUEST['id']);
     $submit_text=__('Save Changes');
     $info = $form->ht;
+    $trans['title'] = $form->getTranslateTag('title');
+    $trans['instructions'] = $form->getTranslateTag('instructions');
     $newcount=2;
 } else {
     $title = __('Add new custom form section');
@@ -38,8 +40,9 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
     <tbody style="vertical-align:top">
         <tr>
             <td width="180" class="required"><?php echo __('Title'); ?>:</td>
-            <td><input type="text" name="title" size="40" value="<?php
-                echo $info['title']; ?>"/>
+            <td><input type="text" name="title" size="40"
+                data-translate-tag="<?php echo $trans['title']; ?>"
+                value="<?php echo $info['title']; ?>"/>
                 <i class="help-tip icon-question-sign" href="#form_title"></i>
                 <font class="error"><?php
                     if ($errors['title']) echo '<br/>'; echo $errors['title']; ?></font>
@@ -129,6 +132,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info);
         <tr>
             <td><i class="icon-sort"></i></td>
             <td><input type="text" size="32" name="label-<?php echo $id; ?>"
+                data-translate-tag="<?php echo $f->getTranslateTag('label'); ?>"
                 value="<?php echo Format::htmlchars($f->get('label')); ?>"/>
                 <font class="error"><?php
                     if ($ferrors['label']) echo '<br/>'; echo $ferrors['label']; ?>
diff --git a/include/staff/templates/dynamic-field-config.tmpl.php b/include/staff/templates/dynamic-field-config.tmpl.php
index 51701b6ee1f66ce93c66fba5845a14abfd65d78b..aba9966d27f34a22c22937e8bbf1a8c30c79cdc0 100644
--- a/include/staff/templates/dynamic-field-config.tmpl.php
+++ b/include/staff/templates/dynamic-field-config.tmpl.php
@@ -61,3 +61,7 @@
          </p>
     </form>
     <div class="clear"></div>
+<script type="text/javascript">
+   // Make translatable fields translatable
+   $('input[data-translate-tag]').translatable();
+</script>
diff --git a/include/staff/templates/dynamic-form.tmpl.php b/include/staff/templates/dynamic-form.tmpl.php
index 3987d9ee0ab9582db3fbcd57d669621477da6846..7f8cd82b88148122c8545c458121c83b27cef56b 100644
--- a/include/staff/templates/dynamic-form.tmpl.php
+++ b/include/staff/templates/dynamic-form.tmpl.php
@@ -53,7 +53,7 @@ if (isset($options['entry']) && $options['mode'] == 'edit') { ?>
                 <td class="multi-line <?php if ($field->get('required')) echo 'required';
                 ?>" style="min-width:120px;" <?php if ($options['width'])
                     echo "width=\"{$options['width']}\""; ?>>
-                <?php echo Format::htmlchars($field->get('label')); ?>:</td>
+                <?php echo Format::htmlchars($field->getLocal('label')); ?>:</td>
                 <td><div style="position:relative"><?php
             }
             $field->render(); ?>
diff --git a/scp/js/jquery.translatable.js b/scp/js/jquery.translatable.js
index f75208ac640ace77ae9642d50ee7413fb08121de..b0d427e2b9efb01a4a7dc39a54b0b7fc8a292569 100644
--- a/scp/js/jquery.translatable.js
+++ b/scp/js/jquery.translatable.js
@@ -6,12 +6,18 @@
   var Translatable = function( element, options ) {
     this.$element = $(element);
     this.options = $.extend({}, $.fn.translatable.defaults, options);
+    if (!this.$element.data('translateTag'))
+        return;
+
     this.$translations = $('<ul class="translations"></ul>');
     this.$status = $('<li class="status"><i class="icon-spinner icon-spin"></i> Loading ...</li>')
       .appendTo(this.$translations);
     this.$footer = $('<div class="add-translation"></div>');
     this.$select = $('<select name="locale"></select>');
     this.$menu = $(this.options.menu).appendTo('body');
+    this.$element.wrap(
+      $('<span></span>').css({display:'inline-block', 'white-space':'nowrap'})
+    )
     this.$button = $(this.options.button).insertAfter(this.$element);
     //this.$menu.append('<a class="close pull-right" href=""><i class="icon-remove-circle"></i></a>')
     //    .on('click', $.proxy(this.hide, this));
@@ -95,6 +101,7 @@
       $('option[value='+lang+']', this.$select).remove();
       if (!$('option', this.$select).length)
         this.$footer.hide();
+      this.$status.remove();
     },
 
     showCommit: function(e) {
diff --git a/scp/js/scp.js b/scp/js/scp.js
index d1493f59036b5e762cdf3ccfefa02e96c35e573b..a9a67b04fe0c0c8b04ef462ee2a34233e2de6787 100644
--- a/scp/js/scp.js
+++ b/scp/js/scp.js
@@ -443,6 +443,9 @@ var scp_prep = function() {
            });
        }
    });
+
+   // Make translatable fields translatable
+   $('input[data-translate-tag]').translatable();
 };
 
 $(document).ready(scp_prep);