diff --git a/packages/app/.gitignore b/packages/app/.gitignore index d16c893..a1cb8fa 100644 --- a/packages/app/.gitignore +++ b/packages/app/.gitignore @@ -27,3 +27,7 @@ gitignore # System Files .DS_Store Thumbs.db + +# i18n +src/locale/locales/_build/ +src/locale/locales/**/*.mjs diff --git a/packages/app/app.config.ts b/packages/app/app.config.ts index 5e287e0..b6a5e8a 100644 --- a/packages/app/app.config.ts +++ b/packages/app/app.config.ts @@ -16,11 +16,13 @@ import remarkGfm from 'remark-gfm'; import {rehypeBlockquote} from './rehype-custom/rehypeCustomBlockquote'; import {statebuilder} from 'statebuilder/compiler'; +import {lingui} from '@lingui/vite-plugin'; const defaultConfig: ViteCustomizableConfig = { plugins: [ viteTsConfigPaths(), customMdxConfig(), + lingui(), statebuilder({ transformStores: ['eDefineAsync'], autoKey: true, @@ -46,6 +48,11 @@ const defaultConfig: ViteCustomizableConfig = { export default defineConfig({ middleware: './src/middleware.ts', + solid: { + babel: { + plugins: ['macros'], + }, + }, server: { prerender: { routes: ['/about', '/about/supported-workflow-features'], diff --git a/packages/app/lingui.config.ts b/packages/app/lingui.config.ts new file mode 100644 index 0000000..93658f6 --- /dev/null +++ b/packages/app/lingui.config.ts @@ -0,0 +1,15 @@ +import type {LinguiConfig} from '@lingui/conf'; + +const config: LinguiConfig = { + locales: ['en'], + compileNamespace: 'es', + catalogs: [ + { + path: '/src/locales/{locale}', + include: ['src'], + }, + ], + format: 'po', +}; + +export default config; diff --git a/packages/app/package.json b/packages/app/package.json index fc48d5b..66fdcc7 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -11,6 +11,9 @@ "dev": "vinxi dev", "build": "vinxi build", "start": "vinxi start", + "intl:build": "pnpm intl:extract && pnpm intl:compile", + "intl:extract": "lingui extract", + "intl:compile": "lingui compile", "version": "vinxi version" }, "dependencies": { @@ -29,6 +32,8 @@ "@kobalte/core": "^0.13.7", "@kobalte/utils": "^0.9.1", "@lezer/highlight": "^1.2.1", + "@lingui/conf": "^4.14.0", + "@lingui/core": "^4.14.0", "@mdx-js/mdx": "^3.1.0", "@open-rpc/client-js": "^1.8.1", "@pipelineui/workflow-languageserver": "workspace:*", @@ -69,7 +74,12 @@ "node": ">=18" }, "devDependencies": { + "@babel/core": "^7.26.0", + "@lingui/cli": "^4.14.0", + "@lingui/macro": "^4.14.0", + "@lingui/vite-plugin": "^4.14.0", "@types/ws": "^8.5.12", + "babel-plugin-macros": "^3.1.0", "rehype-autolink-headings": "^7.1.0", "rehype-raw": "^7.0.0", "rehype-slug": "^6.0.0", diff --git a/packages/app/src/app.tsx b/packages/app/src/app.tsx index d472527..f4608dc 100644 --- a/packages/app/src/app.tsx +++ b/packages/app/src/app.tsx @@ -1,24 +1,33 @@ +import {setupI18n} from '@lingui/core'; import {MetaProvider, Title} from '@solidjs/meta'; import {Router} from '@solidjs/router'; import {FileRoutes} from '@solidjs/start/router'; import {Suspense} from 'solid-js'; -import './global-codeui.css'; -import './app.css'; import {StateProvider} from 'statebuilder'; +import './app.css'; +import './global-codeui.css'; +import {I18nProvider} from './locales/i18n'; + +const {messages: enMessages} = await import('./locales/en.po'); export default function App() { + const i18n = setupI18n(); + i18n.loadAndActivate({locale: 'en', messages: enMessages}); + return ( - ( - - PipelineUI - - {props.children} - - - )} - > - - + + ( + + PipelineUI + + {props.children} + + + )} + > + + + ); } diff --git a/packages/app/src/components/Editor/Header/CurrentUser/CurrentUser.tsx b/packages/app/src/components/Editor/Header/CurrentUser/CurrentUser.tsx index 830d1f3..a571ae7 100644 --- a/packages/app/src/components/Editor/Header/CurrentUser/CurrentUser.tsx +++ b/packages/app/src/components/Editor/Header/CurrentUser/CurrentUser.tsx @@ -11,11 +11,15 @@ import type {Models} from 'appwrite'; import {Show} from 'solid-js'; import {logout, signupWithGithub} from '~/lib/session'; import {badge, currentUser} from './CurrentUser.css'; +import {useI18n} from '~/locales/i18n'; +import {msg} from '@lingui/macro'; export interface CurrentUserBarProps { user: Models.User | null; } export function EditorHeaderCurrentUser(props: CurrentUserBarProps) { + const {_} = useI18n(); + const initials = (user: Models.User) => { if (user.name) { const [firstName, lastName] = user.name.split(' '); @@ -43,7 +47,7 @@ export function EditorHeaderCurrentUser(props: CurrentUserBarProps) { type={'submit'} loading={isSignup.pending} > - Signup with Github + {_(msg`Signup with GitHub`)} } @@ -74,7 +78,7 @@ export function EditorHeaderCurrentUser(props: CurrentUserBarProps) { logoutAction().then(() => window.location.reload()); }} > - Logout + {_(msg`Logout`)} diff --git a/packages/app/src/components/Editor/Header/Header.tsx b/packages/app/src/components/Editor/Header/Header.tsx index 5e18ffe..7d0d34d 100644 --- a/packages/app/src/components/Editor/Header/Header.tsx +++ b/packages/app/src/components/Editor/Header/Header.tsx @@ -16,6 +16,8 @@ import {EditorContext} from '../editor.context'; import {EditorUiStore} from '../../../store/editor/ui.store'; import * as styles from './EditorHeader.css'; import {EditorHeaderCurrentUser} from './CurrentUser/CurrentUser'; +import {useI18n} from '~/locales/i18n'; +import {msg} from '@lingui/macro'; export interface EditorHeaderProps { showBack: boolean; @@ -42,6 +44,7 @@ function EditorHeaderActionButton( } function EditorHeaderForkButton() { + const {_} = useI18n(); const [isOpen, setOpen] = createSignal(false); const editorStore = provideState(EditorStore); const editorContext = useContext(EditorContext)!; @@ -51,13 +54,14 @@ function EditorHeaderForkButton() { ( )} /> - Forking this source will create a new scratch remotely connected to your - profile, that can be modified only by you. + {_( + msg`Forking this source will create a new scratch remotely connected to your profile, that can be modified only by you`, + )}
@@ -81,6 +85,7 @@ function EditorHeaderForkButton() { } export function EditorHeader(props: EditorHeaderProps) { + const {_} = useI18n(); const editorUi = provideState(EditorUiStore); const editorStore = provideState(EditorStore); const user = provideState(UserStore); @@ -92,7 +97,7 @@ export function EditorHeader(props: EditorHeaderProps) {
- Save + {_(msg`Save`)} @@ -147,13 +152,13 @@ export function EditorHeader(props: EditorHeaderProps) { active={editorUi.get.leftPanel === 'code'} onClick={() => editorUi.actions.toggleLeftPanel('code')} > - Code + {_(msg`Code`)} editorUi.actions.toggleLeftPanel('merge')} > - Merge view + {_(msg`Merge view`)}
@@ -161,7 +166,7 @@ export function EditorHeader(props: EditorHeaderProps) { active={editorUi.get.rightPanel === 'properties'} onClick={() => editorUi.actions.toggleRightPanel('properties')} > - Properties + {_(msg`Properties`)}
diff --git a/packages/app/src/components/Editor/RightSidebar/RightSidebar.tsx b/packages/app/src/components/Editor/RightSidebar/RightSidebar.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/packages/app/src/components/Editor/StatusBar/StatusBar.tsx b/packages/app/src/components/Editor/StatusBar/StatusBar.tsx index 3003085..56909e3 100644 --- a/packages/app/src/components/Editor/StatusBar/StatusBar.tsx +++ b/packages/app/src/components/Editor/StatusBar/StatusBar.tsx @@ -1,3 +1,11 @@ +import {Icon} from '#ui/components/Icon'; +import {Button, IconButton, Tooltip} from '@codeui/kit'; +import {msg} from '@lingui/macro'; +import {provideState} from 'statebuilder'; +import {useI18n} from '~/locales/i18n'; +import {EditorStore} from '~/store/editor/editor.store'; +import {EditorUiStore} from '~/store/editor/ui.store'; +import {CanvasStore} from '../../../store/editor/canvas.store'; import { diagnosticCounter, diagnosticCounterBar, @@ -6,18 +14,12 @@ import { statusBar, statusBarAction, } from './EditorStatusBar.css'; -import {provideState} from 'statebuilder'; -import {CanvasStore} from '../../../store/editor/canvas.store'; -import {Button, IconButton, Tooltip} from '@codeui/kit'; -import {Icon} from '#ui/components/Icon'; -import {EditorStore} from '~/store/editor/editor.store'; -import {EditorUiStore} from '~/store/editor/ui.store'; -import {createEffect} from 'solid-js'; export function EditorStatusBar() { const canvasState = provideState(CanvasStore); const editorUiStore = provideState(EditorUiStore); const editorStore = provideState(EditorStore); + const {_} = useI18n(); const size = () => Math.floor(canvasState.get.scale * 100); @@ -37,7 +39,7 @@ export function EditorStatusBar() {
} @@ -76,7 +79,7 @@ export function CurrentUserBar(props: CurrentUserBarProps) { logoutAction().then(() => window.location.reload()); }} > - Logout + {_(msg`Logout`)} diff --git a/packages/app/src/components/Home/Footer/Footer.tsx b/packages/app/src/components/Home/Footer/Footer.tsx index 415a76d..34be2a0 100644 --- a/packages/app/src/components/Home/Footer/Footer.tsx +++ b/packages/app/src/components/Home/Footer/Footer.tsx @@ -1,13 +1,16 @@ +import {msg} from '@lingui/macro'; import {A} from '@solidjs/router'; -import {footer, footerContent, footerLinks, footerLink} from './Footer.css'; +import {useI18n} from '~/locales/i18n'; +import {footer, footerContent, footerLink, footerLinks} from './Footer.css'; export function HomeFooter() { + const {_} = useI18n(); return (