├── .env.example ├── .eslintrc ├── .npmrc ├── .gitignore ├── .github └── renovate.json ├── layouts └── default.vue ├── tsconfig.json ├── .vscode ├── extensions.json └── settings.json ├── components ├── AppTitle.vue ├── Content.vue ├── Input.vue └── SelectLanguage.vue ├── pages ├── index.vue └── [...all].vue ├── html.d.ts ├── composables ├── useTranslate.ts └── language.ts ├── app.vue ├── nuxt.config.ts ├── public └── logo.svg ├── server └── api │ └── translate.post.ts ├── README.md ├── package.json ├── unocss.config.ts └── LICENSE /.env.example: -------------------------------------------------------------------------------- 1 | TRANSLATOR_API_KEY= -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@antfu" 3 | } 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | dist 4 | .output 5 | .nuxt 6 | .env 7 | .DS_Store -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "github>alexzhang-dev/renovate-config" 4 | ] 5 | } -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.nuxt/tsconfig.json", 3 | "compilerOptions": { 4 | "strict": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "antfu.iconify", 4 | "antfu.unocss", 5 | "antfu.goto-alias", 6 | "csstools.postcss", 7 | "dbaeumer.vscode-eslint", 8 | "vue.volar" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.enable": false, 3 | "editor.codeActionsOnSave": { 4 | "source.fixAll.eslint": true 5 | }, 6 | "files.associations": { 7 | "*.css": "postcss" 8 | }, 9 | "editor.formatOnSave": false 10 | } 11 | -------------------------------------------------------------------------------- /components/AppTitle.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Translator 5 | 6 | 7 | powered by GPT-3 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Loading... 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /html.d.ts: -------------------------------------------------------------------------------- 1 | // for UnoCSS attributify mode compact in Volar 2 | // refer: https://github.com/johnsoncodehk/volar/issues/1077#issuecomment-1145361472 3 | declare module '@vue/runtime-dom' { 4 | interface HTMLAttributes { 5 | [key: string]: any 6 | } 7 | } 8 | declare module '@vue/runtime-core' { 9 | interface AllowedComponentProps { 10 | [key: string]: any 11 | } 12 | } 13 | export {} 14 | -------------------------------------------------------------------------------- /pages/[...all].vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Not found 11 | 12 | 13 | Back 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /composables/useTranslate.ts: -------------------------------------------------------------------------------- 1 | import { languagesNameList } from './language' 2 | 3 | export const useContent = () => useState('content', () => '') 4 | export const useTranslated = () => useState('translate', () => ({ 5 | message: '', 6 | loading: false, 7 | })) 8 | export const useTargetLang = () => useState('targetLang', () => languagesNameList[0]) 9 | 10 | export interface TranslateRequest { 11 | word: string 12 | target: string 13 | } 14 | -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 30 | -------------------------------------------------------------------------------- /composables/language.ts: -------------------------------------------------------------------------------- 1 | import ISO6391 from 'iso-639-1' 2 | 3 | export interface LanguageNameItem { 4 | code: string 5 | nativeName: string 6 | name: string 7 | } 8 | 9 | export const languagesNameList: LanguageNameItem[] = ISO6391.getAllCodes().map(code => ({ 10 | code, 11 | nativeName: ISO6391.getNativeName(code), 12 | name: ISO6391.getName(code), 13 | })) 14 | 15 | export const inputLanguages = [{ 16 | code: 'auto', 17 | nativeName: 'Auto Detect', 18 | name: 'Auto Detect', 19 | }] 20 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | export default defineNuxtConfig({ 2 | modules: [ 3 | '@vueuse/nuxt', 4 | '@unocss/nuxt', 5 | '@pinia/nuxt', 6 | '@nuxtjs/color-mode', 7 | '@nuxt/devtools', 8 | '@vue-macros/nuxt', 9 | ], 10 | experimental: { 11 | reactivityTransform: true, 12 | inlineSSRStyles: false, 13 | }, 14 | css: [ 15 | '@unocss/reset/tailwind.css', 16 | ], 17 | colorMode: { 18 | classSuffix: '', 19 | }, 20 | devServer: { 21 | port: 5173, 22 | }, 23 | }) 24 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/api/translate.post.ts: -------------------------------------------------------------------------------- 1 | import { ChatGPTAPI } from 'chatgpt' 2 | import type { TranslateRequest } from '~/composables/useTranslate' 3 | 4 | const api = new ChatGPTAPI({ 5 | apiKey: process.env.TRANSLATOR_API_KEY!, 6 | }) 7 | 8 | const getRequest = (m: string, target: string) => `Please translate "${m}" to ${target}.` 9 | 10 | export default defineEventHandler(async (event) => { 11 | const { word, target } = await readBody(event) 12 | const message = await api.sendMessage(getRequest(word, target)) 13 | return message 14 | }) 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Translator 2 | 3 | It's an online translator powered by OpenAI GPT-3. 4 | 5 | Highly recommend to use it by locally. 6 | 7 | The online demo uses the free API key, may be slow or even broken. 8 | 9 | ## Setup 10 | 11 | ```bash 12 | # Install dependencies 13 | pnpm i 14 | 15 | # Build 16 | pnpm build 17 | 18 | # Start server 19 | pnpm start 20 | ``` 21 | 22 | > Notice you need provide an openai api key in your environment variables before execute `pnpm start`, and the key is `TRANSLATOR_API_KEY`. 23 | 24 | Then access `http://localhost:3000` in your browser. 25 | -------------------------------------------------------------------------------- /components/Content.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | {{ translated.message ?? 'Translated' }} 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "packageManager": "pnpm@8.10.3", 4 | "scripts": { 5 | "build": "nuxi build", 6 | "dev": "nuxi dev --open", 7 | "start": "node .output/server/index.mjs", 8 | "lint": "eslint .", 9 | "postinstall": "nuxi prepare", 10 | "generate": "nuxi generate" 11 | }, 12 | "devDependencies": { 13 | "@antfu/eslint-config": "^0.43.1", 14 | "@iconify-json/carbon": "^1.1.21", 15 | "@iconify-json/twemoji": "^1.1.12", 16 | "@nuxt/devtools": "^1.0.2", 17 | "@nuxtjs/color-mode": "^3.3.0", 18 | "@pinia/nuxt": "^0.5.1", 19 | "@types/node": "^20.9.0", 20 | "@unocss/nuxt": "^0.57.3", 21 | "@vue-macros/nuxt": "^1.9.5", 22 | "@vueuse/nuxt": "^10.6.0", 23 | "eslint": "^8.53.0", 24 | "nuxt": "^3.8.1", 25 | "pinia": "^2.1.7", 26 | "taze": "^0.12.0", 27 | "typescript": "^5.2.2" 28 | }, 29 | "dependencies": { 30 | "@headlessui/vue": "^1.7.16", 31 | "@heroicons/vue": "^2.0.18", 32 | "chatgpt": "^5.2.5", 33 | "iso-639-1": "^3.1.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /unocss.config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | defineConfig, 3 | presetAttributify, 4 | presetIcons, 5 | presetTypography, 6 | presetUno, 7 | presetWebFonts, 8 | transformerDirectives, 9 | transformerVariantGroup, 10 | } from 'unocss' 11 | 12 | export default defineConfig({ 13 | shortcuts: [ 14 | ['btn', 'px-4 py-1 rounded inline-block bg-gray-600 text-white cursor-pointer hover:bg-gray-700 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'], 15 | ['icon-btn', 'inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-teal-600'], 16 | ], 17 | presets: [ 18 | presetUno(), 19 | presetAttributify(), 20 | presetIcons({ 21 | scale: 1.2, 22 | }), 23 | presetTypography(), 24 | presetWebFonts({ 25 | fonts: { 26 | sans: 'DM Sans', 27 | serif: 'DM Serif Display', 28 | mono: 'DM Mono', 29 | }, 30 | }), 31 | ], 32 | transformers: [ 33 | transformerDirectives(), 34 | transformerVariantGroup(), 35 | ], 36 | }) 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Alex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /components/Input.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /components/SelectLanguage.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | {{ modelValue.name }} {{ showLabel ? `(${modelValue.nativeName})` : '' }} 33 | 34 | 37 | 41 | 42 | 43 | 44 | 49 | 52 | 59 | 65 | 70 | {{ languageName.name }} {{ showLabel ? `(${languageName.nativeName})` : '' }} 71 | 72 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | --------------------------------------------------------------------------------