Skip to content

Commit

Permalink
Merge pull request #1 from intlify/feat/basic
Browse files Browse the repository at this point in the history
feat: support translation
  • Loading branch information
kazupon authored Oct 24, 2023
2 parents 5cfaba3 + 5d39bc5 commit 01d5d97
Show file tree
Hide file tree
Showing 21 changed files with 1,277 additions and 17 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

# vitest-environment-miniflare tries to load dist/index.cjs and work with vitest...
- name: Build codes
run: npm run build

- name: Test
run: npm run test

Expand Down
260 changes: 259 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,267 @@

Internationalization middleware & utilities for [Hono](https://hono.dev/)

## 🌟 Features

✅️  **Translation:** Simple API like
[vue-i18n](https://vue-i18n.intlify.dev/)

 **Custom locale detector:** You can implement your own locale detector
on server-side

✅️️  **Useful utilities:** support internationalization composables
utilities via [@intlify/utils](https://github.com/intlify/utils)

## 💿 Installation

```sh
# Using npm
npm install @intlify/hono

# Using yarn
yarn add @intlify/hono

# Using pnpm
pnpm add @intlify/hono

# Using bun
bun add @intlify/hono
```

## 🚀 Usage

```ts
import { Hono } from 'hono'
import {
defineI18nMiddleware,
detectLocaleFromAcceptLanguageHeader,
useTranslation,
} from '@intlify/hono'

// define middleware with vue-i18n like options
const i18nMiddleware = defineI18nMiddleware({
// detect locale with `accept-language` header
locale: detectLocaleFromAcceptLanguageHeader,
// resource messages
messages: {
en: {
hello: 'Hello {name}!',
},
ja: {
hello: 'こんにちは、{name}!',
},
},
// something options
// ...
})

const app = new Hono()

// install middleware with `app.use`
app.use('*', i18nMiddleware)

app.get('/', c => {
// use `useTranslation` in handler
const t = useTranslation(c)
return c.text(t('hello', { name: 'hono' }) + `\n`)
})

export default app
```

## 🛠️ Custom locale detection

You can detect locale with your custom logic from current `Context`.

example for detecting locale from url query:

```ts
import { defineI18nMiddleware, getQueryLocale } from '@intlify/hono'
import type { Context } from 'hono'

const DEFAULT_LOCALE = 'en'

// define custom locale detector
const localeDetector = (ctx: Context): string => {
try {
return getQueryLocale(ctx).toString()
} catch () {
return DEFAULT_LOCALE
}
}

const middleware = defineI18nMiddleware({
// set your custom locale detector
locale: localeDetector,
// something options
// ...
})
```

## 🧩 Type-safe resources

> [!WARNING]
> **This is experimental feature (inspired from [vue-i18n](https://vue-i18n.intlify.dev/guide/advanced/typescript.html#typescript-support)).**
> We would like to get feedback from you 🙂.
> [!NOTE]
> The exeample code is [here](https://github.com/intlify/hono/tree/main/playground/typesafe-schema)
You can support the type-safe resources with schema using TypeScript on `defineI18nMiddleware` options.

Locale messages resource:

```ts
export default {
hello: 'hello, {name}!'
}
```

your application code:

```ts
import { defineI18nMiddleware } from '@intlify/hono'
import en from './locales/en.ts'

// define resource schema, as 'en' is master resource schema
type ResourceSchema = typeof en

const i18nMiddleware = defineI18nMiddleware<[ResourceSchema], 'en' | 'ja'>({
messages: {
en: { hello: 'Hello, {name}' },
},
// something options
// ...
})

// someting your implementation code ...
// ...
```

Result of type checking with `tsc`:

```sh
npx tsc --noEmit
index.ts:13:3 - error TS2741: Property 'ja' is missing in type '{ en: { hello: string; }; }' but required in type '{ en: ResourceSchema; ja: ResourceSchema; }'.

13 messages: {
~~~~~~~~

../../node_modules/@intlify/core/node_modules/@intlify/core-base/dist/core-base.d.ts:125:5
125 messages?: {
~~~~~~~~
The expected type comes from property 'messages' which is declared here on type 'CoreOptions<string, { message: ResourceSchema; datetime: DateTimeFormat; number: NumberFormat; }, { messages: "en"; datetimeFormats: "en"; numberFormats: "en"; } | { ...; }, ... 8 more ..., NumberFormats<...>>'


Found 1 error in index.ts:13
```
If you are using [Visual Studio Code](https://code.visualstudio.com/) as an editor, you can notice that there is a resource definition omission in the editor with the following error before you run the typescript compilation.
![Type-safe resources](assets/typesafe-schema.png)
## 🖌️ Resource keys completion
> [!WARNING]
> **This is WIP 👷
> **This is experimental feature (inspired from [vue-i18n](https://vue-i18n.intlify.dev/guide/advanced/typescript.html#typescript-support)).**
> We would like to get feedback from you 🙂.
> [!NOTE]
> Resource Keys completion can be used if you are using [Visual Studio Code](https://code.visualstudio.com/)
You can completion resources key on translation function with `useTranslation`.
![Key Completion](assets/key-completion.gif)
resource keys completion has twe ways.
### Type parameter for `useTranslation`
> [!NOTE]
> The exeample code is [here](https://github.com/intlify/hono/tree/main/playground/local-schema)
You can `useTranslation` set the type parameter to the resource schema you want to key completion of the translation function.
the part of example:
```ts
app.get('/', c => {
type ResourceSchema = {
hello: string
}
// set resource schema as type parameter
const t = useTranslation<ResourceSchema>(c)
// you can completion when you type `t('`
return c.json(t('hello', { name: 'hono' }))
}),
```
### global resource schema with `declare module '@intlify/hono'`
> [!NOTE]
> The exeample code is [here](https://github.com/intlify/hono/tree/main/playground/global-schema)
You can do resource key completion with the translation function using the typescript `declare module`.
the part of example:
```ts
import en from './locales/en.ts'
// 'en' resource is master schema
type ResourceSchema = typeof en
// you can put the type extending with `declare module` as global resource schema
declare module '@intlify/hono' {
// extend `DefineLocaleMessage` with `ResourceSchema`
export interface DefineLocaleMessage extends ResourceSchema {}
}
app.get('/', c => {
const t = useTranslation(event)
// you can completion when you type `t('`
return c.json(t('hello', { name: 'hono' }))
}),
```
The advantage of this way is that it is not necessary to specify the resource schema in the `useTranslation` type parameter.
## 🛠️ Utilites & Helpers
`@intlify/hono` has a concept of composable utilities & helpers.
### Utilities
`@intlify/hono` composable utilities accept context (from
`(context) => {})`) as their first argument. (Exclud `useTranslation`) return the [`Intl.Locale`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale)
### Translations
- `useTranslation(context)`: use translation function
### Headers
- `getHeaderLocale(context, options)`: get locale from `accept-language` header
- `getHeaderLocales(context, options)`: get some locales from `accept-language` header
### Cookies
- `getCookieLocale(context, options)`: get locale from cookie
- `setCookieLocale(context, options)`: set locale to cookie
### Misc
- `getPathLocale(context, options)`: get locale from path
- `getQueryLocale(context, options)`: get locale from query
## Helpers
- `detectLocaleFromAcceptLanguageHeader(context)`: detect locale from `accept-language` header
## 🙌 Contributing guidelines
If you are interested in contributing to `@intlify/hono`, I highly recommend checking out [the contributing guidelines](/CONTRIBUTING.md) here. You'll find all the relevant information such as [how to make a PR](/CONTRIBUTING.md#pull-request-guidelines), [how to setup development](/CONTRIBUTING.md#development-setup)) etc., there.
## ©️ License
Expand Down
Binary file added assets/key-completion.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/key-completion.mov
Binary file not shown.
Binary file added assets/typesafe-schema.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion deno.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"singleQuote": true
},
"lint": {
"exclude": ["node_modules", "dist", "coverage", "deno"]
"exclude": ["node_modules", "dist", "coverage", "deno", "playground"]
/*
"rules": {
"tags": ["recommended"],
Expand Down
Loading

0 comments on commit 01d5d97

Please sign in to comment.