QuiiBz / next-international

Type-safe internationalization (i18n) for Next.js
https://next-international.vercel.app
MIT License
1.19k stars 52 forks source link

Question: possible to use next-international for fetched, dynamic content? i.e. without types #375

Open paulgrieselhuber opened 4 months ago

paulgrieselhuber commented 4 months ago

This is just a conceptual question that will help me figure out if we can rollout next-international in one project we're working on.

And by dynamic content I don't mean blog posts, I just mean a dynamic list of keys for an application which is continuing to evolve.

We are fetching translation data from our API, but next-international calls out type issues when invoking either getI18n or getScopedI18n

Screenshot 2024-03-05 at 5 45 01 PM

We could conceivably address this by adding >500 keys to the type definition, but aside from such a huge type seeming strange, it also creates a maintenance hurdle as we have to update the type any time we add a new key.

Is there a way to make next-international work with this model without needing to manually track the keys? Also, we're deploying to Vercel on the edge, so no fs to use to store an updated type definition when a new key is added.

QuiiBz commented 3 months ago

Very interesting use case, does that mean you don't want to use at all the type-safety features that provides next-international?

What does your createI18nServer function look like? I'd assume it's using fetch() instead of a dynamic import(), but just to make sure.

That's something we could ad for the v2 of next-international (#359), just need to think a bit about how to implement it.

samit4me commented 3 months ago

Had this exact same problem! Absolutely love the type-safety provided when using static string, but we have quite a bit of data stored in objects and we often map over this data to render JSX and typically build the translation key with that data which gets a TS error.

To overcome this, I've placed a thin wrapper around createI18nClient and will do the same with server that looks something like this:

"use client";
import { createI18nClient } from "next-international/client";

export const {
    useI18n,
    useScopedI18n,
    I18nProviderClient,
    useChangeLocale,
    useCurrentLocale,
} = createI18nClient({
    en: () => import("./en"),
    de: () => import("./de"),
});

// Hacks so scopedT can accept dynamic strings without TS errors
type Params = Record<string, string>;
export function useI18nUntyped() {
    const scopedT = useI18n();
    // @ts-expect-error Argument of type is not assignable
    return (key: string, params?: Params) => scopedT(key, params);
}

type ScopeParams = Parameters<typeof useScopedI18n>[0];
export function useScopedI18nUntyped<Scope extends ScopeParams>(scope: Scope) {
    const scopedT = useScopedI18n(scope);
    // @ts-expect-error Argument of type is not assignable
    return (key: string, params?: Params) => scopedT(key, params);
}

This then allows us to do stuff like this useless example

image