middleware.ts 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import { defineMiddleware } from "astro:middleware"
  2. import { exactLocale, matchLocale } from "./i18n/locales"
  3. function docsAlias(pathname: string) {
  4. const hit = /^\/docs\/([^/]+)(\/.*)?$/.exec(pathname)
  5. if (!hit) return null
  6. const value = hit[1] ?? ""
  7. const tail = hit[2] ?? ""
  8. const locale = exactLocale(value)
  9. if (!locale) return null
  10. const next = locale === "root" ? `/docs${tail}` : `/docs/${locale}${tail}`
  11. if (next === pathname) return null
  12. return next
  13. }
  14. function localeFromCookie(header: string | null) {
  15. if (!header) return null
  16. const raw = header
  17. .split(";")
  18. .map((x) => x.trim())
  19. .find((x) => x.startsWith("oc_locale="))
  20. ?.slice("oc_locale=".length)
  21. if (!raw) return null
  22. return matchLocale(raw)
  23. }
  24. function localeFromAcceptLanguage(header: string | null) {
  25. if (!header) return "root"
  26. const items = header
  27. .split(",")
  28. .map((raw) => raw.trim())
  29. .filter(Boolean)
  30. .map((raw) => {
  31. const parts = raw.split(";").map((x) => x.trim())
  32. const lang = parts[0] ?? ""
  33. const q = parts
  34. .slice(1)
  35. .find((x) => x.startsWith("q="))
  36. ?.slice(2)
  37. return {
  38. lang,
  39. q: q ? Number.parseFloat(q) : 1,
  40. }
  41. })
  42. .sort((a, b) => b.q - a.q)
  43. const locale = items
  44. .map((item) => item.lang)
  45. .filter((lang) => lang && lang !== "*")
  46. .map((lang) => matchLocale(lang))
  47. .find((lang) => lang)
  48. return locale ?? "root"
  49. }
  50. export const onRequest = defineMiddleware((ctx, next) => {
  51. const alias = docsAlias(ctx.url.pathname)
  52. if (alias) {
  53. const url = new URL(ctx.request.url)
  54. url.pathname = alias
  55. return ctx.redirect(url.toString(), 302)
  56. }
  57. if (ctx.url.pathname !== "/docs" && ctx.url.pathname !== "/docs/") return next()
  58. const locale =
  59. localeFromCookie(ctx.request.headers.get("cookie")) ??
  60. localeFromAcceptLanguage(ctx.request.headers.get("accept-language"))
  61. if (!locale || locale === "root") return next()
  62. const url = new URL(ctx.request.url)
  63. url.pathname = `/docs/${locale}/`
  64. return ctx.redirect(url.toString(), 302)
  65. })