UNPKG

ember-intl

Version:

A internationalization toolbox for ambitious applications.

90 lines (75 loc) 2.7 kB
import { get } from '@ember/object'; import type ComputedProperty from '@ember/object/computed'; import EmptyObject from 'ember-intl/-private/utils/empty-object'; import intl from './intl'; import type IntlService from '../services/intl'; import type { TOptions } from '../services/intl'; function partitionDynamicValuesAndStaticValues(options: Record<string, string | Raw<string>>) { const dynamicValues = new EmptyObject() as Record<string, string>; const staticValues = new EmptyObject() as Record<string, string>; Object.keys(options).forEach((key) => { const value = options[key]; if (value instanceof Raw) { staticValues[key] = value.valueOf(); } else { dynamicValues[key] = value; } }); return [dynamicValues, staticValues] as const; } function mapPropertiesByHash<O extends Record<string, unknown>, H extends Record<string, keyof O>>( object: O, hash: H ): { [K in keyof H]: O[H[K]] } { const result = new EmptyObject() as { [K in keyof H]: O[H[K]] }; (Object.keys(hash) as (keyof H)[]).forEach((key) => { result[key] = get(object, hash[key]) as O[H[typeof key]]; }); return result; } /** * This class is used to box primitive types and mark them as raw literals that * should be used as is by the translation macro. * * This class is internal. Instead of using this class directly, use the `raw` * utility function, that creates an instance of this class. */ class Raw<T> { private _value: T; constructor(value: T) { this._value = value; } valueOf() { return this._value; } toString() { return String(this._value); } } /** * Use this utility function to mark a value as a raw literal. * * @param {*} value The value to mark as a raw literal. * @return The same value, but boxed in the `Raw` class so that the consuming * macro understands that this value should be used as is. */ export function raw<T>(value: T): Raw<T> { return new Raw(value); } type MacroOptions = { [K in keyof TOptions]: TOptions[K] | Raw<TOptions[K]>; }; export default function createTranslatedComputedProperty( translationKey: string, options?: MacroOptions ): ComputedProperty<ReturnType<IntlService['t']>> { const hash = options || new EmptyObject(); const [dynamicValues, staticValues] = partitionDynamicValuesAndStaticValues( hash as Record<string, string | Raw<string>> ); const dependentKeys = Object.values(dynamicValues); // @ts-expect-error TS2556: Can't deal with leading rest arguments return intl(...dependentKeys, (intl, propertyKey, ctx) => intl.t(translationKey, { ...staticValues, ...mapPropertiesByHash(ctx as Record<string, unknown>, dynamicValues) }) ); }