Skip to content

Commit

Permalink
feat: 🎸 add $json method to get raw dicitonary values
Browse files Browse the repository at this point in the history
✅ Closes: #109, #83
  • Loading branch information
kaisermann committed Nov 23, 2020
1 parent c02009e commit ae929eb
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 101 deletions.
54 changes: 40 additions & 14 deletions docs/Formatting.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
<!-- code_chunk_output -->

- [Message syntax](#message-syntax)
- [`$format` or `$_` or `$t`](#format-or-_-or-t)
- [`$format`, `$_` or `$t`](#format-_-or-t)
- [`$time(number: Date, options: MessageObject)`](#timenumber-date-options-messageobject)
- [`$date(date: Date, options: MessageObject)`](#datedate-date-options-messageobject)
- [`$number(number: number, options: MessageObject)`](#numbernumber-number-options-messageobject)
- [`$json(messageId: string)`](#jsonmessageid-string)
- [Formats](#formats)
- [Accessing formatters directly](#accessing-formatters-directly)

Expand All @@ -20,7 +21,7 @@ Under the hood, `formatjs` is used for localizing your messages. It allows `svel
- [Runtime Environments](https://formatjs.io/docs/guides/runtime-requirements/)
- [ICU Message Syntax](https://formatjs.io/docs/core-concepts/icu-syntax/)

### `$format` or `$_` or `$t`
### `$format`, `$_` or `$t`

`import { _, t, format } from 'svelte-i18n'`

Expand All @@ -41,11 +42,11 @@ The formatter can be called with two different signatures:

```ts
interface MessageObject {
id?: string
locale?: string
format?: string
default?: string
values?: Record<string, string | number | Date>
id?: string;
locale?: string;
format?: string;
default?: string;
values?: Record<string, string | number | Date>;
}
```

Expand All @@ -56,6 +57,7 @@ interface MessageObject {
- `values`: properties that should be interpolated in the message;

You can pass a `string` as the first parameter for a less verbose way of formatting a message. It is also possible to inject values into the translation like so:

```jsonc
// en.json
{
Expand Down Expand Up @@ -126,6 +128,30 @@ Formats a number with the specified locale and format. Please refer to the [#for
<!-- 100.000.000 -->
```

### `$json(messageId: string)`

`import { json } from 'svelte-i18n'`

Returns the raw JSON value of the specified `messageId` for the current locale. While [`$format`](#format-_-or-t) always returns a string, `$json` can be used to get an object relative to the current locale.

```html
<ul>
{#each $json('list.items') as item}
<li>{item.name}</li>
{/each}
</ul>
```

If you're using TypeScript, you can define the returned type as well:

```html
<ul>
{#each $json<Item[]>('list.items') as item}
<li>{item.name}</li>
{/each}
</ul>
```

### Formats

`svelte-i18n` comes with a default set of `number`, `time` and `date` formats:
Expand Down Expand Up @@ -163,24 +189,24 @@ import {
getNumberFormatter,
getTimeFormatter,
getMessageFormatter,
} from 'svelte-i18n'
} from 'svelte-i18n';
```

By using these methods, it's possible to manipulate values in a more specific way that fits your needs. For example, it's possible to create a method which receives a `date` and returns its relevant date related parts:

```js
import { getDateFormatter } from 'svelte-i18n'
import { getDateFormatter } from 'svelte-i18n';

const getDateParts = date =>
const getDateParts = (date) =>
getDateFormatter()
.formatToParts(date)
.filter(({ type }) => type !== 'literal')
.reduce((acc, { type, value }) => {
acc[type] = value
return acc
}, {})
acc[type] = value;
return acc;
}, {});

getDateParts(new Date(2020, 0, 1)) // { month: '1', day: '1', year: '2020' }
getDateParts(new Date(2020, 0, 1)); // { month: '1', day: '1', year: '2020' }
```

Check the [methods documentation](/docs/Methods.md#low-level-api) for more information.
12 changes: 8 additions & 4 deletions src/runtime/includes/lookup.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { getMessageFromDictionary } from '../stores/dictionary';
import { getFallbackOf } from '../stores/locale';

export const lookupCache: Record<string, Record<string, string>> = {};
export const lookupCache: {
[locale: string]: {
[messageId: string]: any;
};
} = {};

const addToCache = (path: string, locale: string, message: string) => {
if (!message) return message;
Expand All @@ -11,8 +15,8 @@ const addToCache = (path: string, locale: string, message: string) => {
return message;
};

const searchForMessage = (path: string, locale: string): string => {
if (locale == null) return null;
const searchForMessage = (path: string, locale: string): any => {
if (locale == null) return undefined;

const message = getMessageFromDictionary(locale, path);

Expand All @@ -32,5 +36,5 @@ export const lookup = (path: string, locale: string) => {
return addToCache(path, locale, message);
}

return null;
return undefined;
};
1 change: 1 addition & 0 deletions src/runtime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export {
$formatDate as date,
$formatNumber as number,
$formatTime as time,
$json as json,
} from './stores/formatters';

// low-level
Expand Down
31 changes: 24 additions & 7 deletions src/runtime/stores/formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
TimeFormatter,
DateFormatter,
NumberFormatter,
JSONGetter,
} from '../types';
import { lookup } from '../includes/lookup';
import { hasLocaleQueue } from '../includes/loaderQueue';
Expand Down Expand Up @@ -54,23 +55,39 @@ const formatMessage: MessageFormatter = (id, options = {}) => {
}

message = defaultValue || id;
} else if (typeof message !== 'string') {
console.error(
`[svelte-i18n] Message with id "${id}" must be of type "string", found: "${typeof message}". Gettin its value through the "$format" method is deprecated; use the "json" method instead.`,
);

return message;
}

if (!values) return message;
if (!values) {
return message;
}

return getMessageFormatter(message, locale).format(values) as string;
};

const formatTime: TimeFormatter = (t, options) =>
getTimeFormatter(options).format(t);
const formatTime: TimeFormatter = (t, options) => {
return getTimeFormatter(options).format(t);
};

const formatDate: DateFormatter = (d, options) => {
return getDateFormatter(options).format(d);
};

const formatDate: DateFormatter = (d, options) =>
getDateFormatter(options).format(d);
const formatNumber: NumberFormatter = (n, options) => {
return getNumberFormatter(options).format(n);
};

const formatNumber: NumberFormatter = (n, options) =>
getNumberFormatter(options).format(n);
const getJSON: JSONGetter = <T>(id: string, locale = getCurrentLocale()) => {
return lookup(id, locale) as T;
};

export const $format = derived([$locale, $dictionary], () => formatMessage);
export const $formatTime = derived([$locale], () => formatTime);
export const $formatDate = derived([$locale], () => formatDate);
export const $formatNumber = derived([$locale], () => formatNumber);
export const $json = derived([$locale, $dictionary], () => getJSON);
2 changes: 2 additions & 0 deletions src/runtime/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export type NumberFormatter = (
options?: IntlFormatterOptions<Intl.NumberFormatOptions>,
) => string;

export type JSONGetter = <T extends any>(id: string, locale?: string) => T;

type IntlFormatterOptions<T> = T & {
format?: string;
locale?: string;
Expand Down
7 changes: 5 additions & 2 deletions test/runtime/includes/lookup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ beforeEach(() => {
});

test('returns null if no locale was passed', () => {
expect(lookup('message.id', undefined)).toBeNull();
expect(lookup('message.id', null)).toBeNull();
expect(lookup('message.id', undefined)).toBeUndefined();
expect(lookup('message.id', null)).toBeUndefined();
});

test('gets a shallow message of a locale dictionary', () => {
Expand Down Expand Up @@ -61,6 +61,7 @@ test('gets an array ', () => {
test('caches found messages by locale', () => {
addMessages('en', { field: 'name' });
addMessages('pt', { field: 'nome' });

lookup('field', 'en-US');
lookup('field', 'pt');

Expand All @@ -73,8 +74,10 @@ test('caches found messages by locale', () => {
test("doesn't cache falsy messages", () => {
addMessages('en', { field: 'name' });
addMessages('pt', { field: 'nome' });

lookup('field_2', 'en-US');
lookup('field_2', 'pt');

expect(lookupCache).not.toMatchObject({
'en-US': { field_2: 'name' },
pt: { field_2: 'nome' },
Expand Down
Loading

0 comments on commit ae929eb

Please sign in to comment.