{"id":20817285,"url":"https://github.com/intlify/h3","last_synced_at":"2025-04-05T06:06:04.991Z","repository":{"id":194959394,"uuid":"691948863","full_name":"intlify/h3","owner":"intlify","description":"Internationalization middleware \u0026 utilities for h3","archived":false,"fork":false,"pushed_at":"2024-10-07T06:51:02.000Z","size":4461,"stargazers_count":35,"open_issues_count":11,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-10-14T00:31:53.707Z","etag":null,"topics":["h3","i18n","internationalization","intlify","middleware","utilities"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/intlify.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"kazupon"}},"created_at":"2023-09-15T08:26:05.000Z","updated_at":"2024-10-07T06:50:56.000Z","dependencies_parsed_at":"2024-03-12T11:28:53.585Z","dependency_job_id":"656608cc-7b22-4e46-a93e-281682eccdf2","html_url":"https://github.com/intlify/h3","commit_stats":{"total_commits":86,"total_committers":4,"mean_commits":21.5,"dds":"0.18604651162790697","last_synced_commit":"cc3b7a53719a9682a46399a98bb39b6be4c160f1"},"previous_names":["intlify/h3"],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intlify%2Fh3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intlify%2Fh3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intlify%2Fh3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/intlify%2Fh3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/intlify","download_url":"https://codeload.github.com/intlify/h3/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247019022,"owners_count":20870122,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["h3","i18n","internationalization","intlify","middleware","utilities"],"created_at":"2024-11-17T21:40:27.372Z","updated_at":"2025-04-05T06:06:04.975Z","avatar_url":"https://github.com/intlify.png","language":"TypeScript","funding_links":["https://github.com/sponsors/kazupon"],"categories":[],"sub_categories":[],"readme":"# @intlify/h3\n\n[![npm version][npm-version-src]][npm-version-href]\n[![npm downloads][npm-downloads-src]][npm-downloads-href]\n[![CI][ci-src]][ci-href]\n\nInternationalization middleware \u0026 utilities for h3\n\n## 🌟 Features\n\n✅️ \u0026nbsp;**Translation:** Simple API like\n[vue-i18n](https://vue-i18n.intlify.dev/)\n\n✅ \u0026nbsp;**Custom locale detector:** You can implement your own locale detector\non server-side\n\n✅️️ \u0026nbsp;**Useful utilities:** support internationalization composables\nutilities via [@intlify/utils](https://github.com/intlify/utils)\n\n## 💿 Installation\n\n```sh\n# Using npm\nnpm install @intlify/h3\n\n# Using yarn\nyarn add @intlify/h3\n\n# Using pnpm\npnpm add @intlify/h3\n\n# Using bun\nbun add @intlify/h3\n```\n\n## 🚀 Usage\n\n```ts\nimport { createServer } from 'node:http'\nimport { createApp, createRouter, eventHandler, toNodeListener } from 'h3'\nimport {\n  defineI18nMiddleware,\n  detectLocaleFromAcceptLanguageHeader,\n  useTranslation,\n} from '@intlify/h3'\n\n// define middleware with vue-i18n like options\nconst middleware = defineI18nMiddleware({\n  // detect locale with `accept-language` header\n  locale: detectLocaleFromAcceptLanguageHeader,\n  // resource messages\n  messages: {\n    en: {\n      hello: 'Hello {name}!',\n    },\n    ja: {\n      hello: 'こんにちは、{name}！',\n    },\n  },\n  // something options\n  // ...\n})\n\n// install middleware with `createApp` option\nconst app = createApp({ ...middleware })\n\nconst router = createRouter()\nrouter.get(\n  '/',\n  eventHandler(async (event) =\u003e {\n    // use `useTranslation` in event handler\n    const t = await useTranslation(event)\n    return t('hello', { name: 'h3' })\n  }),\n)\n\napp.use(router)\ncreateServer(toNodeListener(app)).listen(3000)\n```\n\n## 🛠️ Custom locale detection\n\nYou can detect locale with your custom logic from current `H3Event`.\n\nexample for detecting locale from url query:\n\n```ts\nimport { defineI18nMiddleware, getQueryLocale } from '@intlify/h3'\nimport type { H3Event } from 'h3'\n\n// define custom locale detector\nconst localeDetector = (event: H3Event): string =\u003e {\n  return getQueryLocale(event).toString()\n}\n\nconst middleware = defineI18nMiddleware({\n  // set your custom locale detector\n  locale: localeDetector,\n  // something options\n  // ...\n})\n```\n\nYou can make that function asynchronous. This is useful when loading resources along with locale detection.\n\n\u003e [!NOTE]\n\u003e The case which a synchronous function returns a promise is not supported. you need to use `async function`.\n\n```ts\nimport { defineI18nMiddleware, getQueryLocale } from '@intlify/h3'\nimport type { DefineLocaleMessage } from '@intlify/h3'\nimport type { H3Event } from 'h3'\n\nconst loader = (path: string) =\u003e import(path).then((m) =\u003e m.default || m)\nconst messages: Record\u003cstring, () =\u003e ReturnType\u003ctypeof loader\u003e\u003e = {\n  en: () =\u003e loader('./locales/en.json'),\n  ja: () =\u003e loader('./locales/ja.json'),\n}\n\n// define custom locale detector and lazy loading\nconst localeDetector = async (event: H3Event, i18n: CoreContext\u003cstring, DefineLocaleMessage\u003e): Promise\u003cstring\u003e =\u003e {\n  // detect locale\n  const locale = getCookieLocale(event).toString()\n\n  // resource lazy loading\n  const loader = messages[locale]\n  if (loader \u0026\u0026 !i18n.messages[locale]) {\n    const message = await loader()\n    i18n.messages[locale] = message\n  }\n\n  return locale\n}\n\nconst middleware = defineI18nMiddleware({\n  // set your custom locale detector\n  locale: localeDetector,\n  // something options\n  // ...\n})\n```\n\n\n## 🧩 Type-safe resources\n\n\u003e [!WARNING]\n\u003e **This is experimental feature (inspired from [vue-i18n](https://vue-i18n.intlify.dev/guide/advanced/typescript.html#typescript-support)).**\n\u003e We would like to get feedback from you 🙂.\n\n\u003e [!NOTE]\n\u003e The exeample code is [here](https://github.com/intlify/h3/tree/main/playground/typesafe-schema)\n\nYou can support the type-safe resources with schema using TypeScript on `defineI18nMiddleware` options.\n\nLocale messages resource:\n\n```ts\nexport default {\n  hello: 'hello, {name}!'\n}\n```\n\nyour application code:\n\n```ts\nimport { defineI18nMiddleware } from '@intlify/h3'\nimport { createApp } from 'h3'\nimport en from './locales/en.ts'\n\n// define resource schema, as 'en' is master resource schema\ntype ResourceSchema = typeof en\n\nconst middleware = defineI18nMiddleware\u003c[ResourceSchema], 'en' | 'ja'\u003e({\n  messages: {\n    en: { hello: 'Hello, {name}' },\n  },\n  // something options\n  // ...\n})\n\nconst app = createApp({ ...middleware })\n// someting your implementation code ...\n// ...\n```\n\nResult of type checking with `tsc`:\n\n```sh\nnpx tsc --noEmit\nindex.ts:13:3 - error TS2741: Property 'ja' is missing in type '{ en: { hello: string; }; }' but required in type '{ en: ResourceSchema; ja: ResourceSchema; }'.\n\n13   messages: {\n     ~~~~~~~~\n\n  ../../node_modules/@intlify/core/node_modules/@intlify/core-base/dist/core-base.d.ts:125:5\n    125     messages?: {\n            ~~~~~~~~\n    The expected type comes from property 'messages' which is declared here on type 'CoreOptions\u003cstring, { message: ResourceSchema; datetime: DateTimeFormat; number: NumberFormat; }, { messages: \"en\"; datetimeFormats: \"en\"; numberFormats: \"en\"; } | { ...; }, ... 8 more ..., NumberFormats\u003c...\u003e\u003e'\n\n\nFound 1 error in index.ts:13\n```\n\nIf 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.\n\n![Type-safe resources](assets/typesafe-schema.png)\n\n\n## 🖌️ Resource keys completion\n\n\u003e [!WARNING]\n\u003e **This is experimental feature (inspired from [vue-i18n](https://vue-i18n.intlify.dev/guide/advanced/typescript.html#typescript-support)).**\n\u003e We would like to get feedback from you 🙂.\n\n\u003e [!NOTE]\n\u003e Resource Keys completion can be used if you are using [Visual Studio Code](https://code.visualstudio.com/)\n\nYou can completion resources key on translation function with `useTranslation`.\n\n![Key Completion](assets/key-completion.gif)\n\nresource keys completion has twe ways.\n\n### Type parameter for `useTranslation`\n\n\u003e [!NOTE]\n\u003e The exeample code is [here](https://github.com/intlify/h3/tree/main/playground/local-schema)\n\nYou can `useTranslation` set the type parameter to the resource schema you want to key completion of the translation function.\n\nthe part of example:\n```ts\nconst router = createRouter()\nrouter.get(\n  '/',\n  eventHandler(async (event) =\u003e {\n    type ResourceSchema = {\n      hello: string\n    }\n    // set resource schema as type parameter\n    const t = await useTranslation\u003cResourceSchema\u003e(event)\n    // you can completion when you type `t('`\n    return t('hello', { name: 'h3' })\n  }),\n)\n```\n\n### global resource schema with `declare module '@intlify/h3'`\n\n\u003e [!NOTE]\n\u003e The exeample code is [here](https://github.com/intlify/h3/tree/main/playground/global-schema)\n\nYou can do resource key completion with the translation function using the typescript `declare module`.\n\nthe part of example:\n```ts\nimport en from './locales/en.ts'\n\n// 'en' resource is master schema\ntype ResourceSchema = typeof en\n\n// you can put the type extending with `declare module` as global resource schema\ndeclare module '@intlify/h3' {\n  // extend `DefineLocaleMessage` with `ResourceSchema`\n  export interface DefineLocaleMessage extends ResourceSchema {}\n}\n\nconst router = createRouter()\nrouter.get(\n  '/',\n  eventHandler(async (event) =\u003e {\n    const t = await useTranslation(event)\n    // you can completion when you type `t('`\n    return t('hello', { name: 'h3' })\n  }),\n)\n\n```\n\nThe advantage of this way is that it is not necessary to specify the resource schema in the `useTranslation` type parameter.\n\n\n## 🛠️ Utilites \u0026 Helpers\n\n`@intlify/h3` has a concept of composable utilities \u0026 helpers.\n\n### Utilities\n\n`@intlify/h3` composable utilities accept event (from\n`eventHandler((event) =\u003e {})`) as their first argument. (Exclude `useTranslation`) return the [`Intl.Locale`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale)\n\n### Translations\n\n- `useTranslation(event)`: use translation function, asynchronous\n\n### Headers\n\n- `getHeaderLocale(event, options)`: get locale from `accept-language` header\n- `getHeaderLocales(event, options)`: get some locales from `accept-language` header\n- `tryHeaderLocale(event, options)`: try to get locale from `accept-language` header\n- `tryHeaderLocales(event, options)`: try to get some locales from `accept-language` header\n\n### Cookies\n\n- `getCookieLocale(event, options)`: get locale from cookie\n- `tryCookieLocale(event, options)`: try to get locale from cookie\n- `setCookieLocale(event, options)`: set locale to cookie\n\n### Misc\n\n- `getPathLocale(event, options)`: get locale from path\n- `tryPathLocale(event, options)`: try to get locale from path\n- `getQueryLocale(event, options)`: get locale from query\n- `tryQueryLocale(event, options)`: try to get locale from query\n\n## Helpers\n\n- `detectLocaleFromAcceptLanguageHeader(event)`: detect locale from `accept-language` header\n\n## 🙌 Contributing guidelines\n\nIf you are interested in contributing to `@intlify/h3`, 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.\n\n## ©️ License\n\n[MIT](http://opensource.org/licenses/MIT)\n\n\u003c!-- Badges --\u003e\n\n[npm-version-src]: https://img.shields.io/npm/v/@intlify/h3?style=flat\u0026colorA=18181B\u0026colorB=FFAD33\n[npm-version-href]: https://npmjs.com/package/@intlify/h3\n[npm-downloads-src]: https://img.shields.io/npm/dm/@intlify/h3?style=flat\u0026colorA=18181B\u0026colorB=FFAD33\n[npm-downloads-href]: https://npmjs.com/package/@intlify/h3\n[ci-src]: https://github.com/intlify/utils/actions/workflows/ci.yml/badge.svg\n[ci-href]: https://github.com/intlify/utils/actions/workflows/ci.yml\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fintlify%2Fh3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fintlify%2Fh3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fintlify%2Fh3/lists"}