solidstart的i18n解决方案
solidstart发布了正式版本1.0,solidjs以性能著称,据说是能替换react的存在,而solidstart则是对应的nextjs。
这几年前端框架也可以卷起来了,一众前端框架脱颖而出,另一个值得关注的框架sveltekit,由于最新svelte 5.0的发布据说修复了4.0很多问题,而sveltekit则是对应的是react的nextjs。
这两个框架和生态都远不及react/nextjs系列,但是长远来看值得关注。
由于有个新的项目需要,目前正在测试这两个框架为了选择技术栈,由于该项目有多国语言的需要,今天测试了一下solidstart,顺便看了下i18n库,它的文档非常不成熟,用的是@solid-primitives/i18n这个库,这里记录一下实现过程,但是遗憾的是该方案没有生成出带有语言标识的URL,这对SEO很不友好,以后实现了再更新吧。
大概是i18n
这个库的文档是为了solid写的,又或者版本不对,它并没有详细的写清楚该怎么用,新手肯定会非常迷茫。
文档目录结构
每个语言目录下的ts文件
import global from './global.json'; import home from './home.json'; export const dict = { global, home, };
入口文件:
App.tsx
import { Router } from "@solidjs/router"; import { FileRoutes } from "@solidjs/start/router"; //import { Suspense } from "solid-js"; import Nav from "~/components/Nav"; import "./app.css"; import * as i18n from "@solid-primitives/i18n"; import { Suspense, createResource, createSignal, Show } from 'solid-js'; import type * as en from "./locales/en/en"; export type Locale = "en" | "zh-cn"; export type RawDictionary = typeof en.dict; export type Dictionary = i18n.Flatten<RawDictionary>; async function fetchDictionary(locale: Locale): Promise<Dictionary> { const dict: RawDictionary = (await import(`./locales/${locale}/${locale}.ts`)).dict; return i18n.flatten(dict); // flatten the dictionary to make all nested keys available top-level } function extractLocale(path: string, defaultLocale = "en") { const segments = path.split("/"); let locale = segments[1]; if (locale && locale.length === 2) { return locale; } else { return defaultLocale; } } export default function App() { const [locale, setLocale] = createSignal<Locale>("en"); const [dict] = createResource(locale, fetchDictionary); dict(); // => Dictionary | undefined // (undefined when the dictionary is not loaded yet) const t = i18n.translator(dict); return ( <Router root={props => ( <> <Nav /> <Suspense> <Show when={dict()}> {dict => { dict(); // => Dictionary (narrowed by Show) const t = i18n.translator(dict); t("home.hero"); // => string return ( <div> <p>Current locale: {locale()}</p> <div> <button onClick={() => setLocale("en")}>English</button> <button onClick={() => setLocale("zh-cn")}>简体中文</button> </div> <h4>{t("global.hello", { name: "John" })}</h4> </div> ); }} </Show> {props.children} </Suspense> </> )} > <FileRoutes /> </Router> ); }
在页面或组件中使用
import LanguageSwitcher from "~/components/LanguageSwitcher"; import * as i18n from "@solid-primitives/i18n"; import { createResource, startTransition, useContext, createSignal, Show } from 'solid-js'; import type * as en from "../locales/en/en"; export type Locale = "en" | "zh-cn"; export type RawDictionary = typeof en.dict; export type Dictionary = i18n.Flatten<RawDictionary>; async function fetchDictionary(locale: Locale): Promise<Dictionary> { const dict: RawDictionary = (await import(`../locales/${locale}/${locale}.ts`)).dict; return i18n.flatten(dict); // flatten the dictionary to make all nested keys available top-level }
最后
export default function Home() { const [locale, setLocale] = createSignal<Locale>("en"); const [commonDict] = createResource(locale, fetchDictionary); const t = i18n.translator(commonDict); return ( <main class="text-center mx-auto text-gray-700 p-4"> <p class="my-4"> <span>Home</span> {t("home.hero")} </p> </main> ); }
整体下来挺复杂的说实话。