DoneDeal0/Talkr

Handle dynamic keys with variables

Closed this issue · 4 comments

We would like to use talkr with dynamic keys using variables.

e.g.:

{
    exercises: {
        1: '...',
        2: '...',
        ...
    }
}
{T(`exercises.${getExerciseId()}`)}

The following code displays a typescript error Argument of type 'exercise.${string}' is not assignable to parameter of ...

We don't want to end up doing this since we have hundred of exercises:

switch (exerciseId) {
    case '1': return T('exercises.1');
    ...
}

What can we do ? Thanks

Hi @Aximem, I'm currently on vacation, I'll take a look when I get back next week.

I assume you are using Talkr's autocompletion to get such errors. You have two options:


Option 1

This is the option I would recommend. Modify your useAutocompleteT hook like this:

import { useT, Autocomplete, TParams, tr } from "talkr";
import en from "./en.json";

type Key = Autocomplete<typeof en>;

export const useAutocompleteT = () => {
  const { locale, setLocale, languages, defaultLanguage } = useT();
  const _tr = (key: Key, params?: TParams) =>
    tr({ locale, languages, defaultLanguage }, key, params);
  return {
    setLocale,
    locale,
    T: (key: Key, params?: TParams) => _tr(key, params),
    TConcat: (key: Key | string, params?: TParams) => _tr(key as Key, params),
  };
};

You now have two translation utilities: T (will provide autocompletion and typescript errors), and TConcat (name it whatever you want. It will allow template strings without providing auto-completion).

usage:

export default function App() {
  const { T, TConcat, locale, setLocale } = useAutocompleteT();
  return (
    <>
      <h1>{T("hello")}</h1>
      <ul>
        {["algebra", "geometry", "programming"].map((field) => {
          return <li key={field}>{TConcat(`exercises.${field}`)}</li>;
        })}
      </ul>
    </>
  );
}

Option 2

Alternatively, you can mix Talkr's useT and useAutocompleteT hooks:

export default function App() {
  const [count, setCount] = useState(0);
  const { T: TConcat } = useT();
  const { T, locale, setLocale } = useAutocompleteT();
  return (
    <>
      <h1>{T("hello")}</h1>
      <ul>
        {["algebra", "geometry", "programming"].map((field) => {
          return <li key={field}>{TConcat(`exercises.${field}`)}</li>;
        })}
      </ul>
    </>
  );
}

Thanks for your complete answer, I will go with option 1 👍

You could also cast the type of your dynamic key like this:

import { Key, useT } from "@i18n/useAutocompleteT" // export the type `Key` from your file

const getExerciseId = (exerciseId: string): Key => {
switch (exerciseId) {
    case '1': return T('exercises.1');
    default: break
 }
}

(...)

{T(`exercises.${getExerciseId()}`)}
// or directly {T(`exercises.${getExerciseId() as Key}`)}

If you are using Talkr for a commercial product, feel free to sponsor it. I would put your company logo on NPM and on the repo.