-
-
Notifications
You must be signed in to change notification settings - Fork 438
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[builtin middleware] Accept Language (i18n?) #1897
Comments
If there is more Accept Header type middleware in the future, it might be better to produce the parser internally. like:
|
related: #1792 |
I understand that you want this feature. Besides, other frameworks have similar i18n features. But, I would like to know the specific use case of using i18n features when building an application with Hono. |
Sure. For middleware path redirectingFor example, Vitepress recommends that when designing a directory split per Local, the middleware (proxy) layer should be configured to specify the lang and redirect. https://vitepress.dev/guide/i18n#separate-directory-for-each-locale const supportedLangs = ['en', 'ja']
app.use(
'*',
i18n({
defaultLang: 'en',
supportedLangs,
})
)
app.get('/*', async (c) => {
const path = c.req.path
const firstPath = path.split('/')[1]
if (supportedLangs.includes(firstPath)) {
return await fetch(path)
}
return c.redirect(`/${c.var.lang}${path}`)
}) For MAP's Context (hono/jsx SSR)import { createContext, useContext } from 'hono/jsx'
const MESSAGE_MAP = {
en: {
hello: 'Hello',
},
ja: {
hello: 'こんにちは',
},
}
const i18nContext = createContext({ lang: 'en' })
const TopPage = () => {
const { lang } = useContext(i18nContext)
return (
<div>
<h1>{MESSAGE_MAP[lang].hello}</h1>
</div>
)
}
app.use(
'*',
i18n({
defaultLang: 'en',
supportedLangs: ['en', 'ja'],
})
)
app.get('/', async (c) => {
return c.render(
<i18nContext.Provider value={{ lang: c.var.lang }}>
<TopPage />
</i18nContext.Provider>
)
}) |
Hi @sor4chi Thanks! I understood well. This is my opinion, but rather than creating i18n, it would be better to create a helper that parses The Issue mentioned by @humphd in #1792 can also be solved. Like this: import { matchAccept } from 'hono/accept-parser'
app.get('/welcome', (c) => {
const mimeType = matchAccept(c, {
header: 'accept',
supports: [/^application\/json/, /^text\/html/],
})
if (mimeType.startsWith('application/json')) {
return c.json({ message: 'Welcome!' })
}
if (mimeType.startsWith('text/html')) {
return c.html('<h1>Welcome!</h1>')
}
// Fallback
return c.text('Welcome!')
}) Using middleware pattern: app.use('*', async (c, next) => {
const lang = matchAccept(c, {
header: 'accept-language',
supports: ['en', 'ja', 'fr-CH'],
default: 'en',
})
c.set('lang', lang)
await next()
}) What do you think? |
Hi, @yusukebe Almost I agree. As I mentioned in the beginning, I also thought the policy of creating a Parser for Accept looked good, so that would certainly be sufficient. If no one has worked on it yet, I'll do this! |
Before starting implementation, how about investigating other frameworks to see if they have parsers for Accept headers? |
Okay, Express has Koa has some simular interfaces with Express. Fastify, Elysia, does not appear to have an internal parser. |
Cool! I don't have an idea right now, but we might make Accepts Middleware or Accepts Helper. If you have a good idea, please make a PR. Then, let's discuss it together. |
I personally like this interface and would like to promote it. const lang = matchAccept(c, {
header: 'accept-language',
supports: ['en', 'ja', 'fr-CH'],
default: 'en',
}) According to the RFC, in addition to quality, there may be other modifiers (such as format and level). https://www.rfc-editor.org/rfc/rfc9110.html#name-accept SuggestionHow about providing a quality order matcher as the default, and allowing the user to decide on a custom matcher such as the following? interface Accept {
type: string
mod: Record<string, string | number>
}
// text/html;level=1;q=0.5 => { type: 'text/html', mod: { level: '1', q: '0.5' } }
const lang = matchAccept(c, {
header: 'accept-language',
supports: ['en', 'ja', 'fr-CH'],
default: 'en',
match: (accepts: Accept[], config) => {
// ...
}
}) |
Seems to be good. What should we specify for the |
The return value of the |
Please show me the code. |
const lang = matchAccept(c, {
header: "accept-language",
supports: ["en", "ja", "fr-CH"],
default: "en",
match: (accepts: Accept[], config) => {
// In the original, sort in order of high quality and return the first match from among the supported languages
// But this time, as a custom matcher, sort in order of low quality and return the first match from among the supported languages
return accepts
.sort((a, b) => (+a.mod.q > +b.mod.q ? 1 : -1))
.find((accept) => config.supports.includes(accept.type));
},
});
// input header:
// Accept-Language: en;q=0.8, fr;q=0.5, fr-CH;q=0.9, ja;q=0.7
// output:
// lang === 'fr' The |
Okay! Please create the PR. Let's discuss it there. |
What is the feature you are proposing?
For Hono MPA's DX improvement, how about providing the middleware that configures the language in option and gets judged language from
c.var.lang
The text was updated successfully, but these errors were encountered: