🛠️ VitNode is still in development! You can try it out, but it is not recommended to use it now in production.
Frontend
Internationalization (i18n)

Internationalization (i18n)

VitNode uses not only native i18n form next-intl (opens in a new tab), but also translations from database. It gives your full control for your translations without restart your server.

Each translations are stored in database as array.

[
  {
    language_code: "en",
    value: "Hello world"
  },
  {
    language_code: "pl",
    value: "Witaj świecie"
  }
];

Display text

To display text you can use useTextLang hook.

import { useTextLang } from "@/hooks/core/use-text-lang";
 
const { convertText } = useTextLang();
 
const text = convertText(value);

value is TextLanguage[] interface form GraphQL backend.

Form

Create form with translations is very similar to normal Forms.

Define schema

import * as z from "zod";
 
import { zodInput } from "@/functions/zod";
 
const formSchema = z.object({
  name: zodInput.languageInput
});

You can add more functions to validate translations.

import * as z from "zod";
 
import { zodInput } from "@/functions/zod";
 
const formSchema = z.object({
  name: z
    .array(
      z.object({
        language_code: zodInput.string,
        value: zodInput.string.min(3).max(50)
      })
    )
    .min(1)
});

We're using zodInput.string instead of z.string to add more functions to validate translations.

Set initial values

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
 
const form = useForm<z.infer<typeof formSchema>>({
  resolver: zodResolver(formSchema),
  defaultValues: {
    name: []
  }
});

Choose input

import { TextLanguageInput } from "@/components/text-language-input";
 
<TextLanguageInput {...field} />;

Create form field

<Form {...form}>
  <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
    <FormField
      control={form.control}
      name="name"
      render={({ field }) => (
        <FormItem>
          <FormLabel>{t("name.label")}</FormLabel>
          <FormControl>
            <TextLanguageInput {...field} />
          </FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
 
    <Button
      disabled={!form.formState.isValid}
      loading={form.formState.isSubmitting}
      type="submit"
    >
      {tCore("create")}
    </Button>
  </form>
</Form>