From 3b2bce499a119772e9fc6b90ded217ac9ed03428 Mon Sep 17 00:00:00 2001 From: Chris Van Pelt Date: Mon, 24 Jun 2024 07:46:33 +0900 Subject: [PATCH] Attempt to get i18n working --- frontend/package.json | 305 ++++++++++++++++--------------- frontend/pnpm-lock.yaml | 10 + frontend/src/components/Chat.tsx | 7 +- frontend/src/lib/i18n.ts | 35 ++++ frontend/src/main.tsx | 1 + 5 files changed, 203 insertions(+), 155 deletions(-) create mode 100644 frontend/src/lib/i18n.ts diff --git a/frontend/package.json b/frontend/package.json index a010805..2d06177 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,154 +1,155 @@ { - "name": "openui", - "license": "MIT", - "private": true, - "version": "1.0.0", - "type": "module", - "scripts": { - "build": "vite build", - "postbuild": "rm -rf ../backend/openui/dist && cp -r dist ../backend/openui/dist", - "deploy": "npm run build -- --mode hosted && fly deploy ../backend", - "commit": "cz", - "dev": "vite --open", - "prepare": "cd .. && husky install frontend/.husky", - "preview": "vite preview", - "preview:test": "start-server-and-test preview http://localhost:4173", - "test": "vitest", - "test:ci": "vitest run", - "test:e2e": "pnpm preview:test 'cypress open'", - "test:e2e:headless": "pnpm preview:test 'cypress run'", - "test:e2e:ci": "vite build && pnpm preview:test 'cypress run --record'", - "format": "prettier -uw --cache --ignore-path .gitignore .", - "run-tsc": "tsc", - "run-eslint": "eslint --cache --fix --ignore-path .gitignore --ext .ts,.tsx .", - "run-stylelint": "stylelint --cache --fix --ignore-path .gitignore **/*.css", - "lint": "run-p run-tsc run-eslint run-stylelint", - "validate": "run-p lint test:ci test:e2e:headless" - }, - "dependencies": { - "@monaco-editor/react": "^4.6.0", - "@radix-ui/react-avatar": "^1.0.4", - "@radix-ui/react-checkbox": "^1.0.4", - "@radix-ui/react-dialog": "^1.0.5", - "@radix-ui/react-dropdown-menu": "^2.0.6", - "@radix-ui/react-hover-card": "^1.0.7", - "@radix-ui/react-icons": "^1.3.0", - "@radix-ui/react-label": "^2.0.2", - "@radix-ui/react-popover": "^1.0.7", - "@radix-ui/react-select": "^2.0.0", - "@radix-ui/react-slider": "^1.1.2", - "@radix-ui/react-slot": "^1.0.2", - "@radix-ui/react-switch": "^1.0.3", - "@radix-ui/react-tooltip": "^1.0.7", - "@tailwindcss/typography": "^0.5.13", - "@tanstack/react-query": "^5.36.0", - "@types/mdast": "^4.0.3", - "@uiw/copy-to-clipboard": "^1.0.17", - "class-variance-authority": "^0.7.0", - "clsx": "^2.1.1", - "groq-sdk": "^0.3.3", - "i18next": "^23.11.5", - "jotai": "^2.8.0", - "jotai-devtools": "^0.10.0", - "jotai-minidb": "^0.0.8", - "js-cookie": "^3.0.5", - "litellm": "^0.12.0", - "lucide-react": "^0.378.0", - "monaco-editor": "^0.49.0", - "monaco-tailwindcss": "^0.6.0", - "nanoid": "^5.0.7", - "openai": "^4.45.0", - "prettier": "^3.2.5", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-i18next": "^14.1.2", - "react-markdown": "^9.0.1", - "react-router-dom": "^6.23.1", - "react-syntax-highlighter": "^15.5.0", - "remark": "^15.0.1", - "remark-frontmatter": "^5.0.0", - "remark-parse": "^11.0.0", - "tailwind-merge": "^2.3.0", - "tailwindcss-animate": "^1.0.7", - "unified": "^11.0.4", - "unsplash-js": "^7.0.19" - }, - "devDependencies": { - "@nabla/vite-plugin-eslint": "^2.0.4", - "@playwright/test": "^1.44.1", - "@tailwindcss/forms": "^0.5.7", - "@testing-library/dom": "^10.1.0", - "@testing-library/jest-dom": "^6.4.5", - "@testing-library/react": "^15.0.7", - "@testing-library/user-event": "^14.5.2", - "@types/css-mediaquery": "^0.1.4", - "@types/js-cookie": "^3.0.6", - "@types/node": "^20.14.7", - "@types/react": "^18.3.2", - "@types/react-dom": "^18.3.0", - "@types/react-router-dom": "^5.3.3", - "@types/react-syntax-highlighter": "^15.5.13", - "@typescript-eslint/eslint-plugin": "^7.8.0", - "@typescript-eslint/parser": "^7.8.0", - "@vitejs/plugin-react": "^4.2.1", - "@vitest/coverage-istanbul": "^1.6.0", - "autoprefixer": "^10.4.19", - "commitizen": "^4.3.0", - "css-mediaquery": "^0.1.2", - "cz-conventional-changelog": "^3.3.0", - "eslint": "^8.57.0", - "eslint-config-airbnb": "^19.0.4", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-config-airbnb-typescript": "^18.0.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsx-a11y": "^6.8.0", - "eslint-plugin-react": "^7.34.1", - "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-prefer-function-component": "^3.3.0", - "eslint-plugin-testing-library": "^6.2.2", - "eslint-plugin-unicorn": "^53.0.0", - "husky": "^9.0.11", - "jsdom": "^24.0.0", - "lint-staged": "^15.2.2", - "msw": "^2.3.0", - "npm-run-all": "^4.1.5", - "ollama": "^0.5.1", - "postcss": "^8.4.38", - "prettier-plugin-tailwindcss": "^0.5.14", - "start-server-and-test": "^2.0.3", - "stylelint": "^16.5.0", - "stylelint-config-standard": "^36.0.0", - "tailwindcss": "^3.4.3", - "typescript": "^5.4.5", - "vite": "^5.2.11", - "vite-plugin-mkcert": "^1.17.5", - "vite-plugin-monaco-editor": "^1.1.0", - "vite-plugin-pwa": "^0.20.0", - "vite-tsconfig-paths": "^4.3.2", - "vitest": "^1.6.0", - "whatwg-fetch": "^3.6.20", - "workbox-build": "^7.1.0", - "workbox-window": "^7.1.0" - }, - "browserslist": { - "production": "Edge >= 18, Firefox >= 60, Chrome >= 61, Safari >= 11, Opera >= 48", - "development": [ - "last 1 chrome version", - "last 1 firefox version" - ] - }, - "lint-staged": { - "*": "prettier -uw --cache", - "*.css": "stylelint --cache --fix", - "*.{ts,tsx}": [ - "eslint --cache --fix", - "vitest related --run --coverage=false" - ] - }, - "pnpm": { - "overrides": { - "headers-polyfill": "3.1.2" - } - } + "name": "openui", + "license": "MIT", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "build": "vite build", + "postbuild": "rm -rf ../backend/openui/dist && cp -r dist ../backend/openui/dist", + "deploy": "npm run build -- --mode hosted && fly deploy ../backend && git restore backend/openui/dist && git clean -f ../backend/openui/dist/assets/", + "commit": "cz", + "dev": "vite --open", + "prepare": "cd .. && husky install frontend/.husky", + "preview": "vite preview", + "preview:test": "start-server-and-test preview http://localhost:4173", + "test": "vitest", + "test:ci": "vitest run", + "test:e2e": "pnpm preview:test 'cypress open'", + "test:e2e:headless": "pnpm preview:test 'cypress run'", + "test:e2e:ci": "vite build && pnpm preview:test 'cypress run --record'", + "format": "prettier -uw --cache --ignore-path .gitignore .", + "run-tsc": "tsc", + "run-eslint": "eslint --cache --fix --ignore-path .gitignore --ext .ts,.tsx .", + "run-stylelint": "stylelint --cache --fix --ignore-path .gitignore **/*.css", + "lint": "run-p run-tsc run-eslint run-stylelint", + "validate": "run-p lint test:ci test:e2e:headless" + }, + "dependencies": { + "@monaco-editor/react": "^4.6.0", + "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-checkbox": "^1.0.4", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-hover-card": "^1.0.7", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-slider": "^1.1.2", + "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tooltip": "^1.0.7", + "@tailwindcss/typography": "^0.5.13", + "@tanstack/react-query": "^5.36.0", + "@types/mdast": "^4.0.3", + "@uiw/copy-to-clipboard": "^1.0.17", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "groq-sdk": "^0.3.3", + "i18next": "^23.11.5", + "i18next-browser-languagedetector": "^8.0.0", + "jotai": "^2.8.0", + "jotai-devtools": "^0.10.0", + "jotai-minidb": "^0.0.8", + "js-cookie": "^3.0.5", + "litellm": "^0.12.0", + "lucide-react": "^0.378.0", + "monaco-editor": "^0.49.0", + "monaco-tailwindcss": "^0.6.0", + "nanoid": "^5.0.7", + "openai": "^4.45.0", + "prettier": "^3.2.5", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-i18next": "^14.1.2", + "react-markdown": "^9.0.1", + "react-router-dom": "^6.23.1", + "react-syntax-highlighter": "^15.5.0", + "remark": "^15.0.1", + "remark-frontmatter": "^5.0.0", + "remark-parse": "^11.0.0", + "tailwind-merge": "^2.3.0", + "tailwindcss-animate": "^1.0.7", + "unified": "^11.0.4", + "unsplash-js": "^7.0.19" + }, + "devDependencies": { + "@nabla/vite-plugin-eslint": "^2.0.4", + "@playwright/test": "^1.44.1", + "@tailwindcss/forms": "^0.5.7", + "@testing-library/dom": "^10.1.0", + "@testing-library/jest-dom": "^6.4.5", + "@testing-library/react": "^15.0.7", + "@testing-library/user-event": "^14.5.2", + "@types/css-mediaquery": "^0.1.4", + "@types/js-cookie": "^3.0.6", + "@types/node": "^20.14.7", + "@types/react": "^18.3.2", + "@types/react-dom": "^18.3.0", + "@types/react-router-dom": "^5.3.3", + "@types/react-syntax-highlighter": "^15.5.13", + "@typescript-eslint/eslint-plugin": "^7.8.0", + "@typescript-eslint/parser": "^7.8.0", + "@vitejs/plugin-react": "^4.2.1", + "@vitest/coverage-istanbul": "^1.6.0", + "autoprefixer": "^10.4.19", + "commitizen": "^4.3.0", + "css-mediaquery": "^0.1.2", + "cz-conventional-changelog": "^3.3.0", + "eslint": "^8.57.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-config-airbnb-typescript": "^18.0.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jsx-a11y": "^6.8.0", + "eslint-plugin-react": "^7.34.1", + "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-prefer-function-component": "^3.3.0", + "eslint-plugin-testing-library": "^6.2.2", + "eslint-plugin-unicorn": "^53.0.0", + "husky": "^9.0.11", + "jsdom": "^24.0.0", + "lint-staged": "^15.2.2", + "msw": "^2.3.0", + "npm-run-all": "^4.1.5", + "ollama": "^0.5.1", + "postcss": "^8.4.38", + "prettier-plugin-tailwindcss": "^0.5.14", + "start-server-and-test": "^2.0.3", + "stylelint": "^16.5.0", + "stylelint-config-standard": "^36.0.0", + "tailwindcss": "^3.4.3", + "typescript": "^5.4.5", + "vite": "^5.2.11", + "vite-plugin-mkcert": "^1.17.5", + "vite-plugin-monaco-editor": "^1.1.0", + "vite-plugin-pwa": "^0.20.0", + "vite-tsconfig-paths": "^4.3.2", + "vitest": "^1.6.0", + "whatwg-fetch": "^3.6.20", + "workbox-build": "^7.1.0", + "workbox-window": "^7.1.0" + }, + "browserslist": { + "production": "Edge >= 18, Firefox >= 60, Chrome >= 61, Safari >= 11, Opera >= 48", + "development": [ + "last 1 chrome version", + "last 1 firefox version" + ] + }, + "lint-staged": { + "*": "prettier -uw --cache", + "*.css": "stylelint --cache --fix", + "*.{ts,tsx}": [ + "eslint --cache --fix", + "vitest related --run --coverage=false" + ] + }, + "pnpm": { + "overrides": { + "headers-polyfill": "3.1.2" + } + } } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 6e4e984..313c9d2 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -77,6 +77,9 @@ importers: i18next: specifier: ^23.11.5 version: 23.11.5 + i18next-browser-languagedetector: + specifier: ^8.0.0 + version: 8.0.0 jotai: specifier: ^2.8.0 version: 2.8.0(@types/react@18.3.2)(react@18.3.1) @@ -3513,6 +3516,9 @@ packages: engines: {node: '>=18'} hasBin: true + i18next-browser-languagedetector@8.0.0: + resolution: {integrity: sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==} + i18next@23.11.5: resolution: {integrity: sha512-41pvpVbW9rhZPk5xjCX2TPJi2861LEig/YRhUkY+1FQ2IQPS0bKUDYnEqY8XPPbB48h1uIwLnP9iiEfuSl20CA==} @@ -9683,6 +9689,10 @@ snapshots: husky@9.0.11: {} + i18next-browser-languagedetector@8.0.0: + dependencies: + '@babel/runtime': 7.24.5 + i18next@23.11.5: dependencies: '@babel/runtime': 7.24.5 diff --git a/frontend/src/components/Chat.tsx b/frontend/src/components/Chat.tsx index bbf4d2a..914a1d0 100644 --- a/frontend/src/components/Chat.tsx +++ b/frontend/src/components/Chat.tsx @@ -20,6 +20,7 @@ import { } from 'lib/utils' import { CircleUser, ImageIcon } from 'lucide-react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' import { ItemWrapper, @@ -56,7 +57,7 @@ export default function Chat({ isShared = false }: { isShared: boolean }) { [rawItem, setRawItem] ) - // const { t } = useTranslation() + const { t } = useTranslation() const [uiState, setUiState] = useAtom(uiStateAtom) const uiTheme = useAtomValue(uiThemeAtom) const theme = themes.find(th => th.name === uiTheme) @@ -207,11 +208,11 @@ export default function Chat({ isShared = false }: { isShared: boolean }) { /> ) : undefined}

- Describe the UI you'd like to generate. + {t('Chat Header')}

{modelSupportsImages ? (

- Pro Tip: You can drag or paste a reference screenshot. + {t('Pro Tip')}

) : undefined} diff --git a/frontend/src/lib/i18n.ts b/frontend/src/lib/i18n.ts new file mode 100644 index 0000000..91caebe --- /dev/null +++ b/frontend/src/lib/i18n.ts @@ -0,0 +1,35 @@ +import i18n from 'i18next' +import LanguageDetector from 'i18next-browser-languagedetector' +import { initReactI18next } from 'react-i18next' + +await i18n + .use(LanguageDetector) + .use(initReactI18next) + .init({ + resources: { + en: { + translation: { + 'Chat Header': "Describe the UI you'd like to generate.", + 'Pro Tip': 'Pro Tip: You can drag or paste a reference screenshot.' + } + }, + jp: { + translation: { + 'Chat Header': '生成したい UI について説明します。', + 'Pro Tip': + 'プロのヒント: 参照スクリーンショットをドラッグまたは貼り付けることができます。' + } + }, + kr: { + translation: { + 'Chat Header': '생성하고 싶은 UI에 대해 설명해주세요.', + 'Pro Tip': + '프로 팁: 참조 스크린샷을 드래그 또는 붙여넣기할 수 있습니다.' + } + } + }, + fallbackLng: 'en', + interpolation: { + escapeValue: false + } + }) diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 3b7d0e6..7d7f98f 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,5 +1,6 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import App from 'App' +import 'lib/i18n' import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './index.css'