Skip to content

Commit

Permalink
API for handling language in @withease/i18next (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
igorkamyshev authored Apr 8, 2024
1 parent 70184a0 commit 60af5db
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/poor-carrots-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@withease/i18next': minor
---

Add _Store_ `$language` and _EventCallable_ `changeLanguage` to integration
26 changes: 26 additions & 0 deletions apps/website/docs/i18next/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,32 @@ const { $isReady } = createI18nextIntegration({
});
```

#### `$language` <Badge text="since v23.2.0" />

A [_Store_](https://effector.dev/docs/api/effector/store) containing the current language.

```ts
const { $language } = createI18nextIntegration({
/* ... */
});
```

#### `changeLanguage` <Badge text="since v23.2.0" />

An [_EventCallable_](https://effector.dev/en/api/effector/event/) that can be called with a language code to change the current language.

```ts
const { changeLanguage } = createI18nextIntegration({
/* ... */
});

sample({
clock: someButtonClicked,
fn: () => 'en',
target: changeLanguage,
});
```

#### `reporting`

An object with the following fields:
Expand Down
35 changes: 34 additions & 1 deletion packages/i18next/src/integration.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
type Event,
type Store,
type EventCallable,
attach,
combine,
createEffect,
Expand Down Expand Up @@ -28,6 +29,8 @@ type I18nextIntegration = {
$t: Store<TFunction>;
translated: Translated;
$isReady: Store<boolean>;
$language: Store<string | null>;
changeLanguage: EventCallable<string>;
reporting: {
missingKey: Event<MissinKeyReport>;
};
Expand Down Expand Up @@ -80,6 +83,12 @@ export function createI18nextIntegration({
missingKey: createEvent<MissinKeyReport>(),
};

const $language = createStore<string | null>(null, { serialize: 'ignore' });

const changeLanguage = createEvent<string>();

// -- End of public API

sample({
clock: [
instanceInitialized,
Expand All @@ -89,6 +98,28 @@ export function createI18nextIntegration({
target: $stanaloneT,
});

sample({
clock: [
instanceInitialized,
sample({ clock: contextChanged, source: $instance, filter: Boolean }),
],
fn: (i18next) => i18next.language,
target: $language,
});

const changeLanguageFx = attach({
source: $instance,
async effect(instance, nextLangauge: string) {
if (!instance) {
return;
}

await instance.changeLanguage(nextLangauge);
},
});

sample({ clock: changeLanguage, target: changeLanguageFx });

sample({
clock: instanceInitialized,
fn: () => true,
Expand Down Expand Up @@ -223,12 +254,14 @@ export function createI18nextIntegration({
sample({ clock: destroy, target: destroyListenersFx });
sample({
clock: destroyListenersFx.done,
target: [$contextChangeListener.reinit!, $missingKeyListener.reinit!],
target: [$contextChangeListener.reinit, $missingKeyListener.reinit],
});

return {
$isReady,
$t,
$language,
changeLanguage,
translated: (firstArg, ...args: any[]) => {
if (typeof firstArg === 'string') {
return translatedWithVariables(firstArg, args[0]);
Expand Down
2 changes: 1 addition & 1 deletion packages/i18next/src/is_ready.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { describe, expect, test } from 'vitest';

import { createI18nextIntegration } from './integration';

describe('integration.$t', () => {
describe('integration.$isReady', () => {
test('not ready if not initialized', async () => {
const setup = createEvent();

Expand Down
43 changes: 43 additions & 0 deletions packages/i18next/src/language.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, test, expect } from 'vitest';
import { allSettled, createEvent, fork } from 'effector';
import { createInstance } from 'i18next';

import { createI18nextIntegration } from './integration';

describe('integration.$language/changeLanguage', () => {
test('change language', async () => {
const setup = createEvent();

const instance = createInstance({
resources: {
th: { common: { key: 'th value' } },
en: { common: { key: 'en value' } },
},
lng: 'th',
});

const { $language, changeLanguage, translated } = createI18nextIntegration({
instance,
setup,
});

const $val = translated('common:key');

const scope = fork();

// Before initialization
expect(scope.getState($language)).toBeNull();

await allSettled(setup, { scope });

// Initial value
expect(scope.getState($language)).toBe('th');
expect(scope.getState($val)).toBe('th value');

await allSettled(changeLanguage, { scope, params: 'en' });

// After change
expect(scope.getState($language)).toBe('en');
expect(scope.getState($val)).toBe('en value');
});
});
2 changes: 1 addition & 1 deletion packages/i18next/src/reporting.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { allSettled, createEvent, createStore, fork } from 'effector';
import { createInstance, type i18n } from 'i18next';
import { createInstance } from 'i18next';
import { describe, expect, test, vi } from 'vitest';

import { createI18nextIntegration } from './integration';
Expand Down
2 changes: 1 addition & 1 deletion packages/i18next/src/translated.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { describe, expect, test } from 'vitest';

import { createI18nextIntegration } from './integration';

describe('integration.$t', () => {
describe('integration.translated', () => {
describe('overload: key', () => {
test('supports simple key', async () => {
const instance = createInstance({
Expand Down

0 comments on commit 60af5db

Please sign in to comment.