diff --git a/package-lock.json b/package-lock.json
index 87d4ff0688099395e11f5523d084e53c1955ddfd..2dfeaeda58ab28cdd0d100a57ff5a86d09ae4ba8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6553,9 +6553,9 @@
       }
     },
     "node_modules/vite": {
-      "version": "5.1.5",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.5.tgz",
-      "integrity": "sha512-BdN1xh0Of/oQafhU+FvopafUp6WaYenLU/NFoL5WyJL++GxkNfieKzBhM24H3HVsPQrlAqB7iJYTHabzaRed5Q==",
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz",
+      "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==",
       "dev": true,
       "dependencies": {
         "esbuild": "^0.19.3",
diff --git a/src/components/workflows/WorkflowWithVersionsCard.vue b/src/components/workflows/WorkflowWithVersionsCard.vue
index 1afe153268f3556139471f3abe4cf5d277dcd90a..1e8e19e97ca32699d60c8bafb8c678db477f525b 100644
--- a/src/components/workflows/WorkflowWithVersionsCard.vue
+++ b/src/components/workflows/WorkflowWithVersionsCard.vue
@@ -325,6 +325,24 @@ onMounted(() => {
                         >Update icon</a
                       >
                     </li>
+                    <li>
+                      <router-link
+                        class="dropdown-item"
+                        :to="{
+                          name: 'workflow-parameter-translation',
+                          params: {
+                            workflowId: props.workflow.workflow_id,
+                            versionId: version.workflow_version_id,
+                          },
+                        }"
+                      >
+                        <template v-if="version.parameter_extension"
+                          >Update</template
+                        >
+                        <template v-else>Add</template>
+                        Parameter Translation
+                      </router-link>
+                    </li>
                   </ul>
                 </td>
               </tr>
diff --git a/src/router/workflowRoutes.ts b/src/router/workflowRoutes.ts
index 5eb83dbb0a380052ba031e92b649b32165b5f32a..c7d238ecef33d463569d43d7ed56d8307b1b8187 100644
--- a/src/router/workflowRoutes.ts
+++ b/src/router/workflowRoutes.ts
@@ -97,4 +97,14 @@ export const workflowRoutes: RouteRecordRaw[] = [
       },
     ],
   },
+  {
+    path: "workflows/:workflowId/version/:versionId/parameters",
+    name: "workflow-parameter-translation",
+    component: () =>
+      import("../views/workflows/CreateParameterTranslationView.vue"),
+    props: (route) => ({
+      versionId: route.params.versionId,
+      workflowId: route.params.workflowId,
+    }),
+  },
 ];
diff --git a/src/stores/workflows.ts b/src/stores/workflows.ts
index b094280840e4f9f43ae78d36e712c47d2e4b8e6e..8ecfdb5f732b011a080264335324331d5e6246d4 100644
--- a/src/stores/workflows.ts
+++ b/src/stores/workflows.ts
@@ -63,6 +63,15 @@ export const useWorkflowStore = defineStore({
       }
       return mapping;
     },
+    ownVersionMapping(): Record<string, WorkflowVersion> {
+      const mapping: Record<string, WorkflowVersion> = {};
+      for (const workflow of this.ownWorkflows) {
+        for (const version of workflow.versions) {
+          mapping[version.workflow_version_id] = version;
+        }
+      }
+      return mapping;
+    },
     getArbitraryWorkflow(): (wid: string) => Promise<WorkflowIn | undefined> {
       return (wid: string) => get(wid);
     },
@@ -71,6 +80,15 @@ export const useWorkflowStore = defineStore({
     __addNameToMapping(key: string, value: string) {
       useNameStore().addNameToMapping(key, value);
     },
+    fetchWorkflowVersion(wid: string, vid: string): Promise<WorkflowVersion> {
+      return WorkflowVersionService.workflowVersionGetWorkflowVersion(
+        vid,
+        wid,
+      ).then((version) => {
+        this.__addNameToMapping(version.workflow_version_id, version.version);
+        return version;
+      });
+    },
     fetchWorkflows(onFinally?: () => void): Promise<WorkflowOut[]> {
       if (Object.keys(this.workflowMapping).length > 0) {
         onFinally?.();
diff --git a/src/views/workflows/CreateParameterTranslationView.vue b/src/views/workflows/CreateParameterTranslationView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..5cc296f746e446b481b32a31bd48e5542346715c
--- /dev/null
+++ b/src/views/workflows/CreateParameterTranslationView.vue
@@ -0,0 +1,287 @@
+<script setup lang="ts">
+import { useNameStore } from "@/stores/names";
+import { useWorkflowStore } from "@/stores/workflows";
+import { computed, onMounted, reactive, watch } from "vue";
+import {
+  DocumentationEnum,
+  type ParameterExtension_Input,
+} from "@/client/workflow";
+
+const props = defineProps<{
+  versionId: string;
+  workflowId: string;
+}>();
+
+const parameterState = reactive<{
+  extension: ParameterExtension_Input;
+  resourceParameters: Set<string>;
+}>({
+  extension: {},
+  resourceParameters: new Set(),
+});
+
+const parameterPools = reactive<{
+  defaults: string[];
+  mapping: string[];
+}>({
+  defaults: [],
+  mapping: [],
+});
+
+const nameRepository = useNameStore();
+const workflowRepository = useWorkflowStore();
+
+watch(
+  () => workflowRepository.documentationFiles[props.versionId ?? ""],
+  (newVal, old) => {
+    if (newVal != old && newVal?.parameter_schema != undefined) {
+      updateParameterPools(newVal);
+    }
+  },
+  {
+    deep: true,
+  },
+);
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function updateParameterPools(newVal?: Record<DocumentationEnum, any>) {
+  if (newVal?.parameter_schema) {
+    const parameters = extractParameterList(newVal.parameter_schema);
+    parameterPools.defaults = parameters.slice();
+    parameterPools.mapping = parameters.slice();
+  }
+  if (
+    workflowRepository.ownVersionMapping[props.versionId]?.parameter_extension
+  ) {
+    parameterPools.defaults = parameterPools.defaults.filter(
+      (param) =>
+        workflowRepository.ownVersionMapping[props.versionId]
+          ?.parameter_extension?.defaults?.[param] == undefined,
+    );
+    parameterPools.mapping = parameterPools.mapping.filter(
+      (param) =>
+        workflowRepository.ownVersionMapping[props.versionId]
+          ?.parameter_extension?.defaults?.[param] == undefined,
+    );
+  }
+}
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function extractParameterList(schema: Record<string, any>): string[] {
+  const groupedParameters = Object.keys(schema["definitions"] ?? {}).reduce(
+    (acc: string[], val) => [
+      ...acc,
+      ...Object.keys(schema["definitions"][val]["properties"]),
+    ],
+    [],
+  );
+  const singleParameters = Object.keys(schema["properties"] ?? {});
+  return [...groupedParameters, ...singleParameters];
+}
+
+function getParameterType(param: string): string | undefined {
+  return parameterSchema.value[param]?.["type"];
+}
+
+function addDefaultParameter(param: string, index: number) {
+  if (parameterState.extension.defaults == undefined) {
+    parameterState.extension.defaults = {};
+  }
+  parameterPools.defaults.splice(index, 1);
+  switch (getParameterType(param)) {
+    case "integer": {
+      parameterState.extension.defaults[param] = 0;
+      break;
+    }
+    case "number": {
+      parameterState.extension.defaults[param] = 0;
+      break;
+    }
+    case "boolean": {
+      parameterState.extension.defaults[param] = true;
+      break;
+    }
+    case "string": {
+      parameterState.extension.defaults[param] =
+        parameterSchema.value[param]?.["enum"]?.[0] ?? "";
+      break;
+    }
+    default: {
+      parameterState.extension.defaults[param] = "";
+      break;
+    }
+  }
+}
+
+function deleteDefaultParameter(param: string) {
+  delete parameterState.extension.defaults?.[param];
+  parameterPools.defaults.push(param);
+  if (Object.keys(parameterState.extension.defaults ?? {}).length === 0) {
+    parameterState.extension.defaults = undefined;
+  }
+}
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const parameterSchema = computed<Record<string, Record<string, any>>>(() => {
+  const schema =
+    workflowRepository.documentationFiles[props.versionId ?? ""]
+      ?.parameter_schema;
+  const a = schema?.["properties"] ?? {};
+  for (const group in schema?.["definitions"] ?? {}) {
+    for (const param in schema?.["definitions"]?.[group]?.["properties"] ??
+      {}) {
+      a[param] = schema["definitions"][group]["properties"][param];
+    }
+  }
+  return a;
+});
+
+onMounted(() => {
+  workflowRepository.fetchWorkflow(props.workflowId, true, () => {
+    parameterState.extension =
+      workflowRepository.ownVersionMapping[props.versionId]
+        ?.parameter_extension ?? {};
+    workflowRepository
+      .fetchWorkflowDocumentation(
+        props.workflowId,
+        props.versionId,
+        DocumentationEnum.PARAMETER_SCHEMA,
+        workflowRepository.ownVersionMapping[props.versionId]?.modes?.[0],
+      )
+      .finally(() => {
+        updateParameterPools(
+          workflowRepository.documentationFiles[props.versionId],
+        );
+      });
+  });
+});
+</script>
+
+<template>
+  <div class="row border-bottom mb-4">
+    <h2 class="mb-2">
+      Add parameter metadata to
+      {{ nameRepository.getName(props.workflowId) }}@{{
+        nameRepository.getName(props.versionId)
+      }}
+    </h2>
+  </div>
+  <div
+    class="d-flex flex-wrap overflow-y-auto p-1 border rounded border-dashed mb-2"
+    style="max-height: 30vh"
+    v-if="parameterPools.defaults.length > 0"
+  >
+    <b class="ms-1 w-100">Workflow parameters:</b>
+    <div
+      class="w-fit border px-2 rounded cursor-pointer m-1 parameter-container"
+      v-for="(param, index) in parameterPools.defaults"
+      :key="param"
+      @click="addDefaultParameter(param, index)"
+    >
+      {{ param }}
+    </div>
+  </div>
+  <table class="table table-bordered">
+    <thead>
+      <tr>
+        <th scope="col"><b>Parameter</b></th>
+        <th scope="col"><b>Value</b></th>
+      </tr>
+    </thead>
+    <tbody v-if="parameterState.extension.defaults">
+      <tr
+        v-for="param in Object.keys(parameterState.extension.defaults)"
+        :key="param"
+      >
+        <td style="width: 10%">{{ param }}</td>
+        <td class="d-flex justify-content-between align-items-center">
+          <input
+            v-if="
+              getParameterType(param) === 'number' ||
+              getParameterType(param) === 'integer'
+            "
+            type="number"
+            class="form-control form-control-sm flex-grow"
+            v-model="parameterState.extension.defaults[param]"
+            :step="getParameterType(param) === 'integer' ? 1 : 0.0001"
+            :min="parameterSchema[param]['minimum']"
+            :max="parameterSchema[param]['maximum']"
+          />
+          <div
+            v-else-if="getParameterType(param) === 'boolean'"
+            class="flex-grow"
+          >
+            <div class="form-check form-check-inline">
+              <label
+                class="form-check-label"
+                :for="'trueOption' + param.replace(/\./g, '')"
+                >True</label
+              >
+              <input
+                class="form-check-input"
+                type="radio"
+                :name="'inlineRadioOptions' + param.replace(/\./g, '')"
+                :id="'trueOption' + param.replace(/\./g, '')"
+                :value="true"
+                v-model="parameterState.extension.defaults[param]"
+              />
+            </div>
+            <div class="form-check form-check-inline">
+              <input
+                class="form-check-input"
+                type="radio"
+                :name="'inlineRadioOptions' + param.replace(/\./g, '')"
+                :id="'falseOption' + param.replace(/\./g, '')"
+                :value="false"
+                v-model="parameterState.extension.defaults[param]"
+              />
+              <label
+                class="form-check-label"
+                :for="'falseOption' + param.replace(/\./g, '')"
+                >False</label
+              >
+            </div>
+          </div>
+          <select
+            v-else-if="parameterSchema[param]?.['enum']"
+            class="form-select form-select-sm flex-grow"
+            v-model="parameterState.extension.defaults[param]"
+          >
+            <option
+              v-for="option in parameterSchema[param]?.['enum']"
+              :key="option"
+              :value="option"
+            >
+              {{ option }}
+            </option>
+          </select>
+          <div v-else>
+            <input
+              type="text"
+              class="form-control form-control-sm flex-grow"
+              v-model="parameterState.extension.defaults[param]"
+              :pattern="parameterSchema[param]?.['pattern']"
+            />
+          </div>
+          <button
+            type="button"
+            class="btn btn-outline-danger btn-sm ms-2"
+            @click="deleteDefaultParameter(param)"
+          >
+            Remove
+          </button>
+        </td>
+      </tr>
+    </tbody>
+  </table>
+  <pre><code>{{
+      parameterState.extension
+    }}</code></pre>
+  <pre><code>{{ parameterPools }}</code></pre>
+</template>
+
+<style scoped>
+.parameter-container:hover {
+  background: var(--bs-secondary-bg-subtle);
+}
+</style>