Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Tests
on:
workflow_dispatch:
pull_request:
types: [synchronize]
types: [opened,reopened,synchronize]
push:
branches:
- master
Expand Down
9 changes: 6 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,17 @@ You can obtain and set current locale using this writable store.
### `locales`: __Readable<string[]>__
Readable store, containing all instance locales.

### `rawTranslations`: __Readable\<{ [locale: string]: { [key: string]: string; } }> & { get: () => string; }__
Readable store, containing all loaded translations before it gets preprocessed.

### `translations`: __Readable\<{ [locale: string]: { [key: string]: string; } }> & { get: () => string; }__
Readable store, containing all loaded translations in dot-notation format.
Readable store, containing all preprocessed translations.

### `t`: __Readable<(key: string, vars?: Record<any, any>) => string> & { get: (key: string; vars?: Record<any, any>) => string; }__
This readable store returns a function you can use to obtain your (previously loaded) translations for given translation key and interpolation variables (you can use it like `$t('my.key', { variable: 'value' })` in Svelte files). You can also use `t.get` method to get the translation (e.g. `t.get('my.key', { variable: 'value' })`), which is handy in `.js` (or `.ts`) files.
This readable store returns a function you can use to obtain your (previously loaded) translations for given translation key and interpolation variables (you can use it like `$t('my.key', { variable: 'value' })` in Svelte files). You can also use `t.get` method to get the translation (e.g. `t.get('my.key', { variable: 'value' })`), which is useful in `.js` (or `.ts`) files.

### `l`: __Readable<(locale: string, key: string, vars?: Record<any, any>) => string> & { get: (locale: string, key: string, vars?: Record<any, any>) => string; }__
This readable store returns a function you can use to obtain your (previously loaded) translations for given locale, translation key and interpolation variables (you can use it like `$l('en', 'my.key', { variable: 'value' })` in Svelte files). You can also use `l.get` method to get the translation (e.g. `l.get('en', 'my.key', { variable: 'value' })`), which is handy in `.js` (or `.ts`) files.
This readable store returns a function you can use to obtain your (previously loaded) translations for given locale, translation key and interpolation variables (you can use it like `$l('en', 'my.key', { variable: 'value' })` in Svelte files). You can also use `l.get` method to get the translation (e.g. `l.get('en', 'my.key', { variable: 'value' })`), which is useful in `.js` (or `.ts`) files.

### `loadConfig`: __(config: Config) => Promise\<void>__
You can load a new `config` using this method.
Expand Down
17 changes: 16 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export default class I18n<ParserParams extends Parser.Params = any> {
get: () => get(this.isLoading),
};

private privateRawTranslations: Writable<Translations.SerializedTranslations> = writable({});

rawTranslations: ExtendedStore<Translations.SerializedTranslations> = { subscribe: this.privateRawTranslations.subscribe, get: () => get(this.rawTranslations) };

private privateTranslations: Writable<Translations.SerializedTranslations> = writable({});

translations: ExtendedStore<Translations.SerializedTranslations> = { subscribe: this.privateTranslations.subscribe, get: () => get(this.translations) };
Expand Down Expand Up @@ -235,7 +239,7 @@ export default class I18n<ParserParams extends Parser.Params = any> {
) || (
fallbackLocale && locale === sanitizedFallbackLocale && (
!translationForFallbackLocale ||
!(this.loadedKeys[sanitizedFallbackLocale] || []).includes(key)
!(this.loadedKeys[sanitizedFallbackLocale] || []).includes(key)
)),
);

Expand Down Expand Up @@ -277,6 +281,17 @@ export default class I18n<ParserParams extends Parser.Params = any> {

const translationLocales = Object.keys(translations || {});

this.privateRawTranslations.update(($rawTranslations) => translationLocales.reduce(
(acc, locale) => ({
...acc,
[locale]: {
...(acc[locale] || {}),
...translations[locale],
},
}),
$rawTranslations,
));

this.privateTranslations.update(($translations) => translationLocales.reduce(
(acc, locale) => {
let dotnotate = true;
Expand Down
4 changes: 2 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ export const sanitizeLocales = (...locales: any[]) => {
try {
const [sanitized] = Intl.Collator.supportedLocalesOf(locale);

if (!sanitized) throw new Error(`'${locale}' is non-standard.`);
if (!sanitized) throw new Error();

current = sanitized;
} catch (error) {
logger.warn(`Non-standard locale provided: '${locale}'. Check your 'translations' and 'loaders' in i18n config...`);
logger.warn(`'${locale}' locale is non-standard.`);
}

return current;
Expand Down
27 changes: 20 additions & 7 deletions tests/specs/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe('i18n instance', () => {
toHaveProperty('locale');
toHaveProperty('locales');
toHaveProperty('translations');
toHaveProperty('rawTranslations');
toHaveProperty('t');
toHaveProperty('l');
toHaveProperty('loadConfig');
Expand Down Expand Up @@ -175,15 +176,25 @@ describe('i18n instance', () => {

expect($initialized).toBe(false);
});
it('`addTranslations` method works', async () => {
it('`addTranslations` method adds raw translations', async () => {
const { addTranslations, rawTranslations } = new i18n();

const translations = getTranslations('none');

addTranslations(translations);

const $rawTranslations = rawTranslations.get();

expect($rawTranslations).toStrictEqual(translations);
});
it('`addTranslations` method adds preprocessed translations', async () => {
const { addTranslations, translations } = new i18n();

addTranslations(TRANSLATIONS);
addTranslations(getTranslations('none'));

const $translations = translations.get();

expect($translations).toEqual(
expect.objectContaining(TRANSLATIONS),
);
expect($translations).toStrictEqual(TRANSLATIONS);
});
it('`addTranslations` prevents duplicit load', async () => {
const { addTranslations, loadTranslations, loading } = new i18n({ loaders, parser, log });
Expand Down Expand Up @@ -212,12 +223,14 @@ describe('i18n instance', () => {
expect($translations[initLocale]['common.preprocess'][0].test).toBe('passed');
});
it('`preprocess` works when set to `none`', async () => {
const { loadTranslations, translations } = new i18n({ loaders, parser, log, preprocess: 'none' });
const { loadTranslations, translations, rawTranslations } = new i18n({ loaders, parser, log, preprocess: 'none' });

await loadTranslations(initLocale);

const $translations = translations.get();
const $rawTranslations = rawTranslations.get();

expect($translations).toStrictEqual($rawTranslations);
expect($translations[initLocale].common.preprocess[0].test).toBe('passed');
});
it('initializes properly with `initLocale`', async () => {
Expand Down Expand Up @@ -401,6 +414,6 @@ describe('i18n instance', () => {
await loading.toPromise();

expect(debug).toHaveBeenCalledWith('[PREFIX] Setting config.');
expect(warn).toHaveBeenCalledWith("[PREFIX] Non-standard locale provided: 'unknown'. Check your 'translations' and 'loaders' in i18n config...");
expect(warn).toHaveBeenCalledWith("[PREFIX] 'unknown' locale is non-standard.");
});
});