Skip to content

Commit

Permalink
fix: fix logging in i18n v4 (#446)
Browse files Browse the repository at this point in the history
* fix: fix logging in i18n v4

---------

Co-authored-by: Maksim Sinelnikov <[email protected]>
  • Loading branch information
sinelnikov-web and Maksim Sinelnikov authored Oct 31, 2024
1 parent 2493faa commit 84e3818
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 37 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ Changelog
_Note: Gaps between patch versions are faulty, broken or test releases._

## v4.0.0-alpha.49 (2024-10-31)

#### :bug: Bug Fix

* `core/prelude/i18n/helpers`
* Fix logging bug in `pluralizeText`.
* Add logging info in i18n helpers.

## v4.0.0-alpha.48 (2024-10-30)

#### :bug: Bug Fix
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "lib/core/index.js",
"typings": "index.d.ts",
"license": "MIT",
"version": "4.0.0-alpha.48",
"version": "4.0.0-alpha.49",
"author": "kobezzza <[email protected]> (https://github.com/kobezzza)",
"repository": {
"type": "git",
Expand Down
7 changes: 7 additions & 0 deletions src/core/prelude/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ Changelog
> - :house: [Internal]
> - :nail_care: [Polish]
## v4.0.0-alpha.49 (2024-10-31)

#### :bug: Bug Fix

* Fix logging bug in `pluralizeText`.
* Add logging info in i18n helpers.

## v4.0.0-alpha.47.speedup (2024-10-01)

#### :rocket: New Feature
Expand Down
28 changes: 15 additions & 13 deletions src/core/prelude/i18n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,30 +89,32 @@ i18n('my-component')('My name is {name}', {name: 'John'});
## Pluralization of translations

Some keys may have multiple translations depending on some numeric value. For example, "1 apple" or "5 apples".
To specify such translations, a special macro `{count}` is used, and translations are specified as a tuple `[one, some, many, none]`.
To specify such translations, a special macro `{count}` is used, and translations are specified as a dictionary `{zero, one, two, few, many, other}`.

```js
export default {
ru: {
"my-component": {
"time": "время",
"{count} product": [
"{count} продукт",
"{count} продукта",
"{count} продуктов",
"{count} продуктов"
]
"{count} product": {
"one": "{count} product",
"few": "{count} products",
"many": "{count} products",
"zero": "{count} products",
"other": "{count} products",
}
}
},

en: {
"my-component": {
"{count} product": [
"{count} product",
"{count} products",
"{count} products",
"{count} products"
]
"{count} product": {
"one": "{count} product",
"few": "{count} products",
"many": "{count} products",
"zero": "{count} products",
"other": "{count} products",
}
}
}
};
Expand Down
56 changes: 35 additions & 21 deletions src/core/prelude/i18n/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import log from 'core/log';
import langPacs, { Translation, PluralTranslation } from 'lang';

import { locale } from 'core/prelude/i18n/const';
import type { I18nOpts, PluralizationCount } from 'core/prelude/i18n/interface';
import type { I18nOpts, PluralizationCount, I18nMeta } from 'core/prelude/i18n/interface';

const
logger = log.namespace('i18n');
Expand Down Expand Up @@ -50,18 +50,19 @@ export function i18nFactory(
const
key = Object.isString(value) ? value : value[0],
correctKeyset = keysetNames.find((keysetName) => langPacs[resolvedLocale]?.[keysetName]?.[key]),
translateValue = langPacs[resolvedLocale]?.[correctKeyset ?? '']?.[key];
translateValue = langPacs[resolvedLocale]?.[correctKeyset ?? '']?.[key],
meta: I18nMeta = {language: resolvedLocale, keyset: correctKeyset, key};

if (translateValue != null && translateValue !== '') {
return resolveTemplate(translateValue, params, {pluralRules});
return resolveTemplate(translateValue, params, {pluralRules, meta});
}

logger.error(
'Translation for the given key is not found',
`Key: ${key}, KeysetNames: ${keysetNames.join(', ')}, LocaleName: ${resolvedLocale}, available locales: ${Object.keys(langPacs).join(', ')}`
);

return resolveTemplate(key, params, {pluralRules});
return resolveTemplate(key, params, {pluralRules, meta});
};
}

Expand All @@ -70,26 +71,27 @@ export function i18nFactory(
*
* @param value - a string for the default case, or an array of strings for the plural case
* @param params - a dictionary with parameters for internationalization
* @params [opts] - additional options for current translation
*
* @example
* ```typescript
* const example = resolveTemplate('My name is {name}, I live in {city}', {name: 'John', city: 'Denver'});
*
* console.log(example); // 'My name is John, I live in Denver'
*
* const examplePluralize = resolveTemplate([
* {count} product, // One
* {count} products, // Some
* {count} products, // Many
* {count} products, // None
* ], {count: 5});
* const examplePluralize = resolveTemplate({
* one: {count} product,
* few: {count} products,
* many: {count} products,
* zero: {count} products,
* }, {count: 5});
*
* console.log(examplePluralize); // '5 products'
* ```
*/
export function resolveTemplate(value: Translation, params?: I18nParams, opts: I18nOpts = {}): string {
const
template = Object.isPlainObject(value) ? pluralizeText(value, params?.count, opts.pluralRules) : value;
template = Object.isPlainObject(value) ? pluralizeText(value, params?.count, opts) : value;

return template.replace(/{([^}]+)}/g, (_, key) => {
if (params?.[key] == null) {
Expand All @@ -106,24 +108,28 @@ export function resolveTemplate(value: Translation, params?: I18nParams, opts: I
*
* @param pluralTranslation - list of translation variants
* @param count - the value on the basis of which the form of pluralization will be selected
* @params [opts] - additional options for current translation
*
* @example
* ```typescript
* const result = pluralizeText([
* {count} product, // One
* {count} products, // Some
* {count} products, // Many
* {count} products, // None
* ], 5);
* const result = pluralizeText({
* one: {count} product,
* few: {count} products,
* many: {count} products,
* zero: {count} products,
* other: {count} products,
* }, 5, {pluralRules: new Intl.PluralRulse('en')});
*
* console.log(result); // '{count} products'
* ```
*/
export function pluralizeText(
pluralTranslation: PluralTranslation,
count: CanUndef<PluralizationCount>,
rules: CanUndef<Intl.PluralRules>
opts: I18nOpts = {}
): string {
const {pluralRules, meta} = opts;

let normalizedCount;

if (Object.isNumber(count)) {
Expand All @@ -138,16 +144,24 @@ export function pluralizeText(
}

if (normalizedCount == null) {
logger.error('Invalid value of the `count` parameter for string pluralization', `String: ${pluralTranslation[0]}`);
logger.error(
'Invalid value of the `count` parameter for string pluralization',
`Count: ${count}, Key: ${meta?.key}, Language: ${meta?.language}, Keyset: ${meta?.keyset}`
);

normalizedCount = 1;
}

const
pluralFormName = getPluralFormName(normalizedCount, rules),
pluralFormName = getPluralFormName(normalizedCount, pluralRules),
translation = pluralTranslation[pluralFormName];

if (translation == null) {
logger.error(`Plural form ${pluralFormName} doesn't exist.`, `String: ${pluralTranslation[0]}`);
logger.error(
`Plural form ${pluralFormName} doesn't exist.`,
`Key: ${meta?.key}, Language: ${meta?.language}, Keyset: ${meta?.keyset}`
);

return pluralTranslation.one;
}

Expand Down
8 changes: 8 additions & 0 deletions src/core/prelude/i18n/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ export interface LocaleKVStorage {

export type PluralizationCount = StringPluralizationForms | number;

export interface I18nMeta {
language: string;
key: string;
keyset?: string;
}

export interface I18nOpts {
pluralRules?: Intl.PluralRules;
meta?: I18nMeta;
}

25 changes: 23 additions & 2 deletions src/core/prelude/i18n/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('core/prelude/i18n', () => {
describe('text pluralization', () => {
it('using pluralization constants to choose the right form', () => {
formNames.forEach((form) => {
expect(pluralizeText(forms, form, rules)).toBe(forms[form]);
expect(pluralizeText(forms, form, {pluralRules: rules})).toBe(forms[form]);
});
});

Expand All @@ -53,7 +53,28 @@ describe('core/prelude/i18n', () => {
};

[forms.one, forms.other, forms.other, forms.other].forEach((form, index) => {
expect(pluralizeText(input.forms, input.count[index], rules)).toBe(form);
expect(pluralizeText(input.forms, input.count[index], {pluralRules: rules})).toBe(form);
});
});

it('returns "one" form when required plural form is missing', () => {
const input = {
forms,
count: [1, 2, 100, 0]
};

[forms.one, forms.one, forms.one, forms.one].forEach((form, index) => {
expect(pluralizeText({one: input.forms.one}, input.count[index], {pluralRules: rules})).toBe(form);
});
});

it('returns "one" form when count is invalid', () => {
const input = {
forms
};

[forms.one, forms.one, forms.one, forms.one].forEach((form) => {
expect(pluralizeText({one: input.forms.one}, undefined, {pluralRules: rules})).toBe(form);
});
});
});
Expand Down

0 comments on commit 84e3818

Please sign in to comment.