Skip to content

Commit

Permalink
docs(i18n): Add 2 examples of i18n usage with i18n-typegen
Browse files Browse the repository at this point in the history
  • Loading branch information
fdrault committed Aug 6, 2024
1 parent 1d64752 commit 65a89d4
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 30 deletions.
34 changes: 4 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,38 +113,12 @@ translate("goodbye"); // OK
- Styling without trouble your translations on React and React-Native [styled-tagged-text](https://github.com/BeTomorrow/styled-tagged-text)
- Works great with [i18n-js](https://github.com/fnando/i18n-js)

### Example of my implementation over i18n-js
### Example of implementation

```typescript
import { I18n } from "i18n-js";
import { TranslationFunction } from "translations";
// Import generated type from translations.d.ts

type MyCustomI18n = Omit<I18n, "t"> & {
t: TranslationFunction;

/**
* Same as `t` without any type checking.
* Should be used only when the translation key cannot be statically inferred.
*/
unsafeTranslate: (key: string, interpolations?: Record<string, unknown>) => string;
};
See `docs` for complete usage of type generation with some i18n implementations

class MyInternationalization extends I18n {
unsafeTranslate(key: string, interpolations?: Record<string, unknown>) {
return this.t(key, interpolations);
}
}

export const i18n = new MyInternationalization(
{
fr,
en,
},
{ locale: getUserLanguage() }
) as MyCustomI18n;
// ^ Apply my custom type to enjoy static translations and interpolations check !
```
- i18n-js [docs/i18n-js.md](docs/i18n-js.md)
- custom implemetation [docs/custom.md](docs/custom.md)

## Contribution

Expand Down
155 changes: 155 additions & 0 deletions docs/custom.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Custom i18n implementation

## Translations

`en.json`:

```json
{
"animals": {
"one": "{{count}} animal",
"other": "{{count}} animals",
"zero": "No animal"
},
"lorem": "Culpa et aliquip proident adipisicing in."
}
```

## Usage

```ts
t("animals", { count: 2 }); // typesafe
t("lorem");
```

# Custom implementation

Since i18n-typegen is a type generator only, you have to provide your own i18n solution.

Here is a very simple example of i18n implementation that take big advantages from typing:

```ts
import type { TranslationFunction } from "translations";

const logger = LoggerManager.getInstance("[🌐 I18n]");

let currentLangData = require("translations/en.json");

export const setLanguage = (data: any) => {
currentLangData = data;
};

const pluralizer = (count: number) => {
switch (count) {
case 0:
return ["zero", "one", "other"];
case 1:
return ["one", "other"];
default:
return ["other"];
}
};

const findPartsForData = (
data: any,
parts: string[],
interpolations?: { [key: string]: string | number } | null
) => {
for (let index = 0; index < parts.length; ++index) {
const part = parts[index];
if (data[part] === undefined) {
return undefined;
}
data = data[part];
}
const count = interpolations?.count;
if (typeof count === "number") {
const keys = pluralizer(count);
while (keys.length) {
const key = keys.shift() as string;
if (data[key] !== undefined) {
data = data[key];
break;
}
}
}
if (typeof data !== "string") {
if (typeof data === "number") {
return String(data);
}
return undefined;
}
return data;
};

const translate: TranslationFunction = (
path: string,
interpolations?: { [key: string]: string | number } | null
) => {
if (path === undefined || path === null) {
console.error(new Error("[🌐 I18n] Invalid translation key.").message);
return "";
}
const parts = path.split(".");
let translation = findPartsForData(currentLangData, parts, interpolations);
if (translation === undefined) {
logger.error(`[🌐 I18n] Can't find translation for "${path}"`);
return path;
}

if (interpolations) {
for (const interpolation in interpolations) {
translation = translation.replace(
`{{${interpolation}}}`,
String(interpolations[interpolation])
);
}
}
return translation;
};

const unsafeTranslate = translate as (
key: string,
interpolations?: { [key: string]: string | number }
) => string;

export { translate as t, unsafeTranslate };
```

- `t` enforce type safety
- `unsafeTranslate` can be used for dynamic keys, when type safety can be a burden

# Configuration

```json
{
"input": {
"format": "nested",
"path": "./i18n/fr.json"
},
"output": {
"path": "./i18n/translations.d.ts"
},
"rules": [
{
"//": "Add pluralization placeholders",
"condition": { "keyEndsWith": ["zero", "one", "other"] },
"transformer": {
"addPlaceholder": { "name": "count", "type": ["number"] },
"removeLastPart": true
}
},
{
"//": "Add interpolation values for matched placeholders",
"condition": { "placeholderPattern": { "prefix": "{{", "suffix": "}}" } },
"transformer": {
"addMatchedPlaceholder": { "type": ["string", "number"] }
}
}
],
"extra": {
"prettierIgnore": true,
"eslintDisablePrettier": false
}
}
```
96 changes: 96 additions & 0 deletions docs/i18n-js.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
### Example of my implementation over i18n-js

Package: [https://www.npmjs.com/package/i18n-js](https://www.npmjs.com/package/i18n-js)

```json
{
"animals": {
"one": "{{count}} animal",
"other": "{{count}} animals",
"zero": "No animal"
},
"lorem": "Culpa et aliquip proident adipisicing in."
}
```

## Usage

```ts
i18n.t("animals", { count: 2 }); // count is mandatory
i18n.t("lorem");
```

### Example of my implementation over i18n-js

```typescript
import { I18n } from "i18n-js";
import { TranslationFunction } from "translations";
// Import generated type from translations.d.ts

type MyCustomI18n = Omit<I18n, "t"> & {
t: TranslationFunction;

/**
* Same as `t` without any type checking.
* Should be used only when the translation key cannot be statically inferred.
*/
unsafeTranslate: (key: string, interpolations?: Record<string, unknown>) => string;
};

class MyInternationalization extends I18n {
unsafeTranslate(key: string, interpolations?: Record<string, unknown>) {
return this.t(key, interpolations);
}
}

export const i18n = new MyInternationalization(
{
fr,
en,
},
{ locale: getUserLanguage() } // <- load the user language by default
) as MyCustomI18n;
// ^ Apply my custom type to enjoy static translations and interpolations check !
```

### Default configuration for i18n-js

```json
{
"input": {
"format": "nested",
"path": "./translations/en.json"
},
"output": {
"path": "./translations/translations.d.ts"
},
"rules": [
{
"//": "Add pluralization placeholders",
"condition": { "keyEndsWith": ["zero", "one", "other"] },
"transformer": {
"addPlaceholder": { "name": "count", "type": ["number", "string"] },
"removeLastPart": true
}
},
{
"//": "Add interpolation values for matched placeholders",
"condition": { "placeholderPattern": { "prefix": "{{", "suffix": "}}" } },
"transformer": {
"addMatchedPlaceholder": { "type": ["string", "number"] }
}
},
{
"//": "Add interpolation values for matched placeholders using percent",
"condition": { "placeholderPattern": { "prefix": "%{", "suffix": "}" } },
"transformer": {
"addMatchedPlaceholder": { "type": ["string", "number"] }
}
}
],
"extra": {
"prettierIgnore": true,
"eslintDisablePrettier": false
}
}
```

0 comments on commit 65a89d4

Please sign in to comment.