diff --git a/.gitignore b/.gitignore
index a672b11f..9b2b961e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ node_modules
/formatters
/importer
/parser
+/preact
/react
/runtime
/schema
@@ -20,4 +21,4 @@ packages/generator/test/generated/console*
/typesafe-i18n-*.tgz
/types
/.eslintcache
-.pnpm-debug.log
\ No newline at end of file
+.pnpm-debug.log
diff --git a/README.md b/README.md
index f50d54da..53723573 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,9 @@ Click [here](https://codesandbox.io/s/typesafe-i18n-demo-qntgqy?file=/index.ts)
+
+
+
@@ -162,6 +165,7 @@ You can use `typesafe-i18n` in a variety of project-setups:
- [Angular](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-angular) applications
- [Node.js](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-node) apis, backends, scripts, ...
- [React / Next.js](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-react) applications
+ - [Preact](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-preact) applications
- [Solid.js](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-solid) applications
- [Svelte / SvelteKit / Sapper](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-svelte) applications
- [Vue.js / Nuxt.js](https://github.com/ivanhofer/typesafe-i18n/tree/main/packages/adapter-vue) applications
diff --git a/assets/icons/preact.svg b/assets/icons/preact.svg
new file mode 100644
index 00000000..fe53254a
--- /dev/null
+++ b/assets/icons/preact.svg
@@ -0,0 +1,9 @@
+
diff --git a/package.json b/package.json
index 7561e5f6..1f14e113 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
"svelte",
"sveltekit",
"react",
+ "preact",
"react-native",
"nextjs",
"expo",
@@ -71,6 +72,11 @@
"import": "./parser/index.mjs",
"require": "./parser/index.cjs"
},
+ "./preact": {
+ "types": "./preact/index.d.ts",
+ "import": "./preact/index.mjs",
+ "require": "./preact/index.cjs"
+ },
"./react": {
"types": "./react/index.d.ts",
"import": "./react/index.mjs",
@@ -113,6 +119,7 @@
"/formatters",
"/importer",
"/parser",
+ "/preact",
"/react",
"/runtime",
"/schema",
@@ -126,7 +133,7 @@
"-- DEV -------------------------------------------------": "",
"update:dependencies": "pnpm up -r",
"update:version": "tsx ./update-version.ts",
- "clear": "rimraf ./angular ./cli ./config ./detectors ./dist ./exporter ./formatters ./importer ./parser ./react ./runtime ./schema ./solid ./svelte ./types ./utils ./vue ./temp-output",
+ "clear": "rimraf ./angular ./cli ./config ./detectors ./dist ./exporter ./formatters ./importer ./parser ./preact ./react ./runtime ./schema ./solid ./svelte ./types ./utils ./vue ./temp-output",
"-- LINT ------------------------------------------------": "",
"lint": "eslint --cache packages",
"lint:ci": "eslint --cache --fix",
diff --git a/packages/adapter-preact/README.md b/packages/adapter-preact/README.md
new file mode 100644
index 00000000..e0deca8f
--- /dev/null
+++ b/packages/adapter-preact/README.md
@@ -0,0 +1,34 @@
+# `typesafe-i18n` Preact
+
+This package provides a lightweight Preact context wrapper around `typesafe-i18n`.
+
+## Setup
+
+See [here](https://github.com/ivanhofer/typesafe-i18n#get-started) for details on the generator.
+
+Run the setup once to scaffold the boilerplate:
+
+```bash
+npx typesafe-i18n --setup-auto
+```
+
+The generator will create `i18n-preact.tsx` (name configurable via `adapterFileName`). Wrap your app with the generated component and consume the context with the generated hook:
+
+```tsx
+import { render } from 'preact'
+import TypesafeI18n, { useI18nContext } from './i18n/i18n-preact'
+
+const App = () => {
+ const { LL } = useI18nContext()
+ return
{LL.HI({ name: 'Mauricio' })}
+}
+
+render(
+
+
+ ,
+ document.getElementById('app')!,
+)
+```
+
+See the main README for more advanced usage and loading locales.
diff --git a/packages/adapter-preact/esbuild.ts b/packages/adapter-preact/esbuild.ts
new file mode 100644
index 00000000..d1055411
--- /dev/null
+++ b/packages/adapter-preact/esbuild.ts
@@ -0,0 +1,47 @@
+/* eslint-disable no-console */
+import { context } from 'esbuild'
+import { dirname, resolve } from 'path'
+import { fileURLToPath } from 'url'
+
+const watch = process.argv.includes('--watch')
+
+//@ts-ignore
+const __filename = fileURLToPath(import.meta.url)
+const __dirname = dirname(__filename)
+
+const getPath = (file: string) => resolve(__dirname, file)
+
+const formats = ['esm', 'cjs'] as const
+
+const contexts = await Promise.all(
+ formats.flatMap((format) =>
+ [false, true].map((minify) =>
+ context({
+ entryPoints: ['./src/index.ts'],
+ bundle: true,
+ outfile: getPath(`../../preact/index${minify ? '.min' : ''}.${format === 'esm' ? 'm' : 'c'}js`),
+ external: ['preact', 'preact/hooks'],
+ platform: 'browser',
+ format,
+ sourcemap: watch,
+ minify,
+ tsconfig: './tsconfig.json',
+ }),
+ ),
+ ),
+)
+
+for (const ctx of contexts) {
+ if (watch) {
+ await ctx.watch()
+ console.info('👀 watching for changes...')
+ process.on('exit', async () => {
+ console.info('🙈 process killed')
+ await ctx.dispose()
+ })
+ } else {
+ await ctx.rebuild()
+ console.info('✅ build complete')
+ await ctx.dispose()
+ }
+}
diff --git a/packages/adapter-preact/package.json b/packages/adapter-preact/package.json
new file mode 100644
index 00000000..93ebf549
--- /dev/null
+++ b/packages/adapter-preact/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "@typesafe-i18n/adapter-preact",
+ "scripts": {
+ "dev": "tsx esbuild.ts --watch",
+ "build": "tsx esbuild.ts && tsc -p tsconfig.json --emitDeclarationOnly",
+ "test": "tsc --noEmit"
+ },
+ "devDependencies": {
+ "esbuild": "^0.18.18",
+ "preact": "^10.17.1",
+ "tsx": "^3.12.7",
+ "typescript": "^5.1.6"
+ },
+ "type": "module"
+}
diff --git a/packages/adapter-preact/src/index.ts b/packages/adapter-preact/src/index.ts
new file mode 100644
index 00000000..7eb35afc
--- /dev/null
+++ b/packages/adapter-preact/src/index.ts
@@ -0,0 +1,74 @@
+import { createContext, h, type ComponentChildren, type Context, type FunctionComponent } from 'preact'
+import { useCallback, useMemo, useState } from 'preact/hooks'
+import { getFallbackProxy } from '../../runtime/src/core-utils.mjs'
+import type { BaseFormatters, BaseTranslation, Locale, TranslationFunctions } from '../../runtime/src/core.mjs'
+import { i18nObject } from '../../runtime/src/util.object.mjs'
+
+// --------------------------------------------------------------------------------------------------------------------
+// types --------------------------------------------------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------------------------------
+
+export type I18nContextType<
+ L extends Locale = Locale,
+ T extends BaseTranslation | BaseTranslation[] = BaseTranslation,
+ TF extends TranslationFunctions = TranslationFunctions,
+> = {
+ locale: L
+ LL: TF
+ setLocale: (locale: L) => void
+}
+
+export type TypesafeI18nProps = {
+ locale: L
+ children: ComponentChildren
+}
+
+export type PreactInit<
+ L extends Locale = Locale,
+ T extends BaseTranslation | BaseTranslation[] = BaseTranslation,
+ TF extends TranslationFunctions = TranslationFunctions,
+> = {
+ component: FunctionComponent>
+ context: Context>
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+// implementation -----------------------------------------------------------------------------------------------------
+// --------------------------------------------------------------------------------------------------------------------
+
+export const initI18nPreact = <
+ L extends Locale = Locale,
+ T extends BaseTranslation = BaseTranslation,
+ TF extends TranslationFunctions = TranslationFunctions,
+ F extends BaseFormatters = BaseFormatters,
+>(
+ translations: Record,
+ formatters: Record = {} as Record,
+): PreactInit => {
+ const context = createContext({} as I18nContextType)
+
+ const component: FunctionComponent> = (props) => {
+ const [locale, _setLocale] = useState(props.locale)
+ const [LL, setLL] = useState(() =>
+ !locale ? getFallbackProxy() : i18nObject(locale, translations[locale], formatters[locale]),
+ )
+
+ const setLocale = useCallback((newLocale: L) => {
+ _setLocale(newLocale)
+ setLL(() => i18nObject(newLocale, translations[newLocale], formatters[newLocale]))
+ }, [])
+
+ const ctx = useMemo>(
+ () => ({
+ setLocale,
+ locale,
+ LL,
+ }),
+ [setLocale, locale, LL],
+ )
+
+ return h(context.Provider, { value: ctx, children: props.children })
+ }
+
+ return { component, context }
+}
diff --git a/packages/adapter-preact/tsconfig.json b/packages/adapter-preact/tsconfig.json
new file mode 100644
index 00000000..022b9f65
--- /dev/null
+++ b/packages/adapter-preact/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../preact"
+ },
+ "include": ["src/**/*.ts"]
+}
diff --git a/packages/cli/src/setup/detect-setup.mts b/packages/cli/src/setup/detect-setup.mts
index 2d560769..5b9f1248 100644
--- a/packages/cli/src/setup/detect-setup.mts
+++ b/packages/cli/src/setup/detect-setup.mts
@@ -11,6 +11,7 @@ const useAdapterWhenDependenciesContain =
shouldContain.reduce((prev, dep) => prev || dependencies.includes(dep), false as boolean)
const shouldUseAngularAdapter = useAdapterWhenDependenciesContain(['@angular/core'])
+const shouldUsePreactAdapter = useAdapterWhenDependenciesContain(['preact'])
const shouldUseReactAdapter = useAdapterWhenDependenciesContain(['react', 'next'])
const shouldUseSolidAdapter = useAdapterWhenDependenciesContain(['solid-js'])
const shouldUseSvelteAdapter = useAdapterWhenDependenciesContain(['svelte', '@sveltejs/kit', 'sapper'])
@@ -21,6 +22,7 @@ const getAdaptersInfo = (type: RuntimeObject['type'], deps: string[]): Adapters[
const adapters: Adapters[] = []
if (shouldUseAngularAdapter(deps)) adapters.push('angular')
+ if (shouldUsePreactAdapter(deps)) adapters.push('preact')
if (shouldUseReactAdapter(deps)) adapters.push('react')
if (shouldUseSolidAdapter(deps)) adapters.push('solid')
if (shouldUseSvelteAdapter(deps)) adapters.push('svelte')
@@ -47,9 +49,12 @@ export const getDefaultConfig = async () => {
const esmImports = (await runtime.getEsmImportOption()) && !adapters.includes('svelte')
const defaultConfig = await getConfigWithDefaultValues()
+ const adapterConfig =
+ adapters.length === 1 ? ({ adapter: adapters[0] } as GeneratorConfig) : adapters.length > 1 ? ({ adapters } as GeneratorConfig) : {}
+
const config: GeneratorConfig = {
baseLocale: defaultConfig.baseLocale,
- ...(adapters ? (adapters.length === 1 ? { adapter: adapters[0] as Adapters } : { adapters }) : {}),
+ ...adapterConfig,
esmImports,
outputFormat: isTypeScriptProject ? 'TypeScript' : 'JavaScript',
outputPath: defaultConfig.outputPath,
diff --git a/packages/cli/src/setup/questions.mts b/packages/cli/src/setup/questions.mts
index 1721f1e3..7bef9cd9 100644
--- a/packages/cli/src/setup/questions.mts
+++ b/packages/cli/src/setup/questions.mts
@@ -18,6 +18,7 @@ export const askConfigQuestions = ({ baseLocale, adapter, esmImports, outputForm
{ title: 'Angular', value: 'angular', description: 'this is an Angular application' },
{ title: 'Node.js', value: 'node', description: 'for Backends, APIs' },
{ title: 'React', value: 'react', description: 'this is a React/Next.js application' },
+ { title: 'Preact', value: 'preact', description: 'this is a Preact application' },
{ title: 'Solid', value: 'solid', description: 'this is a SolidJS application' },
{ title: 'Svelte', value: 'svelte', description: 'this is a Svelte/SvelteKit/Sapper application' },
{ title: 'Vue.js', value: 'vue', description: 'this is a Vue.js application' },
@@ -30,10 +31,14 @@ export const askConfigQuestions = ({ baseLocale, adapter, esmImports, outputForm
return 2
case 'react':
return 3
- case 'svelte':
+ case 'preact':
return 4
- case 'vue':
+ case 'solid':
return 5
+ case 'svelte':
+ return 6
+ case 'vue':
+ return 7
default:
return 0
}
diff --git a/packages/config/src/types.mts b/packages/config/src/types.mts
index 08d6793c..2e6867bf 100644
--- a/packages/config/src/types.mts
+++ b/packages/config/src/types.mts
@@ -1,6 +1,6 @@
import type { Locale } from '../../runtime/src/core.mjs'
-export type Adapters = 'angular' | 'deno' | 'node' | 'react' | 'solid' | 'svelte' | 'vue'
+export type Adapters = 'angular' | 'deno' | 'node' | 'preact' | 'react' | 'solid' | 'svelte' | 'vue'
export type OutputFormats = 'TypeScript' | 'JavaScript'
diff --git a/packages/fix-imports.ts b/packages/fix-imports.ts
index 7d7c1077..4af79c48 100644
--- a/packages/fix-imports.ts
+++ b/packages/fix-imports.ts
@@ -15,6 +15,7 @@ const folders = [
'runtime/esm/runtime/src',
'formatters',
'parser',
+ 'preact',
'react',
'solid',
'svelte',
diff --git a/packages/generator/README.md b/packages/generator/README.md
index 097b1eaf..f513824e 100644
--- a/packages/generator/README.md
+++ b/packages/generator/README.md
@@ -207,8 +207,8 @@ The available options are:
| key | type | default value |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------- |
-| [adapter](#adapter) | `'angular' \| 'node' \| 'react' \| 'solid' \| 'svelte' \| 'vue' \| undefined` | `undefined` |
-| [adapters](#adapters) | `Array<'angular' \| 'node' \| 'react' \| 'solid' \| 'svelte' \| 'vue'> \| undefined` | `undefined`
+| [adapter](#adapter) | `'angular' \| 'node' \| 'preact' \| 'react' \| 'solid' \| 'svelte' \| 'vue' \| undefined` | `undefined` |
+| [adapters](#adapters) | `Array<'angular' \| 'node' \| 'preact' \| 'react' \| 'solid' \| 'svelte' \| 'vue'> \| undefined` | `undefined`
| [baseLocale](#baselocale) | `string` | `'en'` |
| [outputFormat](#outputformat) | `'TypeScript'` | `'JavaScript'` | `'TypeScript'` |
| [esmImports](#esmimports) | `boolean | '.js' | 'fileEnding' | `false` |
@@ -358,4 +358,3 @@ const displaySettingsPage = async (locale) => {
}
```
> make sure to call `setLocale` after you load new namespaces !
-
diff --git a/packages/generator/src/files/generate-adapter-preact.mts b/packages/generator/src/files/generate-adapter-preact.mts
new file mode 100644
index 00000000..22e737b8
--- /dev/null
+++ b/packages/generator/src/files/generate-adapter-preact.mts
@@ -0,0 +1,69 @@
+import type { GeneratorConfigWithDefaultValues } from '../../../config/src/types.mjs'
+import {
+ fileEnding,
+ generics,
+ importTypes,
+ jsDocImports,
+ jsDocType,
+ OVERRIDE_WARNING,
+ relativeFileImportPath,
+ tsCheck,
+ type,
+} from '../output-handler.mjs'
+import { writeFileIfContainsChanges } from '../utils/file.utils.mjs'
+import { prettify } from '../utils/generator.utils.mjs'
+
+const getPreactUtils = ({ utilFileName, typesFileName, banner }: GeneratorConfigWithDefaultValues) => {
+ return `${OVERRIDE_WARNING}${tsCheck}
+${banner}
+
+${jsDocImports(
+ {
+ from: 'typesafe-i18n/preact',
+ type: 'PreactInit',
+ alias: 'PreactInit',
+ },
+ {
+ from: 'typesafe-i18n/preact',
+ type: 'I18nContextType',
+ alias: 'I18nContextType',
+ },
+ { from: relativeFileImportPath(typesFileName), type: 'Formatters' },
+ { from: relativeFileImportPath(typesFileName), type: 'Locales' },
+ { from: relativeFileImportPath(typesFileName), type: 'TranslationFunctions' },
+ { from: relativeFileImportPath(typesFileName), type: 'Translations' },
+)}
+
+import { useContext } from 'preact/hooks'
+import { initI18nPreact } from 'typesafe-i18n/preact'
+${importTypes('typesafe-i18n/preact', 'I18nContextType')}
+${importTypes(relativeFileImportPath(typesFileName), 'Formatters', 'Locales', 'TranslationFunctions', 'Translations')}
+import { loadedFormatters, loadedLocales } from '${relativeFileImportPath(utilFileName)}'
+
+${jsDocType('PreactInit')}
+const { component: TypesafeI18n, context: I18nContext } = initI18nPreact${generics(
+ 'Locales',
+ 'Translations',
+ 'TranslationFunctions',
+ 'Formatters',
+ )}(loadedLocales, loadedFormatters)
+
+${jsDocType('() => I18nContextType')}
+const useI18nContext = ()${type(
+ 'I18nContextType',
+ )} => useContext(I18nContext)
+
+export { I18nContext, useI18nContext }
+
+export default TypesafeI18n
+`
+}
+
+export const generatePreactAdapter = async (config: GeneratorConfigWithDefaultValues): Promise => {
+ const { outputPath } = config
+
+ const preactUtils = getPreactUtils(config)
+
+ const fileName = config.adapterFileName || 'i18n-preact'
+ await writeFileIfContainsChanges(outputPath, `${fileName}${fileEnding}x`, prettify(preactUtils))
+}
diff --git a/packages/generator/src/generate-files.mts b/packages/generator/src/generate-files.mts
index f5ed4fb6..5da1a52d 100644
--- a/packages/generator/src/generate-files.mts
+++ b/packages/generator/src/generate-files.mts
@@ -3,7 +3,8 @@ import type { BaseTranslation, Locale } from '../../runtime/src/core.mjs'
import { generateAngularAdapter } from './files/generate-adapter-angular.mjs'
import { generateDenoAdapter } from './files/generate-adapter-deno.mjs'
import { generateNodeAdapter } from './files/generate-adapter-node.mjs'
-import { generateReactAdapter } from './files/generate-adapter-react.mjs'
+import { generatePreactAdapter } from './files/generate-adapter-preact.mjs'
+import { generateReactAdapter } from './files/generate-adapter-react.mjs'
import { generateSolidAdapter } from './files/generate-adapter-solid.mjs'
import { generateSvelteAdapter } from './files/generate-adapter-svelte.mjs'
import { generateVueAdapter } from './files/generate-adapter-vue.mjs'
@@ -134,12 +135,15 @@ const addAdapters = (config: GeneratorConfigWithDefaultValues, promises: Promise
case 'deno':
promises.push(generateDenoAdapter(config))
break
- case 'node':
- promises.push(generateNodeAdapter(config))
- break
- case 'react':
- promises.push(generateReactAdapter(config))
- break
+ case 'node':
+ promises.push(generateNodeAdapter(config))
+ break
+ case 'preact':
+ promises.push(generatePreactAdapter(config))
+ break
+ case 'react':
+ promises.push(generateReactAdapter(config))
+ break
case 'solid':
promises.push(generateSolidAdapter(config))
break
diff --git a/packages/generator/test/generated/adapter-preact-esm-jsdoc/de/index.js b/packages/generator/test/generated/adapter-preact-esm-jsdoc/de/index.js
new file mode 100644
index 00000000..964e7f91
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm-jsdoc/de/index.js
@@ -0,0 +1,13 @@
+// @ts-check
+
+/**
+ * @typedef { import('../types.actual.js').Translation } Translation
+ */
+
+/** @type { Translation } */
+const de = {
+ // this is an example Translation, just rename or delete this folder if you want
+ HI: 'Hallo {name}! Bitte hinterlasse einen Stern, wenn dir das Projekt gefällt: https://github.com/ivanhofer/typesafe-i18n',
+}
+
+export default de
diff --git a/packages/generator/test/generated/adapter-preact-esm-jsdoc/en/index.js b/packages/generator/test/generated/adapter-preact-esm-jsdoc/en/index.js
new file mode 100644
index 00000000..5c1587f3
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm-jsdoc/en/index.js
@@ -0,0 +1,13 @@
+// @ts-check
+
+/**
+ * @typedef { import('../types.actual.js').BaseTranslation } BaseTranslation
+ */
+
+/** @type { BaseTranslation } */
+const en = {
+ // TODO: your translations go here
+ HI: 'Hi {name:string}! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n',
+}
+
+export default en
diff --git a/packages/generator/test/generated/adapter-preact-esm-jsdoc/formatters-template.expected.js b/packages/generator/test/generated/adapter-preact-esm-jsdoc/formatters-template.expected.js
new file mode 100644
index 00000000..a18203cb
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm-jsdoc/formatters-template.expected.js
@@ -0,0 +1,20 @@
+// @ts-check
+
+/**
+ * @typedef { import('typesafe-i18n').FormattersInitializer } FormattersInitializer
+ * @typedef { import('./types.actual.js').Locales } Locales
+ * @typedef { import('./types.actual.js').Formatters } Formatters
+ */
+
+/**
+ * @param { Locales } locale
+ * @return { Formatters }
+ */
+export const initFormatters = (locale) => {
+ /** @type { Formatters } */
+ const formatters = {
+ // add your formatter functions here
+ }
+
+ return formatters
+}
diff --git a/packages/generator/test/generated/adapter-preact-esm-jsdoc/preact.expected.jsx b/packages/generator/test/generated/adapter-preact-esm-jsdoc/preact.expected.jsx
new file mode 100644
index 00000000..e27e2554
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm-jsdoc/preact.expected.jsx
@@ -0,0 +1,27 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+// @ts-check
+/* eslint-disable */
+
+/**
+ * @typedef { import('typesafe-i18n/preact').PreactInit } PreactInit
+ * @typedef { import('typesafe-i18n/preact').I18nContextType } I18nContextType
+ * @typedef { import('./types.actual.js').Formatters } Formatters
+ * @typedef { import('./types.actual.js').Locales } Locales
+ * @typedef { import('./types.actual.js').TranslationFunctions } TranslationFunctions
+ * @typedef { import('./types.actual.js').Translations } Translations
+ */
+
+import { useContext } from 'preact/hooks'
+import { initI18nPreact } from 'typesafe-i18n/preact'
+
+import { loadedFormatters, loadedLocales } from './util.actual.js'
+
+/** @type { PreactInit } */
+const { component: TypesafeI18n, context: I18nContext } = initI18nPreact(loadedLocales, loadedFormatters)
+
+/** @type { () => I18nContextType } */
+const useI18nContext = () => useContext(I18nContext)
+
+export { I18nContext, useI18nContext }
+
+export default TypesafeI18n
diff --git a/packages/generator/test/generated/adapter-preact-esm-jsdoc/types.expected.d.ts b/packages/generator/test/generated/adapter-preact-esm-jsdoc/types.expected.d.ts
new file mode 100644
index 00000000..f700bc95
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm-jsdoc/types.expected.d.ts
@@ -0,0 +1,30 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+import type { BaseTranslation as BaseTranslationType, LocalizedString, RequiredParams } from 'typesafe-i18n'
+
+export type BaseTranslation = BaseTranslationType
+export type BaseLocale = 'en'
+
+export type Locales =
+ | 'en'
+
+export type Translation = RootTranslation
+
+export type Translations = RootTranslation
+
+type RootTranslation = {
+ /**
+ * H​i​ ​{​0​}
+ * @param {string} 0
+ */
+ HELLO_PREACT: RequiredParams<'0'>
+}
+
+export type TranslationFunctions = {
+ /**
+ * Hi {0}
+ */
+ HELLO_PREACT: (arg0: string) => LocalizedString
+}
+
+export type Formatters = {}
diff --git a/packages/generator/test/generated/adapter-preact-esm-jsdoc/util.expected.async.js b/packages/generator/test/generated/adapter-preact-esm-jsdoc/util.expected.async.js
new file mode 100644
index 00000000..8f7c7539
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm-jsdoc/util.expected.async.js
@@ -0,0 +1,49 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+// @ts-check
+/* eslint-disable */
+
+/**
+ * @typedef { import('./types.actual.js').Locales } Locales
+ * @typedef { import('./types.actual.js').Translations } Translations
+ */
+
+import { initFormatters } from './formatters-template.actual.js'
+
+import { loadedFormatters, loadedLocales, locales } from './util.actual.js'
+
+const localeTranslationLoaders = {
+ en: () => import('./en/index.js'),
+}
+
+/**
+ * @param { Locales } locale
+ * @param { Partial } dictionary
+ * @return { Translations }
+ */
+const updateDictionary = (locale, dictionary) =>
+ loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary }
+
+/**
+ * @param { Locales } locale
+ * @return { Promise }
+ */
+export const importLocaleAsync = async (locale) =>
+ /** @type { Translations } */ (/** @type { unknown } */ ((await localeTranslationLoaders[locale]()).default))
+
+/**
+ * @param { Locales } locale
+ * @return { Promise }
+ */
+export const loadLocaleAsync = async (locale) => {
+ updateDictionary(locale, await importLocaleAsync(locale))
+ loadFormatters(locale)
+}
+
+export const loadAllLocalesAsync = () => Promise.all(locales.map(loadLocaleAsync))
+
+/**
+ * @param { Locales } locale
+ * @return { void }
+ */
+export const loadFormatters = (locale) =>
+ void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/packages/generator/test/generated/adapter-preact-esm-jsdoc/util.expected.js b/packages/generator/test/generated/adapter-preact-esm-jsdoc/util.expected.js
new file mode 100644
index 00000000..678455a4
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm-jsdoc/util.expected.js
@@ -0,0 +1,69 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+// @ts-check
+/* eslint-disable */
+
+/**
+ * @typedef { import('typesafe-i18n').TranslateByString } TranslateByString
+ * @typedef { import('typesafe-i18n').LocaleTranslations } LocaleTranslations
+ * @typedef { import('typesafe-i18n').LocaleTranslationFunctions } LocaleTranslationFunctions
+ * @typedef { import('typesafe-i18n/detectors').LocaleDetector } LocaleDetector
+ * @typedef { import('./types.actual.js').Locales } Locales
+ * @typedef { import('./types.actual.js').Formatters } Formatters
+ * @typedef { import('./types.actual.js').Translations } Translations
+ * @typedef { import('./types.actual.js').TranslationFunctions } TranslationFunctions
+ */
+
+import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n'
+
+import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors'
+import { initExtendDictionary } from 'typesafe-i18n/utils'
+
+/** @type { Locales } */
+export const baseLocale = 'en'
+
+/** @type { Locales[] } */
+export const locales = [
+ 'en'
+]
+
+/**
+ * @param { string } locale
+ * @return { locale is Locales }
+ */
+export const isLocale = (locale) => locales.includes(/** @type { Locales } */ (locale))
+
+export const loadedLocales = /** @type { Record } */ ({})
+
+export const loadedFormatters = /** @type { Record } */ ({})
+
+/** @type { ReturnType> } */
+export const extendDictionary = initExtendDictionary()
+
+/**
+ * @param { Locales } locale
+ * @return { TranslateByString }
+ */
+export const i18nString = (locale) => initI18nString(locale, loadedFormatters[locale])
+
+/**
+ * @param { Locales } locale
+ * @return { TranslationFunctions }
+ */
+export const i18nObject = (locale) =>
+ initI18nObject(
+ locale,
+ loadedLocales[locale],
+ loadedFormatters[locale]
+ )
+
+/**
+ * @return { LocaleTranslationFunctions }
+ */
+export const i18n = () =>
+ initI18n(loadedLocales, loadedFormatters)
+
+/**
+ * @param { LocaleDetector[] } detectors
+ * @return { Locales }
+ */
+export const detectLocale = (...detectors) => detectLocaleFn(baseLocale, locales, ...detectors)
diff --git a/packages/generator/test/generated/adapter-preact-esm-jsdoc/util.expected.sync.js b/packages/generator/test/generated/adapter-preact-esm-jsdoc/util.expected.sync.js
new file mode 100644
index 00000000..fb8160d8
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm-jsdoc/util.expected.sync.js
@@ -0,0 +1,41 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+// @ts-check
+/* eslint-disable */
+
+/**
+ * @typedef { import('./types.actual.js').Locales } Locales
+ * @typedef { import('./types.actual.js').Translations } Translations
+ */
+
+import { initFormatters } from './formatters-template.actual.js'
+
+import { loadedFormatters, loadedLocales, locales } from './util.actual.js'
+
+import en from './en/index.js'
+
+const localeTranslations = {
+ en,
+}
+
+/**
+ * @param { Locales } locale
+ * @return { void }
+ */
+export const loadLocale = (locale) => {
+ if (loadedLocales[locale]) return
+
+ loadedLocales[locale] = /** @type { Translations } */ (/** @type { unknown } */ (localeTranslations[locale]))
+ loadFormatters(locale)
+}
+
+/**
+ * @return { void }
+ */
+export const loadAllLocales = () => locales.forEach(loadLocale)
+
+/**
+ * @param { Locales } locale
+ * @return { void }
+ */
+export const loadFormatters = (locale) =>
+ void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/packages/generator/test/generated/adapter-preact-esm/de/index.ts b/packages/generator/test/generated/adapter-preact-esm/de/index.ts
new file mode 100644
index 00000000..b2fadde7
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm/de/index.ts
@@ -0,0 +1,8 @@
+import type { Translation } from '../types.actual.js'
+
+const de = {
+ // this is an example Translation, just rename or delete this folder if you want
+ HI: 'Hallo {name}! Bitte hinterlasse einen Stern, wenn dir das Projekt gefällt: https://github.com/ivanhofer/typesafe-i18n',
+} satisfies Translation
+
+export default de
diff --git a/packages/generator/test/generated/adapter-preact-esm/en/index.ts b/packages/generator/test/generated/adapter-preact-esm/en/index.ts
new file mode 100644
index 00000000..6981ef49
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm/en/index.ts
@@ -0,0 +1,8 @@
+import type { BaseTranslation } from '../types.actual.js'
+
+const en = {
+ // TODO: your translations go here
+ HI: 'Hi {name:string}! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n',
+} satisfies BaseTranslation
+
+export default en
diff --git a/packages/generator/test/generated/adapter-preact-esm/formatters-template.expected.ts b/packages/generator/test/generated/adapter-preact-esm/formatters-template.expected.ts
new file mode 100644
index 00000000..e42efe0d
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm/formatters-template.expected.ts
@@ -0,0 +1,11 @@
+import type { FormattersInitializer } from 'typesafe-i18n'
+import type { Locales, Formatters } from './types.actual.js'
+
+export const initFormatters: FormattersInitializer = (locale: Locales) => {
+
+ const formatters: Formatters = {
+ // add your formatter functions here
+ }
+
+ return formatters
+}
diff --git a/packages/generator/test/generated/adapter-preact-esm/preact.expected.tsx b/packages/generator/test/generated/adapter-preact-esm/preact.expected.tsx
new file mode 100644
index 00000000..d2e7a3a2
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm/preact.expected.tsx
@@ -0,0 +1,16 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { useContext } from 'preact/hooks'
+import { initI18nPreact } from 'typesafe-i18n/preact'
+import type { I18nContextType } from 'typesafe-i18n/preact'
+import type { Formatters, Locales, TranslationFunctions, Translations } from './types.actual.js'
+import { loadedFormatters, loadedLocales } from './util.actual.js'
+
+const { component: TypesafeI18n, context: I18nContext } = initI18nPreact(loadedLocales, loadedFormatters)
+
+const useI18nContext = (): I18nContextType => useContext(I18nContext)
+
+export { I18nContext, useI18nContext }
+
+export default TypesafeI18n
diff --git a/packages/generator/test/generated/adapter-preact-esm/types.expected.ts b/packages/generator/test/generated/adapter-preact-esm/types.expected.ts
new file mode 100644
index 00000000..f700bc95
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm/types.expected.ts
@@ -0,0 +1,30 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+import type { BaseTranslation as BaseTranslationType, LocalizedString, RequiredParams } from 'typesafe-i18n'
+
+export type BaseTranslation = BaseTranslationType
+export type BaseLocale = 'en'
+
+export type Locales =
+ | 'en'
+
+export type Translation = RootTranslation
+
+export type Translations = RootTranslation
+
+type RootTranslation = {
+ /**
+ * H​i​ ​{​0​}
+ * @param {string} 0
+ */
+ HELLO_PREACT: RequiredParams<'0'>
+}
+
+export type TranslationFunctions = {
+ /**
+ * Hi {0}
+ */
+ HELLO_PREACT: (arg0: string) => LocalizedString
+}
+
+export type Formatters = {}
diff --git a/packages/generator/test/generated/adapter-preact-esm/util.expected.async.ts b/packages/generator/test/generated/adapter-preact-esm/util.expected.async.ts
new file mode 100644
index 00000000..6c591dc1
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm/util.expected.async.ts
@@ -0,0 +1,26 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { initFormatters } from './formatters-template.actual.js'
+import type { Locales, Translations } from './types.actual.js'
+import { loadedFormatters, loadedLocales, locales } from './util.actual.js'
+
+const localeTranslationLoaders = {
+ en: () => import('./en/index.js'),
+}
+
+const updateDictionary = (locale: Locales, dictionary: Partial): Translations =>
+ loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary }
+
+export const importLocaleAsync = async (locale: Locales): Promise =>
+ (await localeTranslationLoaders[locale]()).default as unknown as Translations
+
+export const loadLocaleAsync = async (locale: Locales): Promise => {
+ updateDictionary(locale, await importLocaleAsync(locale))
+ loadFormatters(locale)
+}
+
+export const loadAllLocalesAsync = (): Promise => Promise.all(locales.map(loadLocaleAsync))
+
+export const loadFormatters = (locale: Locales): void =>
+ void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/packages/generator/test/generated/adapter-preact-esm/util.expected.sync.ts b/packages/generator/test/generated/adapter-preact-esm/util.expected.sync.ts
new file mode 100644
index 00000000..2d31ca7f
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm/util.expected.sync.ts
@@ -0,0 +1,24 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { initFormatters } from './formatters-template.actual.js'
+import type { Locales, Translations } from './types.actual.js'
+import { loadedFormatters, loadedLocales, locales } from './util.actual.js'
+
+import en from './en/index.js'
+
+const localeTranslations = {
+ en,
+}
+
+export const loadLocale = (locale: Locales): void => {
+ if (loadedLocales[locale]) return
+
+ loadedLocales[locale] = localeTranslations[locale] as unknown as Translations
+ loadFormatters(locale)
+}
+
+export const loadAllLocales = (): void => locales.forEach(loadLocale)
+
+export const loadFormatters = (locale: Locales): void =>
+ void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/packages/generator/test/generated/adapter-preact-esm/util.expected.ts b/packages/generator/test/generated/adapter-preact-esm/util.expected.ts
new file mode 100644
index 00000000..7f453c2d
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-esm/util.expected.ts
@@ -0,0 +1,37 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n'
+import type { LocaleDetector } from 'typesafe-i18n/detectors'
+import type { LocaleTranslationFunctions, TranslateByString } from 'typesafe-i18n'
+import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors'
+import { initExtendDictionary } from 'typesafe-i18n/utils'
+import type { Formatters, Locales, Translations, TranslationFunctions } from './types.actual.js'
+
+export const baseLocale: Locales = 'en'
+
+export const locales: Locales[] = [
+ 'en'
+]
+
+export const isLocale = (locale: string): locale is Locales => locales.includes(locale as Locales)
+
+export const loadedLocales: Record = {} as Record
+
+export const loadedFormatters: Record = {} as Record
+
+export const extendDictionary = initExtendDictionary()
+
+export const i18nString = (locale: Locales): TranslateByString => initI18nString(locale, loadedFormatters[locale])
+
+export const i18nObject = (locale: Locales): TranslationFunctions =>
+ initI18nObject(
+ locale,
+ loadedLocales[locale],
+ loadedFormatters[locale]
+ )
+
+export const i18n = (): LocaleTranslationFunctions =>
+ initI18n(loadedLocales, loadedFormatters)
+
+export const detectLocale = (...detectors: LocaleDetector[]): Locales => detectLocaleFn(baseLocale, locales, ...detectors)
diff --git a/packages/generator/test/generated/adapter-preact-jsdoc/de/index.js b/packages/generator/test/generated/adapter-preact-jsdoc/de/index.js
new file mode 100644
index 00000000..aed7d238
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-jsdoc/de/index.js
@@ -0,0 +1,13 @@
+// @ts-check
+
+/**
+ * @typedef { import('../types.actual').Translation } Translation
+ */
+
+/** @type { Translation } */
+const de = {
+ // this is an example Translation, just rename or delete this folder if you want
+ HI: 'Hallo {name}! Bitte hinterlasse einen Stern, wenn dir das Projekt gefällt: https://github.com/ivanhofer/typesafe-i18n',
+}
+
+module.exports = de
diff --git a/packages/generator/test/generated/adapter-preact-jsdoc/en/index.js b/packages/generator/test/generated/adapter-preact-jsdoc/en/index.js
new file mode 100644
index 00000000..a3d11869
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-jsdoc/en/index.js
@@ -0,0 +1,13 @@
+// @ts-check
+
+/**
+ * @typedef { import('../types.actual').BaseTranslation } BaseTranslation
+ */
+
+/** @type { BaseTranslation } */
+const en = {
+ // TODO: your translations go here
+ HI: 'Hi {name:string}! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n',
+}
+
+module.exports = en
diff --git a/packages/generator/test/generated/adapter-preact-jsdoc/formatters-template.expected.js b/packages/generator/test/generated/adapter-preact-jsdoc/formatters-template.expected.js
new file mode 100644
index 00000000..1997db52
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-jsdoc/formatters-template.expected.js
@@ -0,0 +1,20 @@
+// @ts-check
+
+/**
+ * @typedef { import('typesafe-i18n').FormattersInitializer } FormattersInitializer
+ * @typedef { import('./types.actual').Locales } Locales
+ * @typedef { import('./types.actual').Formatters } Formatters
+ */
+
+/**
+ * @param { Locales } locale
+ * @return { Formatters }
+ */
+export const initFormatters = (locale) => {
+ /** @type { Formatters } */
+ const formatters = {
+ // add your formatter functions here
+ }
+
+ return formatters
+}
diff --git a/packages/generator/test/generated/adapter-preact-jsdoc/preact.expected.jsx b/packages/generator/test/generated/adapter-preact-jsdoc/preact.expected.jsx
new file mode 100644
index 00000000..5e43cf91
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-jsdoc/preact.expected.jsx
@@ -0,0 +1,27 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+// @ts-check
+/* eslint-disable */
+
+/**
+ * @typedef { import('typesafe-i18n/preact').PreactInit } PreactInit
+ * @typedef { import('typesafe-i18n/preact').I18nContextType } I18nContextType
+ * @typedef { import('./types.actual').Formatters } Formatters
+ * @typedef { import('./types.actual').Locales } Locales
+ * @typedef { import('./types.actual').TranslationFunctions } TranslationFunctions
+ * @typedef { import('./types.actual').Translations } Translations
+ */
+
+import { useContext } from 'preact/hooks'
+import { initI18nPreact } from 'typesafe-i18n/preact'
+
+import { loadedFormatters, loadedLocales } from './util.actual'
+
+/** @type { PreactInit } */
+const { component: TypesafeI18n, context: I18nContext } = initI18nPreact(loadedLocales, loadedFormatters)
+
+/** @type { () => I18nContextType } */
+const useI18nContext = () => useContext(I18nContext)
+
+export { I18nContext, useI18nContext }
+
+export default TypesafeI18n
diff --git a/packages/generator/test/generated/adapter-preact-jsdoc/types.expected.d.ts b/packages/generator/test/generated/adapter-preact-jsdoc/types.expected.d.ts
new file mode 100644
index 00000000..f700bc95
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-jsdoc/types.expected.d.ts
@@ -0,0 +1,30 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+import type { BaseTranslation as BaseTranslationType, LocalizedString, RequiredParams } from 'typesafe-i18n'
+
+export type BaseTranslation = BaseTranslationType
+export type BaseLocale = 'en'
+
+export type Locales =
+ | 'en'
+
+export type Translation = RootTranslation
+
+export type Translations = RootTranslation
+
+type RootTranslation = {
+ /**
+ * H​i​ ​{​0​}
+ * @param {string} 0
+ */
+ HELLO_PREACT: RequiredParams<'0'>
+}
+
+export type TranslationFunctions = {
+ /**
+ * Hi {0}
+ */
+ HELLO_PREACT: (arg0: string) => LocalizedString
+}
+
+export type Formatters = {}
diff --git a/packages/generator/test/generated/adapter-preact-jsdoc/util.expected.async.js b/packages/generator/test/generated/adapter-preact-jsdoc/util.expected.async.js
new file mode 100644
index 00000000..e5ee2ab3
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-jsdoc/util.expected.async.js
@@ -0,0 +1,49 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+// @ts-check
+/* eslint-disable */
+
+/**
+ * @typedef { import('./types.actual').Locales } Locales
+ * @typedef { import('./types.actual').Translations } Translations
+ */
+
+import { initFormatters } from './formatters-template.actual'
+
+import { loadedFormatters, loadedLocales, locales } from './util.actual'
+
+const localeTranslationLoaders = {
+ en: () => import('./en'),
+}
+
+/**
+ * @param { Locales } locale
+ * @param { Partial } dictionary
+ * @return { Translations }
+ */
+const updateDictionary = (locale, dictionary) =>
+ loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary }
+
+/**
+ * @param { Locales } locale
+ * @return { Promise }
+ */
+export const importLocaleAsync = async (locale) =>
+ /** @type { Translations } */ (/** @type { unknown } */ ((await localeTranslationLoaders[locale]()).default))
+
+/**
+ * @param { Locales } locale
+ * @return { Promise }
+ */
+export const loadLocaleAsync = async (locale) => {
+ updateDictionary(locale, await importLocaleAsync(locale))
+ loadFormatters(locale)
+}
+
+export const loadAllLocalesAsync = () => Promise.all(locales.map(loadLocaleAsync))
+
+/**
+ * @param { Locales } locale
+ * @return { void }
+ */
+export const loadFormatters = (locale) =>
+ void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/packages/generator/test/generated/adapter-preact-jsdoc/util.expected.js b/packages/generator/test/generated/adapter-preact-jsdoc/util.expected.js
new file mode 100644
index 00000000..c11b64b3
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-jsdoc/util.expected.js
@@ -0,0 +1,69 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+// @ts-check
+/* eslint-disable */
+
+/**
+ * @typedef { import('typesafe-i18n').TranslateByString } TranslateByString
+ * @typedef { import('typesafe-i18n').LocaleTranslations } LocaleTranslations
+ * @typedef { import('typesafe-i18n').LocaleTranslationFunctions } LocaleTranslationFunctions
+ * @typedef { import('typesafe-i18n/detectors').LocaleDetector } LocaleDetector
+ * @typedef { import('./types.actual').Locales } Locales
+ * @typedef { import('./types.actual').Formatters } Formatters
+ * @typedef { import('./types.actual').Translations } Translations
+ * @typedef { import('./types.actual').TranslationFunctions } TranslationFunctions
+ */
+
+import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n'
+
+import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors'
+import { initExtendDictionary } from 'typesafe-i18n/utils'
+
+/** @type { Locales } */
+export const baseLocale = 'en'
+
+/** @type { Locales[] } */
+export const locales = [
+ 'en'
+]
+
+/**
+ * @param { string } locale
+ * @return { locale is Locales }
+ */
+export const isLocale = (locale) => locales.includes(/** @type { Locales } */ (locale))
+
+export const loadedLocales = /** @type { Record } */ ({})
+
+export const loadedFormatters = /** @type { Record } */ ({})
+
+/** @type { ReturnType> } */
+export const extendDictionary = initExtendDictionary()
+
+/**
+ * @param { Locales } locale
+ * @return { TranslateByString }
+ */
+export const i18nString = (locale) => initI18nString(locale, loadedFormatters[locale])
+
+/**
+ * @param { Locales } locale
+ * @return { TranslationFunctions }
+ */
+export const i18nObject = (locale) =>
+ initI18nObject(
+ locale,
+ loadedLocales[locale],
+ loadedFormatters[locale]
+ )
+
+/**
+ * @return { LocaleTranslationFunctions }
+ */
+export const i18n = () =>
+ initI18n(loadedLocales, loadedFormatters)
+
+/**
+ * @param { LocaleDetector[] } detectors
+ * @return { Locales }
+ */
+export const detectLocale = (...detectors) => detectLocaleFn(baseLocale, locales, ...detectors)
diff --git a/packages/generator/test/generated/adapter-preact-jsdoc/util.expected.sync.js b/packages/generator/test/generated/adapter-preact-jsdoc/util.expected.sync.js
new file mode 100644
index 00000000..3120f3bb
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact-jsdoc/util.expected.sync.js
@@ -0,0 +1,41 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+// @ts-check
+/* eslint-disable */
+
+/**
+ * @typedef { import('./types.actual').Locales } Locales
+ * @typedef { import('./types.actual').Translations } Translations
+ */
+
+import { initFormatters } from './formatters-template.actual'
+
+import { loadedFormatters, loadedLocales, locales } from './util.actual'
+
+import en from './en'
+
+const localeTranslations = {
+ en,
+}
+
+/**
+ * @param { Locales } locale
+ * @return { void }
+ */
+export const loadLocale = (locale) => {
+ if (loadedLocales[locale]) return
+
+ loadedLocales[locale] = /** @type { Translations } */ (/** @type { unknown } */ (localeTranslations[locale]))
+ loadFormatters(locale)
+}
+
+/**
+ * @return { void }
+ */
+export const loadAllLocales = () => locales.forEach(loadLocale)
+
+/**
+ * @param { Locales } locale
+ * @return { void }
+ */
+export const loadFormatters = (locale) =>
+ void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/packages/generator/test/generated/adapter-preact/de/index.ts b/packages/generator/test/generated/adapter-preact/de/index.ts
new file mode 100644
index 00000000..ae326a7b
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact/de/index.ts
@@ -0,0 +1,8 @@
+import type { Translation } from '../types.actual'
+
+const de = {
+ // this is an example Translation, just rename or delete this folder if you want
+ HI: 'Hallo {name}! Bitte hinterlasse einen Stern, wenn dir das Projekt gefällt: https://github.com/ivanhofer/typesafe-i18n',
+} satisfies Translation
+
+export default de
diff --git a/packages/generator/test/generated/adapter-preact/en/index.ts b/packages/generator/test/generated/adapter-preact/en/index.ts
new file mode 100644
index 00000000..8b3ed2d0
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact/en/index.ts
@@ -0,0 +1,8 @@
+import type { BaseTranslation } from '../types.actual'
+
+const en = {
+ // TODO: your translations go here
+ HI: 'Hi {name:string}! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n',
+} satisfies BaseTranslation
+
+export default en
diff --git a/packages/generator/test/generated/adapter-preact/formatters-template.expected.ts b/packages/generator/test/generated/adapter-preact/formatters-template.expected.ts
new file mode 100644
index 00000000..b0f8ca44
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact/formatters-template.expected.ts
@@ -0,0 +1,11 @@
+import type { FormattersInitializer } from 'typesafe-i18n'
+import type { Locales, Formatters } from './types.actual'
+
+export const initFormatters: FormattersInitializer = (locale: Locales) => {
+
+ const formatters: Formatters = {
+ // add your formatter functions here
+ }
+
+ return formatters
+}
diff --git a/packages/generator/test/generated/adapter-preact/preact.expected.tsx b/packages/generator/test/generated/adapter-preact/preact.expected.tsx
new file mode 100644
index 00000000..7ba0a4c8
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact/preact.expected.tsx
@@ -0,0 +1,16 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { useContext } from 'preact/hooks'
+import { initI18nPreact } from 'typesafe-i18n/preact'
+import type { I18nContextType } from 'typesafe-i18n/preact'
+import type { Formatters, Locales, TranslationFunctions, Translations } from './types.actual'
+import { loadedFormatters, loadedLocales } from './util.actual'
+
+const { component: TypesafeI18n, context: I18nContext } = initI18nPreact(loadedLocales, loadedFormatters)
+
+const useI18nContext = (): I18nContextType => useContext(I18nContext)
+
+export { I18nContext, useI18nContext }
+
+export default TypesafeI18n
diff --git a/packages/generator/test/generated/adapter-preact/types.expected.ts b/packages/generator/test/generated/adapter-preact/types.expected.ts
new file mode 100644
index 00000000..f700bc95
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact/types.expected.ts
@@ -0,0 +1,30 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+import type { BaseTranslation as BaseTranslationType, LocalizedString, RequiredParams } from 'typesafe-i18n'
+
+export type BaseTranslation = BaseTranslationType
+export type BaseLocale = 'en'
+
+export type Locales =
+ | 'en'
+
+export type Translation = RootTranslation
+
+export type Translations = RootTranslation
+
+type RootTranslation = {
+ /**
+ * H​i​ ​{​0​}
+ * @param {string} 0
+ */
+ HELLO_PREACT: RequiredParams<'0'>
+}
+
+export type TranslationFunctions = {
+ /**
+ * Hi {0}
+ */
+ HELLO_PREACT: (arg0: string) => LocalizedString
+}
+
+export type Formatters = {}
diff --git a/packages/generator/test/generated/adapter-preact/util.expected.async.ts b/packages/generator/test/generated/adapter-preact/util.expected.async.ts
new file mode 100644
index 00000000..b73ba5a2
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact/util.expected.async.ts
@@ -0,0 +1,26 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { initFormatters } from './formatters-template.actual'
+import type { Locales, Translations } from './types.actual'
+import { loadedFormatters, loadedLocales, locales } from './util.actual'
+
+const localeTranslationLoaders = {
+ en: () => import('./en'),
+}
+
+const updateDictionary = (locale: Locales, dictionary: Partial): Translations =>
+ loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary }
+
+export const importLocaleAsync = async (locale: Locales): Promise =>
+ (await localeTranslationLoaders[locale]()).default as unknown as Translations
+
+export const loadLocaleAsync = async (locale: Locales): Promise => {
+ updateDictionary(locale, await importLocaleAsync(locale))
+ loadFormatters(locale)
+}
+
+export const loadAllLocalesAsync = (): Promise => Promise.all(locales.map(loadLocaleAsync))
+
+export const loadFormatters = (locale: Locales): void =>
+ void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/packages/generator/test/generated/adapter-preact/util.expected.sync.ts b/packages/generator/test/generated/adapter-preact/util.expected.sync.ts
new file mode 100644
index 00000000..38d56b38
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact/util.expected.sync.ts
@@ -0,0 +1,24 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { initFormatters } from './formatters-template.actual'
+import type { Locales, Translations } from './types.actual'
+import { loadedFormatters, loadedLocales, locales } from './util.actual'
+
+import en from './en'
+
+const localeTranslations = {
+ en,
+}
+
+export const loadLocale = (locale: Locales): void => {
+ if (loadedLocales[locale]) return
+
+ loadedLocales[locale] = localeTranslations[locale] as unknown as Translations
+ loadFormatters(locale)
+}
+
+export const loadAllLocales = (): void => locales.forEach(loadLocale)
+
+export const loadFormatters = (locale: Locales): void =>
+ void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/packages/generator/test/generated/adapter-preact/util.expected.ts b/packages/generator/test/generated/adapter-preact/util.expected.ts
new file mode 100644
index 00000000..5895e92c
--- /dev/null
+++ b/packages/generator/test/generated/adapter-preact/util.expected.ts
@@ -0,0 +1,37 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n'
+import type { LocaleDetector } from 'typesafe-i18n/detectors'
+import type { LocaleTranslationFunctions, TranslateByString } from 'typesafe-i18n'
+import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors'
+import { initExtendDictionary } from 'typesafe-i18n/utils'
+import type { Formatters, Locales, Translations, TranslationFunctions } from './types.actual'
+
+export const baseLocale: Locales = 'en'
+
+export const locales: Locales[] = [
+ 'en'
+]
+
+export const isLocale = (locale: string): locale is Locales => locales.includes(locale as Locales)
+
+export const loadedLocales: Record = {} as Record
+
+export const loadedFormatters: Record = {} as Record
+
+export const extendDictionary = initExtendDictionary()
+
+export const i18nString = (locale: Locales): TranslateByString => initI18nString(locale, loadedFormatters[locale])
+
+export const i18nObject = (locale: Locales): TranslationFunctions =>
+ initI18nObject(
+ locale,
+ loadedLocales[locale],
+ loadedFormatters[locale]
+ )
+
+export const i18n = (): LocaleTranslationFunctions =>
+ initI18n(loadedLocales, loadedFormatters)
+
+export const detectLocale = (...detectors: LocaleDetector[]): Locales => detectLocaleFn(baseLocale, locales, ...detectors)
diff --git a/packages/generator/test/generated/adapters-preact-react-svelte/de/index.ts b/packages/generator/test/generated/adapters-preact-react-svelte/de/index.ts
new file mode 100644
index 00000000..ae326a7b
--- /dev/null
+++ b/packages/generator/test/generated/adapters-preact-react-svelte/de/index.ts
@@ -0,0 +1,8 @@
+import type { Translation } from '../types.actual'
+
+const de = {
+ // this is an example Translation, just rename or delete this folder if you want
+ HI: 'Hallo {name}! Bitte hinterlasse einen Stern, wenn dir das Projekt gefällt: https://github.com/ivanhofer/typesafe-i18n',
+} satisfies Translation
+
+export default de
diff --git a/packages/generator/test/generated/adapters-preact-react-svelte/en/index.ts b/packages/generator/test/generated/adapters-preact-react-svelte/en/index.ts
new file mode 100644
index 00000000..8b3ed2d0
--- /dev/null
+++ b/packages/generator/test/generated/adapters-preact-react-svelte/en/index.ts
@@ -0,0 +1,8 @@
+import type { BaseTranslation } from '../types.actual'
+
+const en = {
+ // TODO: your translations go here
+ HI: 'Hi {name:string}! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n',
+} satisfies BaseTranslation
+
+export default en
diff --git a/packages/generator/test/generated/adapters-preact-react-svelte/formatters-template.expected.ts b/packages/generator/test/generated/adapters-preact-react-svelte/formatters-template.expected.ts
new file mode 100644
index 00000000..b0f8ca44
--- /dev/null
+++ b/packages/generator/test/generated/adapters-preact-react-svelte/formatters-template.expected.ts
@@ -0,0 +1,11 @@
+import type { FormattersInitializer } from 'typesafe-i18n'
+import type { Locales, Formatters } from './types.actual'
+
+export const initFormatters: FormattersInitializer = (locale: Locales) => {
+
+ const formatters: Formatters = {
+ // add your formatter functions here
+ }
+
+ return formatters
+}
diff --git a/packages/generator/test/generated/adapters-preact-react-svelte/i18n-preact.tsx b/packages/generator/test/generated/adapters-preact-react-svelte/i18n-preact.tsx
new file mode 100644
index 00000000..7ba0a4c8
--- /dev/null
+++ b/packages/generator/test/generated/adapters-preact-react-svelte/i18n-preact.tsx
@@ -0,0 +1,16 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { useContext } from 'preact/hooks'
+import { initI18nPreact } from 'typesafe-i18n/preact'
+import type { I18nContextType } from 'typesafe-i18n/preact'
+import type { Formatters, Locales, TranslationFunctions, Translations } from './types.actual'
+import { loadedFormatters, loadedLocales } from './util.actual'
+
+const { component: TypesafeI18n, context: I18nContext } = initI18nPreact(loadedLocales, loadedFormatters)
+
+const useI18nContext = (): I18nContextType => useContext(I18nContext)
+
+export { I18nContext, useI18nContext }
+
+export default TypesafeI18n
diff --git a/packages/generator/test/generated/adapters-preact-react-svelte/i18n-react.tsx b/packages/generator/test/generated/adapters-preact-react-svelte/i18n-react.tsx
new file mode 100644
index 00000000..7ed836fc
--- /dev/null
+++ b/packages/generator/test/generated/adapters-preact-react-svelte/i18n-react.tsx
@@ -0,0 +1,16 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { useContext } from 'react'
+import { initI18nReact } from 'typesafe-i18n/react'
+import type { I18nContextType } from 'typesafe-i18n/react'
+import type { Formatters, Locales, TranslationFunctions, Translations } from './types.actual'
+import { loadedFormatters, loadedLocales } from './util.actual'
+
+const { component: TypesafeI18n, context: I18nContext } = initI18nReact(loadedLocales, loadedFormatters)
+
+const useI18nContext = (): I18nContextType => useContext(I18nContext)
+
+export { I18nContext, useI18nContext }
+
+export default TypesafeI18n
diff --git a/packages/generator/test/generated/adapters-preact-react-svelte/i18n-svelte.ts b/packages/generator/test/generated/adapters-preact-react-svelte/i18n-svelte.ts
new file mode 100644
index 00000000..aeb3cee0
--- /dev/null
+++ b/packages/generator/test/generated/adapters-preact-react-svelte/i18n-svelte.ts
@@ -0,0 +1,12 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { initI18nSvelte } from 'typesafe-i18n/svelte'
+import type { Formatters, Locales, TranslationFunctions, Translations } from './types.actual'
+import { loadedFormatters, loadedLocales } from './util.actual'
+
+const { locale, LL, setLocale } = initI18nSvelte(loadedLocales, loadedFormatters)
+
+export { locale, LL, setLocale }
+
+export default LL
diff --git a/packages/generator/test/generated/adapters-preact-react-svelte/types.expected.ts b/packages/generator/test/generated/adapters-preact-react-svelte/types.expected.ts
new file mode 100644
index 00000000..a30410f8
--- /dev/null
+++ b/packages/generator/test/generated/adapters-preact-react-svelte/types.expected.ts
@@ -0,0 +1,30 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+import type { BaseTranslation as BaseTranslationType, LocalizedString, RequiredParams } from 'typesafe-i18n'
+
+export type BaseTranslation = BaseTranslationType
+export type BaseLocale = 'en'
+
+export type Locales =
+ | 'en'
+
+export type Translation = RootTranslation
+
+export type Translations = RootTranslation
+
+type RootTranslation = {
+ /**
+ * H​i​ ​{​0​}
+ * @param {string} 0
+ */
+ HELLO: RequiredParams<'0'>
+}
+
+export type TranslationFunctions = {
+ /**
+ * Hi {0}
+ */
+ HELLO: (arg0: string) => LocalizedString
+}
+
+export type Formatters = {}
diff --git a/packages/generator/test/generated/adapters-preact-react-svelte/util.expected.async.ts b/packages/generator/test/generated/adapters-preact-react-svelte/util.expected.async.ts
new file mode 100644
index 00000000..b73ba5a2
--- /dev/null
+++ b/packages/generator/test/generated/adapters-preact-react-svelte/util.expected.async.ts
@@ -0,0 +1,26 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { initFormatters } from './formatters-template.actual'
+import type { Locales, Translations } from './types.actual'
+import { loadedFormatters, loadedLocales, locales } from './util.actual'
+
+const localeTranslationLoaders = {
+ en: () => import('./en'),
+}
+
+const updateDictionary = (locale: Locales, dictionary: Partial): Translations =>
+ loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary }
+
+export const importLocaleAsync = async (locale: Locales): Promise =>
+ (await localeTranslationLoaders[locale]()).default as unknown as Translations
+
+export const loadLocaleAsync = async (locale: Locales): Promise => {
+ updateDictionary(locale, await importLocaleAsync(locale))
+ loadFormatters(locale)
+}
+
+export const loadAllLocalesAsync = (): Promise => Promise.all(locales.map(loadLocaleAsync))
+
+export const loadFormatters = (locale: Locales): void =>
+ void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/packages/generator/test/generated/adapters-preact-react-svelte/util.expected.sync.ts b/packages/generator/test/generated/adapters-preact-react-svelte/util.expected.sync.ts
new file mode 100644
index 00000000..38d56b38
--- /dev/null
+++ b/packages/generator/test/generated/adapters-preact-react-svelte/util.expected.sync.ts
@@ -0,0 +1,24 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { initFormatters } from './formatters-template.actual'
+import type { Locales, Translations } from './types.actual'
+import { loadedFormatters, loadedLocales, locales } from './util.actual'
+
+import en from './en'
+
+const localeTranslations = {
+ en,
+}
+
+export const loadLocale = (locale: Locales): void => {
+ if (loadedLocales[locale]) return
+
+ loadedLocales[locale] = localeTranslations[locale] as unknown as Translations
+ loadFormatters(locale)
+}
+
+export const loadAllLocales = (): void => locales.forEach(loadLocale)
+
+export const loadFormatters = (locale: Locales): void =>
+ void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/packages/generator/test/generated/adapters-preact-react-svelte/util.expected.ts b/packages/generator/test/generated/adapters-preact-react-svelte/util.expected.ts
new file mode 100644
index 00000000..5895e92c
--- /dev/null
+++ b/packages/generator/test/generated/adapters-preact-react-svelte/util.expected.ts
@@ -0,0 +1,37 @@
+// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
+/* eslint-disable */
+
+import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n'
+import type { LocaleDetector } from 'typesafe-i18n/detectors'
+import type { LocaleTranslationFunctions, TranslateByString } from 'typesafe-i18n'
+import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors'
+import { initExtendDictionary } from 'typesafe-i18n/utils'
+import type { Formatters, Locales, Translations, TranslationFunctions } from './types.actual'
+
+export const baseLocale: Locales = 'en'
+
+export const locales: Locales[] = [
+ 'en'
+]
+
+export const isLocale = (locale: string): locale is Locales => locales.includes(locale as Locales)
+
+export const loadedLocales: Record = {} as Record
+
+export const loadedFormatters: Record = {} as Record
+
+export const extendDictionary = initExtendDictionary()
+
+export const i18nString = (locale: Locales): TranslateByString => initI18nString(locale, loadedFormatters[locale])
+
+export const i18nObject = (locale: Locales): TranslationFunctions =>
+ initI18nObject(
+ locale,
+ loadedLocales[locale],
+ loadedFormatters[locale]
+ )
+
+export const i18n = (): LocaleTranslationFunctions =>
+ initI18n(loadedLocales, loadedFormatters)
+
+export const detectLocale = (...detectors: LocaleDetector[]): Locales => detectLocaleFn(baseLocale, locales, ...detectors)
diff --git a/packages/generator/test/generator.test.ts b/packages/generator/test/generator.test.ts
index 3c6a5bd9..25ed9992 100644
--- a/packages/generator/test/generator.test.ts
+++ b/packages/generator/test/generator.test.ts
@@ -40,6 +40,7 @@ type FileToCheck =
| 'angular.service'
| 'deno'
| 'node'
+ | 'preact'
| 'react'
| 'solid'
| 'svelte'
@@ -63,12 +64,12 @@ const getPathOfOutputFile = (
const fileEnding =
outputFormat === 'TypeScript'
- ? file === 'react'
+ ? file === 'react' || file === 'preact'
? '.tsx'
: '.ts'
: file === 'types' || file === 'types-template'
? '.d.ts'
- : file === 'react'
+ : file === 'react' || file === 'preact'
? '.jsx'
: '.js'
return `${outputPath}/${prefix}/${fileName}${fileEnding}`
@@ -136,6 +137,7 @@ const testGeneratedOutput = async (
await check(prefix, 'util.async', outputFormat)
await check(prefix, 'formatters-template', outputFormat)
await check(prefix, 'types-template', outputFormat)
+ await check(prefix, 'preact', outputFormat)
await check(prefix, 'react', outputFormat)
await check(prefix, 'solid', outputFormat)
await check(prefix, 'angular.service', outputFormat)
@@ -393,6 +395,12 @@ testAdapterMatrix(
{ adapter: 'node', adapterFileName: getFileName('node') },
)
+testAdapterMatrix(
+ 'adapter-preact',
+ { HELLO_PREACT: 'Hi {0:string}' },
+ { adapter: 'preact', adapterFileName: getFileName('preact') },
+)
+
testAdapterMatrix(
'adapter-react',
{ HELLO_REACT: 'Hi {0:string}' },
@@ -425,6 +433,12 @@ testGeneratedOutput('adapters-angular', { HELLO: 'Hi {0:string}' }, { adapters:
testGeneratedOutput('adapters-node-react', { HELLO_NODE_REACT: 'Hi {0:string}' }, { adapters: ['node', 'react'] })
+testGeneratedOutput(
+ 'adapters-preact-react-svelte',
+ { HELLO: 'Hi {0:string}' },
+ { adapters: ['preact', 'react', 'svelte'] },
+)
+
testGeneratedOutput('adapters-react-svelte-vue', { HELLO: 'Hi {0:string}' }, { adapters: ['react', 'svelte', 'vue'] })
// --------------------------------------------------------------------------------------------------------------------
diff --git a/packages/link-typedefinitions.ts b/packages/link-typedefinitions.ts
index df60cdcb..e36144d6 100644
--- a/packages/link-typedefinitions.ts
+++ b/packages/link-typedefinitions.ts
@@ -8,6 +8,7 @@ type FilterFunction = (file: string) => boolean
const mappings: [FromWheretoImport, OutputPath?, FilterFunction?][] = [
['adapter-angular', 'angular'],
+ ['adapter-preact', 'preact'],
['adapter-react', 'react'],
['adapter-solid', 'solid'],
['adapter-svelte', 'svelte'],
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 937d6fd1..f5ee56a8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -72,6 +72,21 @@ importers:
specifier: ^5.1.6
version: 5.1.6
+ packages/adapter-preact:
+ devDependencies:
+ esbuild:
+ specifier: ^0.18.18
+ version: 0.18.18
+ preact:
+ specifier: ^10.17.1
+ version: 10.17.1
+ tsx:
+ specifier: ^3.12.7
+ version: 3.12.7
+ typescript:
+ specifier: ^5.1.6
+ version: 5.1.6
+
packages/adapter-react:
devDependencies:
'@types/react':
@@ -3041,6 +3056,10 @@ packages:
source-map-js: 1.0.2
dev: true
+ /preact@10.17.1:
+ resolution: {integrity: sha512-X9BODrvQ4Ekwv9GURm9AKAGaomqXmip7NQTZgY7gcNmr7XE83adOMJvd3N42id1tMFU7ojiynRsYnY6/BRFxLA==}
+ dev: true
+
/prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
diff --git a/website/src/assets/icons/preact.svg b/website/src/assets/icons/preact.svg
new file mode 100644
index 00000000..fe53254a
--- /dev/null
+++ b/website/src/assets/icons/preact.svg
@@ -0,0 +1,9 @@
+
diff --git a/website/src/routes/+page.svelte b/website/src/routes/+page.svelte
index 79fc9f2e..f7484dec 100644
--- a/website/src/routes/+page.svelte
+++ b/website/src/routes/+page.svelte
@@ -3,6 +3,7 @@
import iconAngular from '../assets/icons/angular.svg'
import iconJavaScript from '../assets/icons/javascript.svg'
import iconNodejs from '../assets/icons/nodejs.svg'
+ import iconPreact from '../assets/icons/preact.svg'
import iconReact from '../assets/icons/react.svg'
import iconSolidjs from '../assets/icons/solidjs.svg'
import iconSvelte from '../assets/icons/svelte.svg'
@@ -51,6 +52,13 @@
>
+
+
+