next-international icon indicating copy to clipboard operation
next-international copied to clipboard

Lot of potential but too strongly bounded by nextjs.

Open akashraj9828 opened this issue 2 years ago • 6 comments

I want to use it in a react native project but just because it's using GetStaticProps and Router from next hence it can't be used anywhere except the next js project.

I see a lot of potential in this library if it was not strongly bounded to one library. This can easily work in any react based project.

U can provide optional "no configuration" and "automatic binding" for the next js as an extension for this library.

Thoughts?

akashraj9828 avatar Jul 27 '22 07:07 akashraj9828

There are a few reasons why this library currently only works with Next.js:

  • getStaticProps allows to serve content in multiple languages while having good Web Vitals and SEO, although I think crawlers can execute JavaScript and so load the right locale client-side; but that means higher Cumulative Layout Shift / First Input Delay.
  • Next.js already offers a very good i18n routing with almost no configuration, and this library uses the same configuration to make developers' life easier.

I've been already thinking about how this could be ported to be framework agnostic, but that's kinda hard to achieve right now. We'll have to split the library into a "core" package, then another package for each framework to allow SSR / plug to the framework's i18n routing (and what happens if a framework doesn't provide i18n routing?). That's definitely something I want to implement in the future.

QuiiBz avatar Jul 27 '22 08:07 QuiiBz

I feel like the core functionality that is providing a type-safe utility to manage language bundles in react project can be easily extracted and quickly made framework agnostic by just exposing the useI18n hook independently and exposing some functions from inside it like changeLocale

I did use some of your types to extend the https://www.npmjs.com/package/i18n-js/v/next library to introduce strict type checking and extracting parameters out of strings (awesome work on that thou 🎉).

I'm glad u were thinking of making this framework agnostic anyways good luck on that.

Keeping this thread open for now. If you ever work on this feature. Feel free to update this thread for others to follow.

akashraj9828 avatar Jul 27 '22 17:07 akashraj9828

Agree that I18nProvider & useI18n can be easily extracted into a standalone package. But one of the goals of this library is to have everything ready-to-go, with minimal/no extra configuration. I'll update the issue when we start working on making next-international framework agnostic!

QuiiBz avatar Jul 28 '22 11:07 QuiiBz

I believe the idea behind this package is really nice, and thanks to your trick for parameter extraction I was able to fill in the gaps for my own implementation.

I'd highly suggest separating the types, helpers, etc. into a separate package and build next-international on top of it. This way people would be able to use your ts magic in a standalone way.

Below I'm sharing some types that I wrote for my own needs, as an experiment, you might find it useful. I've learned parameter extraction from your library - thanks for that!

type SourceKey = string | number
type Source = Record<SourceKey, any>

type InferSourceKeys<TSource extends Source> = Exclude<
  Extract<keyof TSource, SourceKey>,
  keyof any[]
>

type InferSourceNestedKeys<
  TSource extends Source,
  TKey extends SourceKey = InferSourceKeys<TSource>
> = TKey extends SourceKey
  ? TSource[TKey] extends Source
    ? `${TKey}` | `${TKey}.${InferSourceNestedKeys<TSource[TKey]>}`
    : `${TKey}`
  : `${TKey}`

type InferSourceValue<
  TSource extends Source,
  TKey extends InferSourceNestedKeys<TSource>
> = TKey extends InferSourceKeys<TSource>
  ? TSource[TKey]
  : TKey extends `${infer TFirst}.${infer TRest}`
  ? TRest extends InferSourceNestedKeys<TSource[TFirst]>
    ? InferSourceValue<TSource[TFirst], TRest>
    : never
  : never

type InferSourceValuePlaceholderNames<TValue> = TValue extends string
  ? TValue extends ""
    ? []
    : TValue extends `${infer TStart}{${infer TPlaceholder}}${infer TRest}`
    ? [TPlaceholder, ...InferSourceValuePlaceholderNames<TRest>]
    : []
  : []

type InferSourceValuePlaceholders<
  TValue,
  TReplacement = string | number | boolean | symbol
> = Record<InferSourceValuePlaceholderNames<TValue>[number], TReplacement>

type SourceReaderFunction = <
  TSource extends Source,
  TKey extends InferSourceNestedKeys<TSource> = InferSourceNestedKeys<TSource>,
  TPlaceholderNames = InferSourceValuePlaceholderNames<
    InferSourceValue<TSource, TKey>
  >
>(
  source: TSource,
  path: TKey,
  ...placeholders: TPlaceholderNames extends any[]
    ? TPlaceholderNames["length"] extends 0
      ? []
      : [InferSourceValuePlaceholders<InferSourceValue<TSource, TKey>>]
    : []
) => InferSourceValue<TSource, TKey>

type SourceReader<TSource extends Source> = <
  TKey extends InferSourceNestedKeys<TSource> = InferSourceNestedKeys<TSource>,
  TPlaceholderNames = InferSourceValuePlaceholderNames<
    InferSourceValue<TSource, TKey>
  >
>(
  path: TKey,
  ...placeholders: TPlaceholderNames extends any[]
    ? TPlaceholderNames["length"] extends 0
      ? []
      : [InferSourceValuePlaceholders<InferSourceValue<TSource, TKey>>]
    : []
) => InferSourceValue<TSource, TKey>

const read: SourceReaderFunction = (source, key, ...placeholders) => {
  const parts = key.split(".")

  let result: any = source

  for (let i = 0; i < parts.length; i++) {
    result = result[parts[i]]

    if (result === undefined && i === parts.length - 1) {
      return key
    }
  }

  const replacements = placeholders[0]

  if (replacements) {
    Object.entries(replacements).map(
      ([key, value]) =>
        (result = result
          .toString()
          .replace(`{${key}}`, (value as any).toString()))
    )
  }

  return result
}

maximkott avatar Aug 02 '22 07:08 maximkott

Thanks for the kind words!

Yeah, splitting the types into a separate package is something that I want to do, so anyone can use them with the library they want. I think this will be part of the 1.0.0 version, along with multiple "adapters" for each React framework. Non-react frameworks are outside of the scope of this library, which strictly focuses on low-configuration and uses framework features to enhance users' experience.

QuiiBz avatar Aug 02 '22 07:08 QuiiBz

Started working on a type-specific package:

  • https://github.com/QuiiBz/next-international/pull/20

Update: international-types released on NPM and used by next-international starting 0.1.1

QuiiBz avatar Aug 02 '22 07:08 QuiiBz

Closing this issue as stale, feel free to re-open if needed.

QuiiBz avatar Jul 28 '23 11:07 QuiiBz