From 3760093617dd3747a4ccfb5c1b578b8422aec385 Mon Sep 17 00:00:00 2001 From: Sean Sube Date: Thu, 2 Mar 2023 17:24:45 -0600 Subject: [PATCH] feat(gui): translate most of the client --- gui/src/components/ImageCard.tsx | 19 +- gui/src/components/Logo.tsx | 8 + gui/src/components/OnnxError.tsx | 10 +- gui/src/components/OnnxWeb.tsx | 5 +- gui/src/components/control/ImageControl.tsx | 19 +- gui/src/components/control/ModelControl.tsx | 25 +-- .../components/control/OutpaintControl.tsx | 14 +- gui/src/components/control/UpscaleControl.tsx | 23 +- gui/src/components/input/ImageInput.tsx | 5 +- gui/src/components/input/MaskCanvas.tsx | 23 +- gui/src/components/input/NumericField.tsx | 6 +- gui/src/components/input/PromptInput.tsx | 16 +- gui/src/components/input/QueryList.tsx | 23 +- gui/src/components/tab/Blend.tsx | 6 +- gui/src/components/tab/Img2Img.tsx | 8 +- gui/src/components/tab/Inpaint.tsx | 29 +-- gui/src/components/tab/Settings.tsx | 24 ++- gui/src/components/tab/Txt2Img.tsx | 8 +- gui/src/components/tab/Upscale.tsx | 6 +- gui/src/config.ts | 2 +- gui/src/main.tsx | 3 + gui/src/strings.ts | 84 -------- gui/src/strings/all.ts | 15 +- gui/src/strings/de.ts | 159 ++++++++++++++ gui/src/strings/en.ts | 202 +++++++++++++++++- gui/src/strings/es.ts | 159 ++++++++++++++ gui/src/strings/fr.ts | 161 +++++++++++++- 27 files changed, 843 insertions(+), 219 deletions(-) create mode 100644 gui/src/components/Logo.tsx delete mode 100644 gui/src/strings.ts create mode 100644 gui/src/strings/de.ts create mode 100644 gui/src/strings/es.ts diff --git a/gui/src/components/ImageCard.tsx b/gui/src/components/ImageCard.tsx index b8aca9fb..c38329b9 100644 --- a/gui/src/components/ImageCard.tsx +++ b/gui/src/components/ImageCard.tsx @@ -9,7 +9,6 @@ import { useStore } from 'zustand'; import { ImageResponse } from '../client.js'; import { BLEND_SOURCES, ConfigContext, StateContext } from '../state.js'; -import { MODEL_LABELS, SCHEDULER_LABELS } from '../strings.js'; import { range, visibleIndex } from '../utils.js'; export interface ImageCardProps { @@ -99,8 +98,12 @@ export function ImageCard(props: ImageCardProps) { const [index, setIndex] = useState(0); const { t } = useTranslation(); - const model = mustDefault(MODEL_LABELS[params.model], params.model); - const scheduler = mustDefault(SCHEDULER_LABELS[params.scheduler], params.scheduler); + function getLabel(key: string, name: string) { + return mustDefault(t(`${key}.${name}`), name); + } + + const model = getLabel('model', params.model); + const scheduler = getLabel('scheduler', params.scheduler); return Model: {model} - Scheduler: {scheduler} - Seed: {params.seed} - CFG: {params.cfg} - Steps: {params.steps} - Size: {size.width}x{size.height} + {t('parameter.scheduler')}: {scheduler} + {t('parameter.seed')}: {params.seed} + {t('parameter.cfg')}: {params.cfg} + {t('parameter.steps')}: {params.steps} + {t('parameter.size')}: {size.width}x{size.height} {params.prompt} diff --git a/gui/src/components/Logo.tsx b/gui/src/components/Logo.tsx new file mode 100644 index 00000000..a5be712e --- /dev/null +++ b/gui/src/components/Logo.tsx @@ -0,0 +1,8 @@ +import { Link, Typography } from '@mui/material'; +import * as React from 'react'; + +export function Logo() { + return + ONNX Web + ; +} diff --git a/gui/src/components/OnnxError.tsx b/gui/src/components/OnnxError.tsx index aad760fa..da6d7147 100644 --- a/gui/src/components/OnnxError.tsx +++ b/gui/src/components/OnnxError.tsx @@ -1,7 +1,9 @@ -import { Box, Button, Container, Link, Stack, Typography } from '@mui/material'; +import { Box, Button, Container, Stack, Typography } from '@mui/material'; import * as React from 'react'; import { ReactNode } from 'react'; -import { STATE_KEY } from '../state'; + +import { STATE_KEY } from '../state.js'; +import { Logo } from './Logo.js'; export interface OnnxErrorProps { children?: ReactNode; @@ -19,9 +21,7 @@ export function OnnxError(props: OnnxErrorProps) { return ( - - ONNX Web - + diff --git a/gui/src/components/OnnxWeb.tsx b/gui/src/components/OnnxWeb.tsx index bb12d607..b9385c72 100644 --- a/gui/src/components/OnnxWeb.tsx +++ b/gui/src/components/OnnxWeb.tsx @@ -6,6 +6,7 @@ import { useHash } from 'react-use/lib/useHash'; import { ModelControl } from './control/ModelControl.js'; import { ImageHistory } from './ImageHistory.js'; +import { Logo } from './Logo.js'; import { Blend } from './tab/Blend.js'; import { Img2Img } from './tab/Img2Img.js'; import { Inpaint } from './tab/Inpaint.js'; @@ -41,9 +42,7 @@ export function OnnxWeb() { return ( - - ONNX Web - + diff --git a/gui/src/components/control/ImageControl.tsx b/gui/src/components/control/ImageControl.tsx index fdeb26b8..0925533f 100644 --- a/gui/src/components/control/ImageControl.tsx +++ b/gui/src/components/control/ImageControl.tsx @@ -3,13 +3,13 @@ import { Casino } from '@mui/icons-material'; import { Button, Stack } from '@mui/material'; import * as React from 'react'; import { useContext } from 'react'; +import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; import { useStore } from 'zustand'; import { BaseImgParams } from '../../client.js'; import { STALE_TIME } from '../../config.js'; import { ClientContext, ConfigContext, OnnxState, StateContext } from '../../state.js'; -import { SCHEDULER_LABELS } from '../../strings.js'; import { NumericField } from '../input/NumericField.js'; import { PromptInput } from '../input/PromptInput.js'; import { QueryList } from '../input/QueryList.js'; @@ -27,6 +27,7 @@ export function ImageControl(props: ImageControlProps) { const { params } = mustExist(useContext(ConfigContext)); const state = mustExist(useContext(StateContext)); const controlState = useStore(state, props.selector); + const { t } = useTranslation(); const client = mustExist(useContext(ClientContext)); const schedulers = useQuery('schedulers', async () => client.schedulers(), { @@ -37,8 +38,8 @@ export function ImageControl(props: ImageControlProps) { - New Seed + {t('parameter.newSeed')} s.model); // eslint-disable-next-line @typescript-eslint/unbound-method const setModel = useStore(state, (s) => s.setModel); + const { t } = useTranslation(); const models = useQuery('models', async () => client.models(), { staleTime: STALE_TIME, @@ -27,8 +28,8 @@ export function ModelControl() { return result.diffusion, @@ -56,8 +57,8 @@ export function ModelControl() { /> result.inversion, @@ -72,8 +73,8 @@ export function ModelControl() { /> result.upscaling, @@ -87,8 +88,8 @@ export function ModelControl() { /> result.correction, @@ -101,7 +102,7 @@ export function ModelControl() { }} /> s.outpaint); // eslint-disable-next-line @typescript-eslint/unbound-method const setOutpaint = useStore(state, (s) => s.setOutpaint); + const { t } = useTranslation(); return { + onChange={(_event) => { setOutpaint({ enabled: outpaint.enabled === false, }); @@ -28,7 +30,7 @@ export function OutpaintControl() { />} /> s.upscale); // eslint-disable-next-line @typescript-eslint/unbound-method const setUpscale = useStore(state, (s) => s.setUpscale); + const { t } = useTranslation(); return } /> } /> Upscale Order diff --git a/gui/src/components/input/ImageInput.tsx b/gui/src/components/input/ImageInput.tsx index cd607bbd..d49afaa3 100644 --- a/gui/src/components/input/ImageInput.tsx +++ b/gui/src/components/input/ImageInput.tsx @@ -2,6 +2,7 @@ import { doesExist, Maybe, mustDefault, mustExist } from '@apextoaster/js-utils' import { PhotoCamera } from '@mui/icons-material'; import { Button, Stack, Typography } from '@mui/material'; import * as React from 'react'; +import { useTranslation } from 'react-i18next'; export interface ImageInputProps { filter: string; @@ -14,6 +15,8 @@ export interface ImageInputProps { } export function ImageInput(props: ImageInputProps) { + const { t } = useTranslation(); + function renderImage() { if (doesExist(props.image)) { if (mustDefault(props.hideSelection, false)) { @@ -28,7 +31,7 @@ export function ImageInput(props: ImageInputProps) { }} />; } else { - return Please select an image.; + return {t('input.image.empty')}; } } diff --git a/gui/src/components/input/MaskCanvas.tsx b/gui/src/components/input/MaskCanvas.tsx index 2d1a14ef..de43fced 100644 --- a/gui/src/components/input/MaskCanvas.tsx +++ b/gui/src/components/input/MaskCanvas.tsx @@ -3,6 +3,7 @@ import { Download, FormatColorFill, Gradient, InvertColors, Save, Undo } from '@ import { Button, Stack, Typography } from '@mui/material'; import { throttle } from 'lodash'; import React, { RefObject, useContext, useEffect, useMemo, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; import { useStore } from 'zustand'; import { SAVE_TIME } from '../../config.js'; @@ -201,6 +202,7 @@ export function MaskCanvas(props: MaskCanvasProps) { const brush = useStore(state, (s) => s.brush); // eslint-disable-next-line @typescript-eslint/unbound-method const setBrush = useStore(state, (s) => s.setBrush); + const { t } = useTranslation(); useEffect(() => { if (dirty.current) { @@ -308,14 +310,11 @@ export function MaskCanvas(props: MaskCanvasProps) { onMouseUp={finishPainting} onMouseMove={drawMouse} /> - - Black pixels in the mask will stay the same, white pixels will be replaced. The masked pixels will be blended - with the noise source before the diffusion model runs, giving it more variety to use. - + {t('mask.help')} { drawFill(floodBlack); }}> - Fill with black + {t('mask.fill.black')} diff --git a/gui/src/components/input/NumericField.tsx b/gui/src/components/input/NumericField.tsx index 1f4ea018..2f891902 100644 --- a/gui/src/components/input/NumericField.tsx +++ b/gui/src/components/input/NumericField.tsx @@ -1,6 +1,7 @@ import { doesExist } from '@apextoaster/js-utils'; import { Slider, Stack, TextField } from '@mui/material'; import * as React from 'react'; +import { useTranslation } from 'react-i18next'; export function parseNumber(num: string, decimal = false): number { if (decimal) { @@ -24,14 +25,15 @@ export interface ImageControlProps { export function NumericField(props: ImageControlProps) { const { decimal = false, disabled = false, label, min, max, step, value } = props; - const error = (value < min) || (value > max); + const { t } = useTranslation(); + return PROMPT_LIMIT; + const { t } = useTranslation(); + function promptHelper() { + const params = { + current: promptLength, + max: PROMPT_LIMIT, + }; + if (error) { - return `Too many tokens: ${promptLength}/${PROMPT_LIMIT}`; + return t('input.prompt.error.length', params); } else { - return `Tokens: ${promptLength}/${PROMPT_LIMIT}`; + return t('input.prompt.tokens', params); } } return { diff --git a/gui/src/components/input/QueryList.tsx b/gui/src/components/input/QueryList.tsx index 6230cce3..548968a9 100644 --- a/gui/src/components/input/QueryList.tsx +++ b/gui/src/components/input/QueryList.tsx @@ -2,6 +2,7 @@ import { doesExist, mustDefault, mustExist } from '@apextoaster/js-utils'; import { Alert, FormControl, InputLabel, MenuItem, Select, Typography } from '@mui/material'; import * as React from 'react'; import { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { UseQueryResult } from 'react-query'; export interface QueryListComplete { @@ -15,7 +16,7 @@ export interface QueryListFilter { export interface QueryListProps { id: string; - labels: Record; + labelKey: string; name: string; value: string; @@ -47,9 +48,11 @@ export function filterQuery(query: QueryListComplete | QueryListFilter, sh } export function QueryList(props: QueryListProps) { - const { labels, query, showEmpty = false, value } = props; + const { labelKey, query, showEmpty = false, value } = props; const { result } = query; + const { t } = useTranslation(); + function firstValidValue(): string { if (doesExist(value) && data.includes(value)) { return value; @@ -58,6 +61,10 @@ export function QueryList(props: QueryListProps) { } } + function getLabel(name: string) { + return mustDefault(t(`${labelKey}.${name}`), name); + } + // update state when previous selection was invalid: https://github.com/ssube/onnx-web/issues/120 useEffect(() => { if (result.status === 'success' && doesExist(result.data) && doesExist(props.onChange)) { @@ -70,18 +77,20 @@ export function QueryList(props: QueryListProps) { if (result.status === 'error') { if (result.error instanceof Error) { - return Error: {result.error.message}; + return {t('input.list.error.specific', { + message: result.error.message, + })}; } else { - return Unknown Error; + return {t('input.list.error.unknown')}; } } if (result.status === 'loading') { - return Loading...; + return {t('input.list.loading')}; } if (result.status === 'idle') { - return Idle?; + return {t('input.list.idle')}; } // else: success @@ -100,7 +109,7 @@ export function QueryList(props: QueryListProps) { } }} > - {data.map((name) => {mustDefault(labels[name], name)})} + {data.map((name) => {getLabel(name)})} ; } diff --git a/gui/src/components/tab/Blend.tsx b/gui/src/components/tab/Blend.tsx index 2460e400..a4aed16a 100644 --- a/gui/src/components/tab/Blend.tsx +++ b/gui/src/components/tab/Blend.tsx @@ -2,6 +2,7 @@ import { mustDefault, mustExist } from '@apextoaster/js-utils'; import { Box, Button, Stack } from '@mui/material'; import * as React from 'react'; import { useContext } from 'react'; +import { useTranslation } from 'react-i18next'; import { useMutation, useQueryClient } from 'react-query'; import { useStore } from 'zustand'; @@ -37,6 +38,7 @@ export function Blend() { const setBlend = useStore(state, (s) => s.setBlend); // eslint-disable-next-line @typescript-eslint/unbound-method const setLoading = useStore(state, (s) => s.pushLoading); + const { t } = useTranslation(); const sources = mustDefault(blend.sources, []); @@ -48,7 +50,7 @@ export function Blend() { filter={IMAGE_FILTER} image={sources[idx]} hideSelection={true} - label='Source' + label={t('input.image.source')} onChange={(file) => { const newSources = [...sources]; newSources[idx] = file; @@ -73,7 +75,7 @@ export function Blend() { disabled={sources.length === 0} variant='contained' onClick={() => upload.mutate()} - >Generate + >{t('generate')} ; } diff --git a/gui/src/components/tab/Img2Img.tsx b/gui/src/components/tab/Img2Img.tsx index 0ac4f08e..1c5b393c 100644 --- a/gui/src/components/tab/Img2Img.tsx +++ b/gui/src/components/tab/Img2Img.tsx @@ -2,6 +2,7 @@ import { doesExist, mustExist } from '@apextoaster/js-utils'; import { Box, Button, Stack } from '@mui/material'; import * as React from 'react'; import { useContext } from 'react'; +import { useTranslation } from 'react-i18next'; import { useMutation, useQueryClient } from 'react-query'; import { useStore } from 'zustand'; @@ -39,10 +40,11 @@ export function Img2Img() { const setImg2Img = useStore(state, (s) => s.setImg2Img); // eslint-disable-next-line @typescript-eslint/unbound-method const setLoading = useStore(state, (s) => s.pushLoading); + const { t } = useTranslation(); return - { + { setImg2Img({ source: file, }); @@ -50,7 +52,7 @@ export function Img2Img() { s.img2img} onChange={setImg2Img} /> upload.mutate()} - >Generate + >{t('generate')} ; } diff --git a/gui/src/components/tab/Inpaint.tsx b/gui/src/components/tab/Inpaint.tsx index 69416dfa..b3268c64 100644 --- a/gui/src/components/tab/Inpaint.tsx +++ b/gui/src/components/tab/Inpaint.tsx @@ -1,14 +1,13 @@ import { doesExist, mustExist } from '@apextoaster/js-utils'; import { Box, Button, FormControl, FormControlLabel, InputLabel, MenuItem, Select, Stack } from '@mui/material'; -import { capitalize } from 'lodash'; import * as React from 'react'; import { useContext } from 'react'; +import { useTranslation } from 'react-i18next'; import { useMutation, useQuery, useQueryClient } from 'react-query'; import { useStore } from 'zustand'; import { IMAGE_FILTER, STALE_TIME } from '../../config.js'; import { ClientContext, ConfigContext, StateContext } from '../../state.js'; -import { MASK_LABELS, NOISE_LABELS } from '../../strings.js'; import { ImageControl } from '../control/ImageControl.js'; import { OutpaintControl } from '../control/OutpaintControl.js'; import { UpscaleControl } from '../control/UpscaleControl.js'; @@ -65,6 +64,7 @@ export function Inpaint() { const setInpaint = useStore(state, (s) => s.setInpaint); // eslint-disable-next-line @typescript-eslint/unbound-method const setLoading = useStore(state, (s) => s.pushLoading); + const { t } = useTranslation(); const query = useQueryClient(); const upload = useMutation(uploadSource, { @@ -76,7 +76,7 @@ export function Inpaint() { { setInpaint({ @@ -87,7 +87,7 @@ export function Inpaint() { { setInpaint({ @@ -111,7 +111,7 @@ export function Inpaint() { }} /> Tile Order upload.mutate()} - >Generate + >{t('generate')} ; } diff --git a/gui/src/components/tab/Settings.tsx b/gui/src/components/tab/Settings.tsx index 0f5e9eb8..030a8880 100644 --- a/gui/src/components/tab/Settings.tsx +++ b/gui/src/components/tab/Settings.tsx @@ -3,6 +3,7 @@ import { Refresh } from '@mui/icons-material'; import { Alert, Button, Stack, TextField } from '@mui/material'; import * as React from 'react'; import { useContext, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useStore } from 'zustand'; import { getApiRoot } from '../../config.js'; @@ -32,28 +33,29 @@ export function Settings() { const [json, setJson] = useState(JSON.stringify(state, removeBlobs)); const [root, setRoot] = useState(getApiRoot(config)); + const { t } = useTranslation(); return state.setLimit(value)} /> - { + { state.setDefaults({ prompt: event.target.value, }); }} /> - { + { state.setDefaults({ scheduler: event.target.value, }); }} /> - { + { setRoot(event.target.value); }} /> {config.params.version} - { + { setJson(event.target.value); }} /> - - - - + + + + ; } diff --git a/gui/src/components/tab/Txt2Img.tsx b/gui/src/components/tab/Txt2Img.tsx index 1b1d4406..0b735fe2 100644 --- a/gui/src/components/tab/Txt2Img.tsx +++ b/gui/src/components/tab/Txt2Img.tsx @@ -2,6 +2,7 @@ import { mustExist } from '@apextoaster/js-utils'; import { Box, Button, Stack } from '@mui/material'; import * as React from 'react'; import { useContext } from 'react'; +import { useTranslation } from 'react-i18next'; import { useMutation, useQueryClient } from 'react-query'; import { useStore } from 'zustand'; @@ -33,13 +34,14 @@ export function Txt2Img() { const setTxt2Img = useStore(state, (s) => s.setTxt2Img); // eslint-disable-next-line @typescript-eslint/unbound-method const setLoading = useStore(state, (s) => s.pushLoading); + const { t } = useTranslation(); return s.txt2img} onChange={setTxt2Img} /> generate.mutate()} - >Generate + >{t('generate')} ; } diff --git a/gui/src/components/tab/Upscale.tsx b/gui/src/components/tab/Upscale.tsx index b0e00f51..20243f83 100644 --- a/gui/src/components/tab/Upscale.tsx +++ b/gui/src/components/tab/Upscale.tsx @@ -2,6 +2,7 @@ import { doesExist, mustExist } from '@apextoaster/js-utils'; import { Box, Button, Stack } from '@mui/material'; import * as React from 'react'; import { useContext } from 'react'; +import { useTranslation } from 'react-i18next'; import { useMutation, useQueryClient } from 'react-query'; import { useStore } from 'zustand'; @@ -35,13 +36,14 @@ export function Upscale() { const setSource = useStore(state, (s) => s.setUpscaleTab); // eslint-disable-next-line @typescript-eslint/unbound-method const setLoading = useStore(state, (s) => s.pushLoading); + const { t } = useTranslation(); return { setSource({ source: file, @@ -60,7 +62,7 @@ export function Upscale() { disabled={doesExist(params.source) === false} variant='contained' onClick={() => upload.mutate()} - >Generate + >{t('generate')} ; } diff --git a/gui/src/config.ts b/gui/src/config.ts index 1d46f815..ea4fe56c 100644 --- a/gui/src/config.ts +++ b/gui/src/config.ts @@ -11,7 +11,7 @@ export interface ConfigNumber { export interface ConfigString { default: string; - keys: Array; + keys: Record; } /** diff --git a/gui/src/main.tsx b/gui/src/main.tsx index 351d5fa2..ed93988e 100644 --- a/gui/src/main.tsx +++ b/gui/src/main.tsx @@ -61,8 +61,11 @@ export async function main() { escapeValue: false, // not needed for react as it escapes by default }, resources: I18N_STRINGS, + returnEmptyString: false, }); + i18n.addResourceBundle(i18n.resolvedLanguage, 'model', params.model.keys); + // prep zustand with a slice for each tab, using local storage const { createBrushSlice, diff --git a/gui/src/strings.ts b/gui/src/strings.ts deleted file mode 100644 index 6e6dec3e..00000000 --- a/gui/src/strings.ts +++ /dev/null @@ -1,84 +0,0 @@ -// TODO: set up i18next -export const MODEL_LABELS: Record = { - 'stable-diffusion-onnx-v1-4': 'Stable Diffusion v1.4', - 'stable-diffusion-onnx-v1-5': 'Stable Diffusion v1.5', - 'stable-diffusion-onnx-v1-inpainting': 'SD Inpainting v1', - 'stable-diffusion-onnx-v2-0': 'Stable Diffusion v2.0', - 'stable-diffusion-onnx-v2-1': 'Stable Diffusion v2.1', - 'stable-diffusion-onnx-v2-inpainting': 'SD Inpainting v2', - // upscaling - 'upscaling-real-esrgan-x2-plus': 'Real ESRGAN x2 Plus', - 'upscaling-real-esrgan-x4-plus': 'Real ESRGAN x4 Plus', - 'upscaling-real-esrgan-x4-v3': 'Real ESRGAN x4 v3', - 'upscaling-stable-diffusion-x4': 'Stable Diffusion x4', - // correction - 'correction-codeformer': 'CodeFormer', - 'correction-gfpgan-v1-3': 'GFPGAN v1.3', - // extras - 'diffusion-stablydiffused-aesthetic-v2-6': 'Aesthetic Mix v2.6', - 'diffusion-anything': 'Anything', - 'diffusion-anything-v3': 'Anything v3', - 'diffusion-anything-v4': 'Anything v4', - 'diffusion-darkvictorian': 'Dark Victorian', - 'diffusion-dreamlike-photoreal': 'Dreamlike Photoreal', - 'diffusion-dreamlike-photoreal-v1': 'Dreamlike Photoreal 1.0', - 'diffusion-dreamlike-photoreal-v2': 'Dreamlike Photoreal 2.0', - 'diffusion-ghibli': 'Ghibli', - 'diffusion-knollingcase': 'Knollingcase', - 'diffusion-openjourney': 'OpenJourney', - 'diffusion-openjourney-v1': 'OpenJourney v1', - 'diffusion-openjourney-v2': 'OpenJourney v2', - 'diffusion-pastel-mix': 'Pastel Mix', - 'diffusion-unstable-ink-dream-v6': 'Unstable Ink Dream v6', -}; - -export const INVERSION_LABELS: Record = { - '': 'None', - 'inversion-cubex': 'Cubex', - 'inversion-birb': 'Birb Style', - 'inversion-line-art': 'Line Art', - 'inversion-minecraft': 'Minecraft Concept', -}; - -export const PLATFORM_LABELS: Record = { - amd: 'AMD GPU', - // eslint-disable-next-line id-blacklist - any: 'Any Platform', - cpu: 'CPU', - cuda: 'CUDA', - directml: 'DirectML', - nvidia: 'Nvidia GPU', - rocm: 'ROCm', -}; - -export const SCHEDULER_LABELS: Record = { - 'ddim': 'DDIM', - 'ddpm': 'DDPM', - 'deis-multi': 'DEIS Multistep', - 'dpm-multi': 'DPM Multistep', - 'dpm-single': 'DPM Singlestep', - 'euler': 'Euler', - 'euler-a': 'Euler Ancestral', - 'heun': 'Heun', - 'k-dpm-2-a': 'KDPM2 Ancestral', - 'k-dpm-2': 'KDPM2', - 'karras-ve': 'Karras Ve', - 'ipndm': 'iPNDM', - 'lms-discrete': 'LMS', - 'pndm': 'PNDM', -}; - -export const NOISE_LABELS: Record = { - 'fill-edge': 'Fill Edges', - 'fill-mask': 'Fill Masked', - 'gaussian': 'Gaussian Blur', - 'histogram': 'Histogram Noise', - 'normal': 'Gaussian Noise', - 'uniform': 'Uniform Noise', -}; - -export const MASK_LABELS: Record = { - 'none': 'None', - 'gaussian-multiply': 'Gaussian Multiply', - 'gaussian-screen': 'Gaussian Screen', -}; diff --git a/gui/src/strings/all.ts b/gui/src/strings/all.ts index d75a7505..9d9e54d9 100644 --- a/gui/src/strings/all.ts +++ b/gui/src/strings/all.ts @@ -1,7 +1,18 @@ -import { I18N_STRINGS_EN, RequiredStrings } from './en.js'; +import { I18N_STRINGS_DE } from './de.js'; +import { I18N_STRINGS_EN } from './en.js'; +import { I18N_STRINGS_ES } from './es.js'; import { I18N_STRINGS_FR } from './fr.js'; -export const I18N_STRINGS: Record = { +// easy way to make sure all locales have the complete set of strings +export type RequiredStrings = typeof I18N_STRINGS_EN['en']['translation']; + +interface PartialLanguage { + [key: string]: Omit; +} + +export const I18N_STRINGS: Record = { + ...I18N_STRINGS_DE, ...I18N_STRINGS_EN, + ...I18N_STRINGS_ES, ...I18N_STRINGS_FR, }; diff --git a/gui/src/strings/de.ts b/gui/src/strings/de.ts new file mode 100644 index 00000000..72d5681d --- /dev/null +++ b/gui/src/strings/de.ts @@ -0,0 +1,159 @@ +/** + * This is a machine translation and may have some mistakes. + * + * If you have a more accurate translation for any of these strings, please open an issue or pull request. + */ +export const I18N_STRINGS_DE = { + de: { + translation: { + generate: 'Erzeugen', + history: { + empty: 'Keine neuere Geschichte. Drücken Sie Generieren, um ein Bild zu erstellen.', + }, + input: { + image: { + empty: 'Bitte wählen Sie ein Bild aus', + mask: 'Maskenbild', + source: 'Quellbild', + }, + list: { + error: { + specific: '', + unknown: 'unbekannter Fehler', + }, + idle: '', + loading: '', + }, + numeric: { + error: { + range: 'außerhalb der Reichweite', + }, + }, + prompt: { + tokens: '', + error: { + length: '', + }, + }, + }, + loading: { + cancel: 'stornieren', + progress: '{{current}} von {{total}} Schritten', + unknown: 'vielen', + }, + mask: { + fill: { + black: 'Mit Schwarz füllen', + white: 'Weiß füllen', + }, + gray: { + black: 'Grau in schwarz umwandeln', + white: 'Grau in weiß umwandeln', + }, + help: '', + invert: 'Farben umkehren', + }, + maskFilter: { + 'gaussian-multiply': '', + 'gaussian-screen': '', + }, + modelType: { + correction: 'Korrekturmodelle', + diffusion: 'Diffusionsmodelle', + inversion: '', + upscaling: 'Modelle vergrößern', + }, + noiseSource: { + 'fill-edge': 'Kanten füllen', + 'fill-mask': 'Maskiert füllen', + 'gaussian': 'Gaußsche Unschärfe', + 'histogram': 'Histogrammrauschen', + 'normal': 'Gaußsches Rauschen', + 'uniform': 'gleichförmiges Rauschen', + }, + parameter: { + batch: 'Losgröße', + brush: { + color: 'Pinselfarbe', + size: 'Pinselgröße', + strength: 'Pinseldeckkraft', + }, + cfg: '', + eta: '', + fillColor: 'Füllfarbe', + height: 'Höhe', + lpw: '', + maskFilter: 'Maskenfilter', + noiseSource: 'Lärmquelle', + negativePrompt: 'Gegenprompt', + newSeed: 'neue Saat', + outpaint: { + label: '', + left: 'Links', + right: 'Rechts', + top: 'Top', + bottom: 'Unterseite', + }, + platform: '', + prompt: 'Prompt', + scheduler: 'Planer', + seed: 'Saat', + size: '', + steps: 'Schritte', + strength: 'Stärke', + tileOrder: '', + upscale: { + label: '', + denoise: 'Entrauschen', + scale: 'Skala', + order: '', + outscale: 'Ausgangsskala', + }, + width: 'Breite', + correction: { + label: 'Gesichtskorrektur', + strength: 'Stärke', + outscale: 'Ausgangsskala', + }, + }, + setting: { + connectServer: 'verbinden zum Server', + history: 'Bildgeschichte', + loadState: 'Laden', + prompt: 'Standard-Eingabeaufforderung', + reset: { + all: 'Alles zurücksetzen', + img2img: 'Img2img zurücksetzen', + inpaint: 'Inpaint zurücksetzen', + txt2img: 'Txt2img zurücksetzen', + }, + scheduler: 'Standardplaner', + server: 'API-Server', + state: 'Kundenstatus', + }, + tab: { + blend: 'Mischung', + img2img: '', + inpaint: '', + txt2txt: '', + txt2img: '', + upscale: 'Vergrößern', + }, + tileOrder: { + grid: 'Raster', + spiral: 'Spiral', + }, + tooltip: { + delete: 'Löschen', + next: 'Nächste', + previous: 'Vorherige', + save: 'Speichern', + }, + upscaleOrder: { + 'correction-both': '', + 'correction-first': '', + 'correction-last': '', + }, + }, + }, +}; diff --git a/gui/src/strings/en.ts b/gui/src/strings/en.ts index 86abd2ea..24467588 100644 --- a/gui/src/strings/en.ts +++ b/gui/src/strings/en.ts @@ -1,14 +1,198 @@ export const I18N_STRINGS_EN = { en: { translation: { + generate: 'Generate', history: { - empty: 'No results. Press Generate to create an image.', + empty: 'No recent history. Press Generate to create an image.', + }, + input: { + image: { + empty: 'Please select an image.', + mask: 'Mask', + source: 'Source', + }, + list: { + error: { + specific: 'Error: {{message}}', + unknown: 'Unknown Error', + }, + idle: 'Idle?', + loading: 'Loading...', + }, + numeric: { + error: { + range: 'Out of range', + }, + }, + prompt: { + tokens: 'Tokens: {{current}}/{{max}}', + error: { + length: 'Too many tokens: {{current}}/{{max}}', + }, + }, }, loading: { cancel: 'Cancel', progress: '{{current}} of {{total}} steps', unknown: 'many', }, + mask: { + fill: { + black: 'Fill with black', + white: 'Fill with white', + }, + gray: { + black: 'Gray to black', + white: 'Gray to white', + }, + // eslint-disable-next-line max-len + help: 'Black pixels in the mask will stay the same, white pixels will be replaced. The masked pixels will be blended with the noise source before the diffusion model runs, giving it more variety to use.', + invert: 'Invert', + }, + maskFilter: { + 'gaussian-multiply': 'Gaussian Multiply', + 'gaussian-screen': 'Gaussian Screen', + }, + model: { + '': 'None', + // correction + 'correction-codeformer': 'CodeFormer', + 'correction-gfpgan-v1-3': 'GFPGAN v1.3', + // diffusion + 'stable-diffusion-onnx-v1-4': 'Stable Diffusion v1.4', + 'stable-diffusion-onnx-v1-5': 'Stable Diffusion v1.5', + 'stable-diffusion-onnx-v1-inpainting': 'SD Inpainting v1', + 'stable-diffusion-onnx-v2-0': 'Stable Diffusion v2.0', + 'stable-diffusion-onnx-v2-1': 'Stable Diffusion v2.1', + 'stable-diffusion-onnx-v2-inpainting': 'SD Inpainting v2', + // inversion + 'inversion-cubex': 'Cubex', + 'inversion-birb': 'Birb Style', + 'inversion-line-art': 'Line Art', + 'inversion-minecraft': 'Minecraft Concept', + 'inversion-ugly-sonic': 'Ugly Sonic', + // upscaling + 'upscaling-real-esrgan-x2-plus': 'Real ESRGAN x2 Plus', + 'upscaling-real-esrgan-x4-plus': 'Real ESRGAN x4 Plus', + 'upscaling-real-esrgan-x4-v3': 'Real ESRGAN x4 v3', + 'upscaling-stable-diffusion-x4': 'Stable Diffusion x4', + // extras + 'diffusion-stablydiffused-aesthetic-v2-6': 'Aesthetic Mix v2.6', + 'diffusion-anything': 'Anything', + 'diffusion-anything-v3': 'Anything v3', + 'diffusion-anything-v4': 'Anything v4', + 'diffusion-darkvictorian': 'Dark Victorian', + 'diffusion-dreamlike-photoreal': 'Dreamlike Photoreal', + 'diffusion-dreamlike-photoreal-v1': 'Dreamlike Photoreal 1.0', + 'diffusion-dreamlike-photoreal-v2': 'Dreamlike Photoreal 2.0', + 'diffusion-ghibli': 'Ghibli', + 'diffusion-knollingcase': 'Knollingcase', + 'diffusion-openjourney': 'OpenJourney', + 'diffusion-openjourney-v1': 'OpenJourney v1', + 'diffusion-openjourney-v2': 'OpenJourney v2', + 'diffusion-pastel-mix': 'Pastel Mix', + 'diffusion-unstable-ink-dream-v6': 'Unstable Ink Dream v6', + }, + modelType: { + correction: 'Correction Model', + diffusion: 'Diffusion Model', + inversion: 'Textual Inversion', + upscaling: 'Upscaling Model', + }, + noiseSource: { + 'fill-edge': 'Fill Edges', + 'fill-mask': 'Fill Masked', + 'gaussian': 'Gaussian Blur', + 'histogram': 'Histogram Noise', + 'normal': 'Gaussian Noise', + 'uniform': 'Uniform Noise', + }, + parameter: { + batch: 'Batch Size', + brush: { + color: 'Brush Color', + size: 'Brush Size', + strength: 'Brush Strength', + }, + cfg: 'CFG', + eta: 'Eta', + fillColor: 'Fill Color', + height: 'Height', + lpw: 'Long Prompt Weighting', + maskFilter: 'Mask Filter', + noiseSource: 'Noise Source', + negativePrompt: 'Negative Prompt', + newSeed: 'New Seed', + outpaint: { + label: 'Outpaint', + left: 'Left', + right: 'Right', + top: 'Top', + bottom: 'Bottom', + }, + platform: 'Platform', + prompt: 'Prompt', + scheduler: 'Scheduler', + seed: 'Seed', + size: 'Size', + steps: 'Steps', + strength: 'Strength', + tileOrder: 'Tile Order', + upscale: { + label: 'Upscale', + denoise: 'Denoise', + scale: 'Scale', + order: 'Upscale Order', + outscale: 'Outscale', + }, + width: 'Width', + correction: { + label: 'Face Correction', + strength: 'Strength', + outscale: 'Outscale', + }, + }, + platform: { + amd: 'AMD GPU', + // eslint-disable-next-line id-blacklist + any: 'Any Platform', + cpu: 'CPU', + cuda: 'CUDA', + directml: 'DirectML', + nvidia: 'Nvidia GPU', + rocm: 'ROCm', + }, + setting: { + connectServer: 'Connect', + history: 'Image History', + loadState: 'Load', + prompt: 'Default Prompt', + reset: { + all: 'Reset All', + img2img: 'Reset Img2img', + inpaint: 'Reset Inpaint', + txt2img: 'Reset Txt2img', + }, + scheduler: 'Default Scheduler', + server: 'API Server', + state: 'Client State', + }, + scheduler: { + 'ddim': 'DDIM', + 'ddpm': 'DDPM', + 'deis-multi': 'DEIS Multistep', + 'dpm-multi': 'DPM Multistep', + 'dpm-single': 'DPM Singlestep', + 'euler': 'Euler', + 'euler-a': 'Euler Ancestral', + 'heun': 'Heun', + 'k-dpm-2-a': 'KDPM2 Ancestral', + 'k-dpm-2': 'KDPM2', + 'karras-ve': 'Karras Ve', + 'ipndm': 'iPNDM', + 'lms-discrete': 'LMS', + 'pndm': 'PNDM', + }, tab: { blend: 'Blend', img2img: 'Img2img', @@ -17,15 +201,21 @@ export const I18N_STRINGS_EN = { txt2img: 'Txt2img', upscale: 'Upscale', }, + tileOrder: { + grid: 'Grid', + spiral: 'Spiral', + }, tooltip: { delete: 'Delete', - next: 'EN Next', - previous: 'EN Previous', + next: 'Next', + previous: 'Previous', save: 'Save', }, + upscaleOrder: { + 'correction-both': 'Correction Both', + 'correction-first': 'Correction First', + 'correction-last': 'Correction Last', + }, } }, }; - -// easy way to make sure all locales have the complete set of strings -export type RequiredStrings = typeof I18N_STRINGS_EN['en']; diff --git a/gui/src/strings/es.ts b/gui/src/strings/es.ts new file mode 100644 index 00000000..72640359 --- /dev/null +++ b/gui/src/strings/es.ts @@ -0,0 +1,159 @@ +/** + * This is a machine translation and may have some mistakes. + * + * If you have a more accurate translation for any of these strings, please open an issue or pull request. + */ +export const I18N_STRINGS_ES = { + es: { + translation: { + generate: 'Generar', + history: { + empty: 'Sin antecedentes recientes. Presiona generar para crear una nueva imagen.', + }, + input: { + image: { + empty: 'Por favor, seleccione una imagen.', + mask: 'Máscara de imagen', + source: 'Imagen de origen', + }, + list: { + error: { + specific: 'Error: {{message}}', + unknown: 'Error desconocido', + }, + idle: '', + loading: '', + }, + numeric: { + error: { + range: 'Fuera de intervalo', + }, + }, + prompt: { + tokens: '', + error: { + length: '', + }, + }, + }, + loading: { + cancel: 'Cancelar', + progress: '{{current}} de {{total}} pasos', + unknown: 'muchos', + }, + mask: { + fill: { + black: 'Llenar con negro', + white: 'Llenar con blanco', + }, + gray: { + black: 'Convertir gris a negro', + white: 'Convertir gris a blanco', + }, + help: '', + invert: 'Colores invertidos', + }, + maskFilter: { + 'gaussian-multiply': '', + 'gaussian-screen': '', + }, + modelType: { + correction: 'Modelo de corrección', + diffusion: 'Modelo de difusión', + inversion: '', + upscaling: 'Modelo de aumento', + }, + noiseSource: { + 'fill-edge': 'Rellena los bordes', + 'fill-mask': 'Rellena la máscara', + 'gaussian': 'Desenfoque gaussiano', + 'histogram': 'Ruido de histograma', + 'normal': 'Ruido gaussiano', + 'uniform': 'Ruido uniforme', + }, + parameter: { + batch: 'Tamaño del lote', + brush: { + color: 'Color del pincel', + size: 'Tamaño del pincel', + strength: 'Opacidad del pincel', + }, + cfg: '', + eta: '', + fillColor: 'Color de relleno', + height: 'Altura', + lpw: '', + maskFilter: 'Filtro de máscara', + noiseSource: 'Fuente de ruido', + negativePrompt: '', + newSeed: '', + outpaint: { + label: '', + left: 'Izquierda', + right: 'Derecha', + top: 'Top', + bottom: 'Fondo', + }, + platform: 'Plataforma de hardware', + prompt: 'Aviso', + scheduler: 'Planificador', + seed: 'Semilla', + size: '', + steps: 'Pasos', + strength: 'Fuerza', + tileOrder: 'Orden de secciones', + upscale: { + label: 'Aumento', + denoise: '', + scale: 'Escala', + order: '', + outscale: 'Escala de producción', + }, + width: 'Anchura', + correction: { + label: 'Corrección facial', + strength: 'Fuerza', + outscale: 'Escala de producción', + }, + }, + setting: { + connectServer: 'Conectar al servidor', + history: 'Historia de la imagen', + loadState: '', + prompt: '', + reset: { + all: 'Resetear todo', + img2img: 'Resetear img2img', + inpaint: 'Resetear inpaint', + txt2img: 'Resetear txt2img', + }, + scheduler: 'Programador predeterminado', + server: 'Servidor API', + state: 'Estado del cliente', + }, + tab: { + blend: 'Mezclar', + img2img: 'Img2img', + inpaint: 'Inpaint', + txt2txt: 'Txt2txt', + txt2img: 'Txt2img', + upscale: 'Aumentar', + }, + tileOrder: { + grid: 'Red', + spiral: 'Espiral', + }, + tooltip: { + delete: 'Borrar', + next: 'Próximo', + previous: 'Anterior', + save: 'Ahorrar', + }, + upscaleOrder: { + 'correction-both': '', + 'correction-first': '', + 'correction-last': '', + }, + }, + }, +}; diff --git a/gui/src/strings/fr.ts b/gui/src/strings/fr.ts index 722b4abf..eb5d43f2 100644 --- a/gui/src/strings/fr.ts +++ b/gui/src/strings/fr.ts @@ -1,20 +1,159 @@ +/** + * This is a machine translation and may have some mistakes. + * + * If you have a more accurate translation for any of these strings, please open an issue or pull request. + */ export const I18N_STRINGS_FR = { fr: { translation: { + generate: '', + history: { + empty: '', + }, + input: { + image: { + empty: '', + mask: '', + source: '', + }, + list: { + error: { + specific: '', + unknown: '', + }, + idle: '', + loading: '', + }, + numeric: { + error: { + range: '', + }, + }, + prompt: { + tokens: '', + error: { + length: '', + }, + }, + }, + loading: { + cancel: '', + progress: '', + unknown: '', + }, + mask: { + fill: { + black: '', + white: '', + }, + gray: { + black: '', + white: '', + }, + help: '', + invert: '', + }, + maskFilter: { + 'gaussian-multiply': '', + 'gaussian-screen': '', + }, + modelType: { + correction: '', + diffusion: '', + inversion: '', + upscaling: '', + }, + noiseSource: { + 'fill-edge': '', + 'fill-mask': '', + 'gaussian': '', + 'histogram': '', + 'normal': '', + 'uniform': '', + }, + parameter: { + batch: '', + brush: { + color: '', + size: '', + strength: '', + }, + cfg: '', + eta: '', + fillColor: '', + height: '', + lpw: '', + maskFilter: '', + noiseSource: '', + negativePrompt: '', + newSeed: '', + outpaint: { + label: '', + left: '', + right: '', + top: '', + bottom: '', + }, + platform: '', + prompt: '', + scheduler: '', + seed: '', + size: '', + steps: '', + strength: '', + tileOrder: '', + upscale: { + label: '', + denoise: '', + scale: '', + order: '', + outscale: '', + }, + width: '', + correction: { + label: '', + strength: '', + outscale: '', + }, + }, + setting: { + connectServer: '', + history: '', + loadState: '', + prompt: '', + reset: { + all: '', + img2img: '', + inpaint: '', + txt2img: '', + }, + scheduler: '', + server: '', + state: '', + }, tab: { - blend: 'Blend', - img2img: 'Img2img', - inpaint: 'Inpaint', - txt2txt: 'Txt2txt', - txt2img: 'Txt2img', - upscale: 'Upscale', + blend: '', + img2img: '', + inpaint: '', + txt2txt: '', + txt2img: '', + upscale: '', + }, + tileOrder: { + grid: '', + spiral: '', }, tooltip: { - delete: 'Delete', - next: 'FR-Next', - previous: 'FR-Previous', - save: 'Save', + delete: '', + next: '', + previous: '', + save: '', }, - } + upscaleOrder: { + 'correction-both': '', + 'correction-first': '', + 'correction-last': '', + }, + }, }, };