npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@execonline-inc/translations

v9.17.0

Published

Support for typed and interpolatable translations

Downloads

12

Readme

translations

The translations package provides a library for support of typed translation keys and typed React-based translation interpolation.

Usage

This library uses the i18next library underneath a custom adapter. The i18next library handles translating, pluralizing, and basic interpolation (using {{abc}} placeholders).

The custom adapter implementation handles type-checking and component interpolation (using <abc/> and <abc>...</abc> placeholders).

All translation content must be added to the translation files whose location is specified with the loadPath configuration which is passed to i18next.

See the translations function for integrating the library into an application. Create a translations module in the application and export the relevant things.

Types

Components

AlreadyTranslated

This component represents content that has already been translated (such as pre-translated content from a backend). Its use in a component tree helps to distinguish content that has been addressed for translation with content that has not.

import { AlreadyTranslated, AlreadyTranslatedText } from '@execonline-inc/translations';

const text: AlreadyTranslatedText = { kind: 'already-translated-text', text: 'content' };
const component = <AlreadyTranslated content={text} />;

L

This component will localize a date/time according to a given format.

import { LocalizationFormat, Localizeable, translations } from '@execonline-inc/translations';
const { L } = translations<...>(...) // see `translations` documentation

const localizeable: Localizeable = new Date();
const format: LocalizationFormat = 'long-date-and-time';

const component = <L localizeable={localizeable} format={format} />;

T

This component performs all available translation features given a typed translation definition via its props.

import { translations } from '@execonline-inc/translations';
const { T } = translations<...>(...) // see `translations` documentation

const component = <T kind="something to translate" />;

NotTranslated

Some content is not suitable for translation. Some examples of this are:

  • proper names (though there may be cases where they can be represented in the target language)
  • email addresses
  • URLs

Rendering this type of content directly in component trees may be confused with content that hasn't been addressed for translation yet, so wrapping it in this component makes it clear that it has been considered for translation.

import { NotTranslated } from '@execonline-inc/translations';

const text: string = 'not translatable';
const component = <NotTranslated text={text} />;

TranslationsContext

This component is an implementation of a React Context whose data is a TranslationsState.

import { TranslationsContext, TranslationsState } from '@execonline-inc/translations';

const state: TranslationsState = { kind: 'uninitialized' };
const consumer = (ts: TranslationsState): React.ReactNode => <>{ts.kind}</>;

const Example: React.FC = () => (
  <TranslationsContext.Provider value={state}>
    <TranslationsContext.Consumer>{consumer}</TranslationsContext.Consumer>
  </TranslationsContext.Provider>
);

TranslationsLoader

This component loads the the translations when it is mounted and wraps its children in a TranslationsContext.Provider. It renders the component passed as the loading prop until the translations are loaded to prevent the normal child components from rendering without content (because it's awaiting translation) and eventually appearing suddenly.

import { translations, TranslationsLoader } from '@execonline-inc/translations';
const { loader } = translations<...>(...) // see `translations` documentation

const component = <TranslationsLoader loader={loader} loading={<></>} />;

Functions

alreadyTranslatedText

This decoder decodes to an AlreadyTranslatedText object from a string.

import { alreadyTranslatedText, AlreadyTranslatedText } from '@execonline-inc/translations';

const decoder: Decoder<AlreadyTranslatedText> = alreadyTranslatedText;
const result: Result<string, AlreadyTranslatedText> = decoder.decodeAny('content');

defaultSettings

These are default settings for i18next based on ExecOnline's use of the library.

initTask

This function will return a Task to initialize i18next. It provides the opportunity for i18next to be configured as necessary by the user of this library.

import { initTask, TranslationsLoader } from '@execonline-inc/translations';
import i18next from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import HttpApi from 'i18next-http-backend';

const i18nextSettings = {
  /* ... */
};

const i18nextWithModules = i18next.use(HttpApi).use(LanguageDetector);

const loader = initTask(i18nextWithModules, i18nextSettings);

<TranslationsLoader loader={loader} loading={<></>} />;

localization

This function will localize a date/time for a given language and according to a given format.

import { localization, LocalizationFormat, Localizeable } from '@execonline-inc/translations';

const localizeable: Localizeable = new Date();
const format: LocalizationFormat = 'long-date-and-time';
const language = 'en-US';

const result = localization(localizeable, format, language);

localizer

This curried function will localize a date/time according to a given format under certain translation mapping load states.

When the state is loaded or loaded-from-fallback, localization occurs via a call to localization.

When the state is uninitialized, the localizeable argument is converted to a string.

import {
  localizer,
  LocalizationFormat,
  Localizeable,
  TranslationsState,
} from '@execonline-inc/translations';

const localizeable: Localizeable = new Date();
const format: LocalizationFormat = 'long-date-and-time';
const state: TranslationsState = { kind: 'uninitialized' };

const result: string = localizer(localizeable, format)(state);

translation

This curried function performs the lookup in the translation mapping file and simple interpolation when appropriate based on the text to be translated and the state of the mapping.

When the state is loaded or loaded-from-fallback, the mapped translation text is returned. If the text is found to be "not translatable", then it is returned without translation.

When the state is uninitialized, an empty string is returned.

import { translations, TranslationsState } from '@execonline-inc/translations';
const { translation } = translations<...>(...) // see `translations` documentation

const state: TranslationsState = { kind: 'uninitialized' };
const result: string = translation('something to translate', {})(state);

translator

This curried function performs all available translation features given a typed translation definition and the mapping load state.

import { translations, TranslationsState } from '@execonline-inc/translations';
const { translator } = translations<...>(...) // see `translations` documentation

const state: TranslationsState = { kind: 'uninitialized' };
const result: React.ReactNode = translator({ kind: 'something to translate' })(state);

translations

This function returns an object of functions and components that are typed according to the translation types provided to the translations function.

import {
  Config,
  translations,
  interpolator,
  Interpolator,
  parameterized,
  Parameterized,
  ParameterizedFn,
  scalar,
} from '@execonline-inc/translations';
import i18next, * as i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

const loader = initTask(i18next.use(LanguageDetector), {
  ...defaultSettings,
  backend: {
    loadPath: 'public/locales/{{lng}}/{{ns}}.json',
  },
});

const translatablePlainTextKeys = ['something to translate', 'something else'] as const;
const notTranslatable = ['[email protected]', 'https://example.com/'] as const;

type TranslatablePlainTextKey = typeof translatablePlainTextKeys[number];
type NotTranslatable = typeof notTranslatable[number];
type ParameterizedProps =
  | { kind: '{{count}} minutes'; count: number }
  | { kind: '<bolded>So</bolded> cool!'; bolded: Interpolator };

type PlainTextKey = TranslatablePlainTextKey | NotTranslatable;
type ParameterizedKey = ParameterizedProps['kind'];

const parameterizedValues: ParameterizedFn<ParameterizedKey, ParameterizedProps> = (
  props: ParameterizedProps
): Parameterized<ParameterizedKey, ParameterizedProps> => {
  switch (props.kind) {
    case '{{count}} minutes':
      return parameterized(props, { count: scalar(props.count) });
    case '<bolded>So</bolded> cool!':
      return parameterized(props, { bolded: interpolator(props.bolded) });
  }
};

const { L, translation, translator, T } = translations<
  PlainTextKey,
  NotTranslatable,
  ParameterizedKey,
  ParameterizedProps
>(translatablePlainTextKeys, notTranslatable, parameterizedValues);