Failing to "dynamically" import date-fns/locale libs - TypeScript giving an attempted import error
Asked Answered
Z

2

11

I have an app that receives a list of supported locales from the backend as a following response:

{locales: [{code: 'enUS'}, {code: 'deDE'}, {code: 'arAR'}]}

I want to use date-fns library for handling date formatting but I have to import the whole date-fns/locale as I can't know beforehand which locale will be needed:

import * as dateFnsLocales from 'date-fns/locale';

The problem is, some of the locales are in different code format (for example, support for deutsch language is enabled when the backend response includes code: 'deDE', but the corresponding date-fns package is just 'de'. On the other hand date-fns package for english is 'enUS', not just 'en'.

Easy solution imho would be to handle it with some coalescing operator. The example is following:

import * as dateFnsLocales from 'date-fns/locale';

const supportedLocales = {locales: [{code: 'enUS'}, {code: 'deDE'}, {code: 'plPL'}]}
const newArrayWithSupportedLocales = supportedLocales.locales.map((locale) => ({
        ...locale,
        dateFnsLocale: (dateFnsLocales[locale.code] || dateFnsLocales[locale.code.substring(0,2)]),
      }));

Unfortunately I get the typescript error: No index signature with a parameter of type 'string' was found on type 'typeof import("date-fns/locale")'. TS7053

Even if I hardcode the attempt like so:

dateFnsLocale: dateFnsLocales['plPL'.substring(0,2)]

it fails with the same error, even though this:

dateFnsLocale: dateFnsLocales['pl']

works just fine.

Zamboanga answered 16/3, 2021 at 15:10 Comment(2)
I know this is an old question and you've already solved it with the answer below using Expo's Localization, which is probably a better solution anyway. But we were using Object.entries(locales).find(([key]) => key === str)?.[1], where str could be your 'plPL'.substring(0,2) for example. (I came across this question trying to find an alternative for the import * as dateFnsLocales from 'date-fns/locale';, since it almost doubles the bundle-size in our Angular project..)Salvucci
Thanks, I don't work on this project anymore but I sent it to the former work colleague to test it. import * always bugged me thereZamboanga
M
17

Here's the code I am using for doing dynamic lookups using Expo's Localization object.

import * as Localization from 'expo-localization';
import * as Locales from 'date-fns/locale';
import { Locale } from 'date-fns';

/**
 * Looks up a date-fns locale from the Expo localization object.  This falls back to `en-US`
 * @param localization Expo Localization object containing the locale and region.
 * @returns date-fns locale.
 */
export function getDateFnsLocale({ locale, region }: Pick<typeof Localization, 'locale'|'region'>) : Locale {
  return (
    Locales[locale.substring(0, 2) + region] ?? Locales[locale.substring(0, 2)] ?? Locales.enUS
  );
}

Here's the test

import { enUS, fr, frCA } from 'date-fns/locale';

describe('date-fns locale lookup', () => {
  it('shound find fr', () => {
    expect(getDateFnsLocale({ locale: 'fr', region: null })).toBe(fr);
  });
  it('shound find fr-CA', () => {
    expect(getDateFnsLocale({ locale: 'fr-CA', region: 'CA' })).toBe(frCA);
  });
  it('shound not find zz-ZZ', () => {
    expect(getDateFnsLocale({ locale: 'zz-ZZ', region: 'ZZ' })).toBe(enUS);
  });
});
Maverick answered 21/8, 2021 at 20:36 Comment(2)
Thanks for providing the answer and the useful tests!Rascally
Annoying that you require an additional package to solve a problem date-fns should've already provided. Thank you for the solution. Worked great!Oleoresin
D
0

this is my solution

import * as loc from 'date-fns/locale';

export const getDateFnsLocaleByActiveLanguage = (lang: string) => 
  lang === 'en'
   ? loc['enUS']
   : Object.values(loc).find((l) => l.code === lang);

Dimitris answered 21/3, 2024 at 21:4 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.