react-scoped-i18n 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -0
  3. package/dist/cjs/createI18nContext/api/createCommons.d.ts +6 -0
  4. package/dist/cjs/createI18nContext/api/createCommons.js +30 -0
  5. package/dist/cjs/createI18nContext/api/createCommons.test.d.ts +1 -0
  6. package/dist/cjs/createI18nContext/api/createCommons.test.js +26 -0
  7. package/dist/cjs/createI18nContext/api/createFormat.d.ts +9 -0
  8. package/dist/cjs/createI18nContext/api/createFormat.js +54 -0
  9. package/dist/cjs/createI18nContext/api/createFormat.test.d.ts +1 -0
  10. package/dist/cjs/createI18nContext/api/createFormat.test.js +71 -0
  11. package/dist/cjs/createI18nContext/api/createT.d.ts +7 -0
  12. package/dist/cjs/createI18nContext/api/createT.js +15 -0
  13. package/dist/cjs/createI18nContext/api/createT.test.d.ts +1 -0
  14. package/dist/cjs/createI18nContext/api/createT.test.js +23 -0
  15. package/dist/cjs/createI18nContext/api/createTGlobal.d.ts +8 -0
  16. package/dist/cjs/createI18nContext/api/createTGlobal.js +31 -0
  17. package/dist/cjs/createI18nContext/api/createTGlobal.test.d.ts +1 -0
  18. package/dist/cjs/createI18nContext/api/createTGlobal.test.js +20 -0
  19. package/dist/cjs/createI18nContext/api/createTPlural.d.ts +5 -0
  20. package/dist/cjs/createI18nContext/api/createTPlural.js +19 -0
  21. package/dist/cjs/createI18nContext/api/createTPlural.test.d.ts +1 -0
  22. package/dist/cjs/createI18nContext/api/createTPlural.test.js +31 -0
  23. package/dist/cjs/createI18nContext/api/createUseI18n.d.ts +21 -0
  24. package/dist/cjs/createI18nContext/api/createUseI18n.js +52 -0
  25. package/dist/cjs/createI18nContext/api/createUseI18n.test.d.ts +1 -0
  26. package/dist/cjs/createI18nContext/api/createUseI18n.test.js +46 -0
  27. package/dist/cjs/createI18nContext/const/index.d.ts +1 -0
  28. package/dist/cjs/createI18nContext/const/index.js +4 -0
  29. package/dist/cjs/createI18nContext/index.d.ts +29 -0
  30. package/dist/cjs/createI18nContext/index.integration.test.d.ts +1 -0
  31. package/dist/cjs/createI18nContext/index.integration.test.js +41 -0
  32. package/dist/cjs/createI18nContext/index.js +45 -0
  33. package/dist/cjs/createI18nContext/provider/createI18nProvider.d.ts +15 -0
  34. package/dist/cjs/createI18nContext/provider/createI18nProvider.js +23 -0
  35. package/dist/cjs/createI18nContext/type/index.d.ts +19 -0
  36. package/dist/cjs/createI18nContext/type/index.js +7 -0
  37. package/dist/cjs/createI18nContext/type/isLanguage.test.d.ts +1 -0
  38. package/dist/cjs/createI18nContext/type/isLanguage.test.js +16 -0
  39. package/dist/cjs/createI18nContext/util/getPluralTranslation.d.ts +7 -0
  40. package/dist/cjs/createI18nContext/util/getPluralTranslation.js +48 -0
  41. package/dist/cjs/createI18nContext/util/getPluralTranslation.test.d.ts +1 -0
  42. package/dist/cjs/createI18nContext/util/getPluralTranslation.test.js +126 -0
  43. package/dist/cjs/createI18nContext/util/getTranslation.d.ts +7 -0
  44. package/dist/cjs/createI18nContext/util/getTranslation.js +32 -0
  45. package/dist/cjs/createI18nContext/util/getTranslation.test.d.ts +1 -0
  46. package/dist/cjs/createI18nContext/util/getTranslation.test.js +96 -0
  47. package/dist/cjs/env.d.ts +1 -0
  48. package/dist/cjs/env.js +9 -0
  49. package/dist/cjs/index.d.ts +1 -0
  50. package/dist/cjs/index.js +5 -0
  51. package/dist/cjs/index.test.d.ts +0 -0
  52. package/dist/cjs/index.test.js +40 -0
  53. package/dist/esm/createI18nContext/api/createCommons.d.ts +7 -0
  54. package/dist/esm/createI18nContext/api/createCommons.d.ts.map +1 -0
  55. package/dist/esm/createI18nContext/api/createCommons.js +26 -0
  56. package/dist/esm/createI18nContext/api/createCommons.test.d.ts +2 -0
  57. package/dist/esm/createI18nContext/api/createCommons.test.d.ts.map +1 -0
  58. package/dist/esm/createI18nContext/api/createCommons.test.js +24 -0
  59. package/dist/esm/createI18nContext/api/createFormat.d.ts +10 -0
  60. package/dist/esm/createI18nContext/api/createFormat.d.ts.map +1 -0
  61. package/dist/esm/createI18nContext/api/createFormat.js +50 -0
  62. package/dist/esm/createI18nContext/api/createFormat.test.d.ts +2 -0
  63. package/dist/esm/createI18nContext/api/createFormat.test.d.ts.map +1 -0
  64. package/dist/esm/createI18nContext/api/createFormat.test.js +69 -0
  65. package/dist/esm/createI18nContext/api/createT.d.ts +8 -0
  66. package/dist/esm/createI18nContext/api/createT.d.ts.map +1 -0
  67. package/dist/esm/createI18nContext/api/createT.js +11 -0
  68. package/dist/esm/createI18nContext/api/createT.test.d.ts +2 -0
  69. package/dist/esm/createI18nContext/api/createT.test.d.ts.map +1 -0
  70. package/dist/esm/createI18nContext/api/createT.test.js +21 -0
  71. package/dist/esm/createI18nContext/api/createTGlobal.d.ts +9 -0
  72. package/dist/esm/createI18nContext/api/createTGlobal.d.ts.map +1 -0
  73. package/dist/esm/createI18nContext/api/createTGlobal.js +27 -0
  74. package/dist/esm/createI18nContext/api/createTGlobal.test.d.ts +2 -0
  75. package/dist/esm/createI18nContext/api/createTGlobal.test.d.ts.map +1 -0
  76. package/dist/esm/createI18nContext/api/createTGlobal.test.js +18 -0
  77. package/dist/esm/createI18nContext/api/createTPlural.d.ts +6 -0
  78. package/dist/esm/createI18nContext/api/createTPlural.d.ts.map +1 -0
  79. package/dist/esm/createI18nContext/api/createTPlural.js +15 -0
  80. package/dist/esm/createI18nContext/api/createTPlural.test.d.ts +2 -0
  81. package/dist/esm/createI18nContext/api/createTPlural.test.d.ts.map +1 -0
  82. package/dist/esm/createI18nContext/api/createTPlural.test.js +29 -0
  83. package/dist/esm/createI18nContext/api/createUseI18n.d.ts +22 -0
  84. package/dist/esm/createI18nContext/api/createUseI18n.d.ts.map +1 -0
  85. package/dist/esm/createI18nContext/api/createUseI18n.js +48 -0
  86. package/dist/esm/createI18nContext/api/createUseI18n.test.d.ts +2 -0
  87. package/dist/esm/createI18nContext/api/createUseI18n.test.d.ts.map +1 -0
  88. package/dist/esm/createI18nContext/api/createUseI18n.test.js +44 -0
  89. package/dist/esm/createI18nContext/const/index.d.ts +2 -0
  90. package/dist/esm/createI18nContext/const/index.d.ts.map +1 -0
  91. package/dist/esm/createI18nContext/const/index.js +1 -0
  92. package/dist/esm/createI18nContext/index.d.ts +30 -0
  93. package/dist/esm/createI18nContext/index.d.ts.map +1 -0
  94. package/dist/esm/createI18nContext/index.integration.test.d.ts +2 -0
  95. package/dist/esm/createI18nContext/index.integration.test.d.ts.map +1 -0
  96. package/dist/esm/createI18nContext/index.integration.test.js +39 -0
  97. package/dist/esm/createI18nContext/index.js +42 -0
  98. package/dist/esm/createI18nContext/provider/createI18nProvider.d.ts +16 -0
  99. package/dist/esm/createI18nContext/provider/createI18nProvider.d.ts.map +1 -0
  100. package/dist/esm/createI18nContext/provider/createI18nProvider.js +19 -0
  101. package/dist/esm/createI18nContext/type/index.d.ts +20 -0
  102. package/dist/esm/createI18nContext/type/index.d.ts.map +1 -0
  103. package/dist/esm/createI18nContext/type/index.js +3 -0
  104. package/dist/esm/createI18nContext/type/isLanguage.test.d.ts +2 -0
  105. package/dist/esm/createI18nContext/type/isLanguage.test.d.ts.map +1 -0
  106. package/dist/esm/createI18nContext/type/isLanguage.test.js +14 -0
  107. package/dist/esm/createI18nContext/util/getPluralTranslation.d.ts +8 -0
  108. package/dist/esm/createI18nContext/util/getPluralTranslation.d.ts.map +1 -0
  109. package/dist/esm/createI18nContext/util/getPluralTranslation.js +44 -0
  110. package/dist/esm/createI18nContext/util/getPluralTranslation.test.d.ts +2 -0
  111. package/dist/esm/createI18nContext/util/getPluralTranslation.test.d.ts.map +1 -0
  112. package/dist/esm/createI18nContext/util/getPluralTranslation.test.js +124 -0
  113. package/dist/esm/createI18nContext/util/getTranslation.d.ts +8 -0
  114. package/dist/esm/createI18nContext/util/getTranslation.d.ts.map +1 -0
  115. package/dist/esm/createI18nContext/util/getTranslation.js +28 -0
  116. package/dist/esm/createI18nContext/util/getTranslation.test.d.ts +2 -0
  117. package/dist/esm/createI18nContext/util/getTranslation.test.d.ts.map +1 -0
  118. package/dist/esm/createI18nContext/util/getTranslation.test.js +94 -0
  119. package/dist/esm/env.d.ts +2 -0
  120. package/dist/esm/env.d.ts.map +1 -0
  121. package/dist/esm/env.js +6 -0
  122. package/dist/esm/index.d.ts +2 -0
  123. package/dist/esm/index.d.ts.map +1 -0
  124. package/dist/esm/index.js +1 -0
  125. package/dist/esm/index.test.d.ts +1 -0
  126. package/dist/esm/index.test.d.ts.map +1 -0
  127. package/dist/esm/index.test.js +7 -0
  128. package/package.json +55 -0
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const createUseI18n_1 = require("./createUseI18n");
4
+ describe(`createUseI18n`, () => {
5
+ const mockedI18nContext = {};
6
+ it(`prints console.error if used outside of a provider`, () => {
7
+ const useI18n = (0, createUseI18n_1.createUseI18n)({
8
+ languages: [`en`, `es`],
9
+ fallbackLanguage: `en`,
10
+ commons: undefined,
11
+ I18nContext: mockedI18nContext,
12
+ });
13
+ // expect console.error to be called
14
+ const consoleErrorSpy = jest
15
+ .spyOn(console, `error`)
16
+ .mockImplementation(() => { });
17
+ try {
18
+ useI18n();
19
+ }
20
+ catch (e) {
21
+ expect(consoleErrorSpy).toHaveBeenCalled(); // eslint-disable-line unused-imports/no-unused-vars
22
+ }
23
+ consoleErrorSpy.mockRestore();
24
+ });
25
+ it(`can be initialised with only required parameters`, () => {
26
+ expect(() => (0, createUseI18n_1.createUseI18n)({
27
+ languages: [`en`, `es`],
28
+ fallbackLanguage: `en`,
29
+ commons: undefined,
30
+ I18nContext: mockedI18nContext,
31
+ })).toBeDefined();
32
+ });
33
+ it(`should be initialised with all parameters`, () => {
34
+ expect(() => (0, createUseI18n_1.createUseI18n)({
35
+ languages: [`en`, `es`],
36
+ fallbackLanguage: `es`,
37
+ commons: {
38
+ accept: {
39
+ en: `Accept`,
40
+ es: `Aceptar`,
41
+ },
42
+ },
43
+ I18nContext: mockedI18nContext,
44
+ })).toBeDefined();
45
+ });
46
+ });
@@ -0,0 +1 @@
1
+ export declare const MISSING_TRANSLATION = "[MISSING TRANSLATION]";
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MISSING_TRANSLATION = void 0;
4
+ exports.MISSING_TRANSLATION = `[MISSING TRANSLATION]`;
@@ -0,0 +1,29 @@
1
+ import { TranslationMap } from "./type";
2
+ export declare function createI18nContext<Languages extends readonly string[], Commons extends Record<string, TranslationMap<Languages[number], string>>>({ languages, fallbackLanguage, commons, }: {
3
+ languages: Readonly<Languages>;
4
+ fallbackLanguage: Languages[number];
5
+ commons?: Readonly<Commons>;
6
+ }): {
7
+ I18nProvider: {
8
+ ({ children, initialLanguage, }: {
9
+ children: import("react").ReactNode;
10
+ initialLanguage: Languages[number];
11
+ }): import("react/jsx-runtime").JSX.Element;
12
+ displayName: string;
13
+ };
14
+ useI18n: () => {
15
+ t: <Value extends import("./type").TranslationValue = import("react").ReactNode>(translation: TranslationMap<Languages[number], Value>) => Value;
16
+ tPlural: (count: number, translations: import("./type").PluralTranslationMap<Languages[number]>) => string | number | bigint | true | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null | undefined>;
17
+ setCurrentLanguage: import("react").Dispatch<import("react").SetStateAction<Languages[number]>>;
18
+ currentLanguage: Languages[number];
19
+ format: {
20
+ number: (value: number, options?: Intl.NumberFormatOptions) => string;
21
+ date: (value: Date | number, options?: Intl.DateTimeFormatOptions) => string;
22
+ time: (value: Date | number, options?: Intl.DateTimeFormatOptions) => string;
23
+ currency: (value: number, currency: string, options?: Omit<Intl.NumberFormatOptions, `style` | `currency`>) => string;
24
+ percentage: (value: number, options?: Omit<Intl.NumberFormatOptions, `style`>) => string;
25
+ };
26
+ commons: { [K in keyof Readonly<Commons>]?: string | undefined; };
27
+ };
28
+ tGlobal: (translation: TranslationMap<string>) => import("./type").TranslationValue;
29
+ };
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsx_runtime_1 = require("react/jsx-runtime");
4
+ /**
5
+ * @jest-environment jsdom
6
+ */
7
+ const react_1 = require("react");
8
+ const react_2 = require("@testing-library/react");
9
+ const index_1 = require("./index");
10
+ test(`very basic integration test`, () => {
11
+ const { I18nProvider, useI18n } = (0, index_1.createI18nContext)({
12
+ languages: [`en`, `es`],
13
+ fallbackLanguage: `en`,
14
+ commons: {
15
+ continue: {
16
+ en: `Continue`,
17
+ es: `Continuar`,
18
+ },
19
+ },
20
+ });
21
+ const TestComponent = () => {
22
+ const { t, currentLanguage, setCurrentLanguage, commons, format } = useI18n();
23
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("h1", { "data-testid": `current-language`, children: currentLanguage }), (0, jsx_runtime_1.jsx)("div", { "data-testid": `test-component`, children: t({ en: `Hello`, es: `Hola` }) }), (0, jsx_runtime_1.jsx)("button", { "data-testid": `switch-to-english-button`, onClick: () => {
24
+ setCurrentLanguage(`en`);
25
+ } }), (0, jsx_runtime_1.jsx)("div", { "data-testid": `formatted-number`, children: format.number(10000.555, {
26
+ maximumFractionDigits: 2,
27
+ }) }), (0, jsx_runtime_1.jsx)("button", { "data-testid": `continue-button`, children: commons.continue })] }));
28
+ };
29
+ (0, react_2.render)((0, jsx_runtime_1.jsx)(I18nProvider, { initialLanguage: `es`, children: (0, jsx_runtime_1.jsx)(TestComponent, {}) }));
30
+ expect(react_2.screen.getByTestId(`current-language`).textContent).toBe(`es`);
31
+ expect(react_2.screen.getByTestId(`test-component`).textContent).toBe(`Hola`);
32
+ expect(react_2.screen.getByTestId(`continue-button`).textContent).toBe(`Continuar`);
33
+ expect(react_2.screen.getByTestId(`formatted-number`).textContent).toBe(`10.000,56`);
34
+ (0, react_1.act)(() => {
35
+ react_2.screen.getByTestId(`switch-to-english-button`).click();
36
+ });
37
+ expect(react_2.screen.getByTestId(`current-language`).textContent).toBe(`en`);
38
+ expect(react_2.screen.getByTestId(`test-component`).textContent).toBe(`Hello`);
39
+ expect(react_2.screen.getByTestId(`continue-button`).textContent).toBe(`Continue`);
40
+ expect(react_2.screen.getByTestId(`formatted-number`).textContent).toBe(`10,000.56`);
41
+ });
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createI18nContext = createI18nContext;
4
+ const react_1 = require("react");
5
+ const createI18nProvider_1 = require("./provider/createI18nProvider");
6
+ const createUseI18n_1 = require("./api/createUseI18n");
7
+ const createTGlobal_1 = require("./api/createTGlobal");
8
+ const env_1 = require("../env");
9
+ function createI18nContext({ languages, fallbackLanguage, commons, }) {
10
+ if (env_1.IS_DEV) {
11
+ if (commons) {
12
+ Object.freeze(commons);
13
+ }
14
+ Object.freeze(languages);
15
+ }
16
+ const moduleScopedCurrentLanguageRef =
17
+ /**
18
+ * Module-scoped variable to hold the current language for tGlobal function.
19
+ * This is updated by the I18nProvider component.
20
+ * Don't rely on this
21
+ */
22
+ { current: fallbackLanguage };
23
+ if (!fallbackLanguage || !languages.includes(fallbackLanguage)) {
24
+ throw new Error(`Default language "${fallbackLanguage}" must be one of the supported languages: [${languages.join(`, `)}]`);
25
+ }
26
+ const I18nContext = (0, react_1.createContext)(null);
27
+ I18nContext.displayName = `I18nContext`;
28
+ const I18nProvider = (0, createI18nProvider_1.createI18nProvider)({
29
+ I18nContext,
30
+ moduleScopedCurrentLanguageRef,
31
+ fallbackLanguage,
32
+ });
33
+ const useI18n = (0, createUseI18n_1.createUseI18n)({
34
+ I18nContext,
35
+ languages,
36
+ fallbackLanguage,
37
+ commons,
38
+ });
39
+ const tGlobal = (0, createTGlobal_1.createTGlobal)({
40
+ languages,
41
+ currentLanguage: moduleScopedCurrentLanguageRef,
42
+ fallbackLanguage,
43
+ });
44
+ return { I18nProvider, useI18n, tGlobal };
45
+ }
@@ -0,0 +1,15 @@
1
+ import { Context, ReactNode } from "react";
2
+ import { I18nContextType } from "../type";
3
+ export declare const createI18nProvider: <const Language extends string>({ I18nContext, moduleScopedCurrentLanguageRef, fallbackLanguage, }: {
4
+ I18nContext: Context<I18nContextType<Language> | null>;
5
+ moduleScopedCurrentLanguageRef: {
6
+ current: Language;
7
+ };
8
+ fallbackLanguage: Language;
9
+ }) => {
10
+ ({ children, initialLanguage, }: {
11
+ children: ReactNode;
12
+ initialLanguage: Language;
13
+ }): import("react/jsx-runtime").JSX.Element;
14
+ displayName: string;
15
+ };
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createI18nProvider = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const createI18nProvider = ({ I18nContext, moduleScopedCurrentLanguageRef, fallbackLanguage, }) => {
7
+ const I18nProvider = ({ children, initialLanguage, }) => {
8
+ const [currentLanguage, setCurrentLanguage] = (0, react_1.useState)(initialLanguage || fallbackLanguage);
9
+ (0, react_1.useEffect)(() => {
10
+ // Used only for tGlobal function outside React components.
11
+ // Do not rely on this!
12
+ moduleScopedCurrentLanguageRef.current = currentLanguage;
13
+ }, [currentLanguage]);
14
+ return ((0, jsx_runtime_1.jsx)(I18nContext.Provider, { value: {
15
+ currentLanguage,
16
+ fallbackLanguage,
17
+ setCurrentLanguage,
18
+ }, children: children }));
19
+ };
20
+ I18nProvider.displayName = `I18nProvider`;
21
+ return I18nProvider;
22
+ };
23
+ exports.createI18nProvider = createI18nProvider;
@@ -0,0 +1,19 @@
1
+ import { Dispatch, ReactNode, SetStateAction } from "react";
2
+ export type TranslationValue = ReactNode | string | number;
3
+ export type TranslationMap<Language extends string, Value = TranslationValue> = Record<Language, Value>;
4
+ export type Languages = readonly string[];
5
+ export declare const isLanguage: (language: string, languages: Languages) => language is Languages[number];
6
+ export type I18nContextType<Language extends string> = {
7
+ currentLanguage: Language;
8
+ fallbackLanguage: Language;
9
+ setCurrentLanguage: Dispatch<SetStateAction<Language>>;
10
+ };
11
+ export type PluralTranslation = {
12
+ negative?: ReactNode;
13
+ zero?: ReactNode;
14
+ one?: ReactNode;
15
+ two?: ReactNode;
16
+ many: ReactNode;
17
+ [key: number]: ReactNode;
18
+ };
19
+ export type PluralTranslationMap<Language extends string> = Record<Language, PluralTranslation>;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isLanguage = void 0;
4
+ const isLanguage = (language, languages) => {
5
+ return languages.includes(language);
6
+ };
7
+ exports.isLanguage = isLanguage;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_1 = require("./index");
4
+ describe(`isLanguage`, () => {
5
+ const languages = [`en`, `es`, `sl`];
6
+ it(`returns true for supported languages`, () => {
7
+ expect((0, index_1.isLanguage)(`en`, languages)).toBe(true);
8
+ expect((0, index_1.isLanguage)(`es`, languages)).toBe(true);
9
+ expect((0, index_1.isLanguage)(`sl`, languages)).toBe(true);
10
+ });
11
+ it(`returns false for unsupported languages`, () => {
12
+ expect((0, index_1.isLanguage)(`fr`, languages)).toBe(false);
13
+ expect((0, index_1.isLanguage)(`de`, languages)).toBe(false);
14
+ expect((0, index_1.isLanguage)(`it`, languages)).toBe(false);
15
+ });
16
+ });
@@ -0,0 +1,7 @@
1
+ import { PluralTranslationMap } from "../type";
2
+ export declare const getPluralTranslation: <const Language extends string>({ currentLanguage, fallbackLanguage, count, translations, }: {
3
+ currentLanguage: Language;
4
+ fallbackLanguage: Language;
5
+ count: number;
6
+ translations: PluralTranslationMap<Language[][number]>;
7
+ }) => string | number | bigint | true | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null | undefined>;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getPluralTranslation = void 0;
4
+ const const_1 = require("../const");
5
+ const env_1 = require("../../env");
6
+ const getPluralTranslation = ({ currentLanguage, fallbackLanguage, count, translations, }) => {
7
+ if (!translations) {
8
+ throw new Error(`[i18n] Translations object is undefined or null`);
9
+ }
10
+ const targetTranslation = translations[currentLanguage];
11
+ const defaultTranslation = translations[fallbackLanguage];
12
+ if (targetTranslation == undefined && defaultTranslation === undefined) {
13
+ if (env_1.IS_DEV) {
14
+ console.warn(`[i18n] Missing plural translation for language "${currentLanguage}" and for default language "${fallbackLanguage}"`);
15
+ }
16
+ return const_1.MISSING_TRANSLATION;
17
+ }
18
+ if (targetTranslation === undefined) {
19
+ if (env_1.IS_DEV) {
20
+ console.warn(`[i18n] Missing plural translation for language "${currentLanguage}". Falling back to default language "${fallbackLanguage}"`);
21
+ }
22
+ }
23
+ const translation = targetTranslation || defaultTranslation;
24
+ const exactMatch = translation[count];
25
+ if (exactMatch) {
26
+ return exactMatch;
27
+ }
28
+ if (count < 0 && translation.negative) {
29
+ return translation.negative;
30
+ }
31
+ if (count === 0 && translation.zero) {
32
+ return translation.zero;
33
+ }
34
+ if (count === 1 && translation.one) {
35
+ return translation.one;
36
+ }
37
+ if (count === 2 && translation.two) {
38
+ return translation.two;
39
+ }
40
+ if (translation.many) {
41
+ return translation.many;
42
+ }
43
+ if (env_1.IS_DEV) {
44
+ console.warn(`[i18n] No pluralization match found for count "${count}". Please provide appropriate pluralization translations.`);
45
+ }
46
+ return const_1.MISSING_TRANSLATION;
47
+ };
48
+ exports.getPluralTranslation = getPluralTranslation;
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const getPluralTranslation_1 = require("./getPluralTranslation");
4
+ const const_1 = require("../const");
5
+ describe(`getPluralTranslation`, () => {
6
+ const fallbackLanguage = `en`;
7
+ const translations = {
8
+ en: {
9
+ negative: `You have a negative count.`,
10
+ zero: `You have no items.`,
11
+ one: `You have one item.`,
12
+ two: `You have two items.`,
13
+ many: `You have many items.`,
14
+ 42: `You have the perfect count of items!`,
15
+ },
16
+ es: {
17
+ negative: `Tienes un conteo negativo.`,
18
+ zero: `No tienes artículos.`,
19
+ one: `Tienes un artículo.`,
20
+ two: `Tienes dos artículos.`,
21
+ many: `Tienes muchos artículos.`,
22
+ },
23
+ };
24
+ it(`Error: Translations object is undefined or null`, () => {
25
+ expect(() => (0, getPluralTranslation_1.getPluralTranslation)({
26
+ currentLanguage: `es`,
27
+ fallbackLanguage: fallbackLanguage,
28
+ count: -5,
29
+ // @ts-expect-error testing undefined translations
30
+ translations: undefined,
31
+ })).toThrow(`[i18n] Translations object is undefined or null`);
32
+ });
33
+ it(`returns the correct translation for negative count`, () => {
34
+ expect((0, getPluralTranslation_1.getPluralTranslation)({
35
+ currentLanguage: `es`,
36
+ fallbackLanguage: fallbackLanguage,
37
+ count: -5,
38
+ translations,
39
+ })).toBe(`Tienes un conteo negativo.`);
40
+ });
41
+ it(`returns the correct translation for zero count`, () => {
42
+ expect((0, getPluralTranslation_1.getPluralTranslation)({
43
+ currentLanguage: `en`,
44
+ fallbackLanguage: fallbackLanguage,
45
+ count: 0,
46
+ translations,
47
+ })).toBe(`You have no items.`);
48
+ });
49
+ it(`returns the correct translation for one count`, () => {
50
+ expect((0, getPluralTranslation_1.getPluralTranslation)({
51
+ currentLanguage: `es`,
52
+ fallbackLanguage: fallbackLanguage,
53
+ count: 1,
54
+ translations,
55
+ })).toBe(`Tienes un artículo.`);
56
+ });
57
+ it(`returns the correct translation for two count`, () => {
58
+ expect((0, getPluralTranslation_1.getPluralTranslation)({
59
+ currentLanguage: `en`,
60
+ fallbackLanguage: fallbackLanguage,
61
+ count: 2,
62
+ translations,
63
+ })).toBe(`You have two items.`);
64
+ });
65
+ it(`returns the correct translation for many count`, () => {
66
+ expect((0, getPluralTranslation_1.getPluralTranslation)({
67
+ currentLanguage: `es`,
68
+ fallbackLanguage: fallbackLanguage,
69
+ count: 10,
70
+ translations,
71
+ })).toBe(`Tienes muchos artículos.`);
72
+ });
73
+ it(`returns the correct translation for exact match count`, () => {
74
+ expect((0, getPluralTranslation_1.getPluralTranslation)({
75
+ currentLanguage: `en`,
76
+ fallbackLanguage: fallbackLanguage,
77
+ count: 42,
78
+ translations,
79
+ })).toBe(`You have the perfect count of items!`);
80
+ });
81
+ it(`falls back to default language if translation is missing for current language`, () => {
82
+ const incompleteTranslations = {
83
+ en: {
84
+ one: `You have one item.`,
85
+ },
86
+ };
87
+ expect((0, getPluralTranslation_1.getPluralTranslation)({
88
+ currentLanguage: `es`,
89
+ fallbackLanguage: fallbackLanguage,
90
+ count: 1,
91
+ // @ts-expect-error incomplete translation for testing
92
+ translations: incompleteTranslations,
93
+ })).toBe(`You have one item.`);
94
+ });
95
+ it(`returns ${const_1.MISSING_TRANSLATION} if translation is missing for both current and default languages`, () => {
96
+ const incompleteTranslations = {
97
+ de: {
98
+ one: `Tienes un artículo.`,
99
+ },
100
+ };
101
+ expect((0, getPluralTranslation_1.getPluralTranslation)({
102
+ currentLanguage: `en`,
103
+ fallbackLanguage: fallbackLanguage,
104
+ count: 1,
105
+ // @ts-expect-error incomplete translation for testing
106
+ translations: incompleteTranslations,
107
+ })).toBe(const_1.MISSING_TRANSLATION);
108
+ });
109
+ it(`Warn: No pluralization match found for count`, () => {
110
+ const consoleWarnSpy = jest.spyOn(console, `warn`).mockImplementation();
111
+ const incompleteTranslations = {
112
+ en: {
113
+ one: `You have one item.`,
114
+ },
115
+ };
116
+ (0, getPluralTranslation_1.getPluralTranslation)({
117
+ currentLanguage: `en`,
118
+ fallbackLanguage: fallbackLanguage,
119
+ count: 5,
120
+ // @ts-expect-error incomplete translation for testing
121
+ translations: incompleteTranslations,
122
+ });
123
+ expect(consoleWarnSpy).toHaveBeenCalledWith(`[i18n] No pluralization match found for count "5". Please provide appropriate pluralization translations.`);
124
+ consoleWarnSpy.mockRestore();
125
+ });
126
+ });
@@ -0,0 +1,7 @@
1
+ import { TranslationMap, TranslationValue } from "../type";
2
+ export declare const getTranslation: <const Language extends string, Value = TranslationValue>({ translation, language, languages, fallbackLanguage, }: {
3
+ translation: TranslationMap<Language, Value>;
4
+ language: Language;
5
+ languages: readonly Language[];
6
+ fallbackLanguage: Language;
7
+ }) => Value;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTranslation = void 0;
4
+ const const_1 = require("../const");
5
+ const env_1 = require("../../env");
6
+ const getTranslation = ({ translation, language, languages, fallbackLanguage, }) => {
7
+ if (!translation) {
8
+ throw new Error(`[i18n] Translation object is undefined or null`);
9
+ }
10
+ if (env_1.IS_DEV) {
11
+ const missing = languages.filter((l) => !(l in translation));
12
+ if (missing.length > 0) {
13
+ console.warn(`[i18n] Missing languages: ${missing.join(`, `)} in ${Object.keys(translation)}`);
14
+ }
15
+ }
16
+ const targetTranslation = translation[language];
17
+ if (targetTranslation !== undefined) {
18
+ return targetTranslation;
19
+ }
20
+ const fallbackTranslation = translation[fallbackLanguage];
21
+ if (fallbackTranslation !== undefined) {
22
+ if (env_1.IS_DEV) {
23
+ console.warn(`[i18n] Missing translation for language "${language}". Falling back to default language "${fallbackLanguage}"`);
24
+ }
25
+ return fallbackTranslation;
26
+ }
27
+ if (env_1.IS_DEV) {
28
+ console.warn(`[i18n] Missing translation for language "${language}" and for default language "${fallbackLanguage}"`);
29
+ }
30
+ return const_1.MISSING_TRANSLATION;
31
+ };
32
+ exports.getTranslation = getTranslation;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const getTranslation_1 = require("./getTranslation");
4
+ const const_1 = require("../const");
5
+ describe(`getTranslation`, () => {
6
+ const languages = [`en`, `es`, `sl`];
7
+ const translation = {
8
+ en: `Hello!`,
9
+ es: `¡Hola!`,
10
+ sl: `Živjo!`,
11
+ };
12
+ const fallbackLanguage = `es`;
13
+ it(`returns the correct translation for the specified language`, () => {
14
+ expect((0, getTranslation_1.getTranslation)({
15
+ translation,
16
+ language: `sl`,
17
+ languages,
18
+ fallbackLanguage: fallbackLanguage,
19
+ })).toBe(`Živjo!`);
20
+ });
21
+ it(`falls back to the default language if the specified language translation is missing`, () => {
22
+ expect(
23
+ // @ts-expect-error testing missing language
24
+ (0, getTranslation_1.getTranslation)({
25
+ translation,
26
+ languages,
27
+ fallbackLanguage: fallbackLanguage,
28
+ })).toBe(`¡Hola!`);
29
+ });
30
+ it(`falls back to the default language if the specified language is not supported`, () => {
31
+ expect((0, getTranslation_1.getTranslation)({
32
+ // @ts-expect-error unsupported language
33
+ translation,
34
+ language: `fr`,
35
+ languages,
36
+ fallbackLanguage: fallbackLanguage,
37
+ })).toBe(`¡Hola!`);
38
+ });
39
+ it(`returns ${const_1.MISSING_TRANSLATION} if the translation is missing for both the specified and default languages`, () => {
40
+ const incompleteTranslation = {
41
+ en: `Hello!`,
42
+ };
43
+ expect((0, getTranslation_1.getTranslation)({
44
+ // @ts-expect-error incomplete translation
45
+ translation: incompleteTranslation,
46
+ languages,
47
+ fallbackLanguage: fallbackLanguage,
48
+ language: `de`,
49
+ })).toBe(const_1.MISSING_TRANSLATION);
50
+ });
51
+ it(`Error: Translation object is undefined or null`, () => {
52
+ expect(() => {
53
+ (0, getTranslation_1.getTranslation)({
54
+ // @ts-expect-error testing undefined translation
55
+ translation: undefined,
56
+ languages,
57
+ fallbackLanguage: fallbackLanguage,
58
+ language: `es`,
59
+ });
60
+ }).toThrow(`[i18n] Translation object is undefined or null`);
61
+ });
62
+ it(`Warn: Missing languages`, () => {
63
+ const incompleteLanguages = [`en`, `de`];
64
+ const consoleWarnSpy = jest.spyOn(console, `warn`).mockImplementation();
65
+ (0, getTranslation_1.getTranslation)({
66
+ // @ts-expect-error incomplete languages
67
+ translation,
68
+ language: `sl`,
69
+ languages: incompleteLanguages,
70
+ fallbackLanguage: fallbackLanguage,
71
+ });
72
+ expect(consoleWarnSpy).toHaveBeenCalledWith(`[i18n] Missing languages: de in en,es,sl`);
73
+ });
74
+ it(`Warn: Missing translation for language, falling back to default`, () => {
75
+ const consoleWarnSpy = jest.spyOn(console, `warn`).mockImplementation();
76
+ (0, getTranslation_1.getTranslation)({
77
+ // @ts-expect-error testing translation for language
78
+ translation,
79
+ language: `pt`,
80
+ languages,
81
+ fallbackLanguage: fallbackLanguage,
82
+ });
83
+ expect(consoleWarnSpy).toHaveBeenCalledWith(`[i18n] Missing translation for language "pt". Falling back to default language "es"`);
84
+ });
85
+ it(`Warn: Missing translation for language and for default language`, () => {
86
+ const consoleWarnSpy = jest.spyOn(console, `warn`).mockImplementation();
87
+ (0, getTranslation_1.getTranslation)({
88
+ // @ts-expect-error testing translation for language and default language
89
+ translation,
90
+ language: `ru`,
91
+ languages,
92
+ fallbackLanguage: `nl`,
93
+ });
94
+ expect(consoleWarnSpy).toHaveBeenCalledWith(`[i18n] Missing translation for language "ru" and for default language "nl"`);
95
+ });
96
+ });
@@ -0,0 +1 @@
1
+ export declare const IS_DEV: boolean;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IS_DEV = void 0;
4
+ const rawNodeEnv = typeof process !== `undefined` &&
5
+ process.env &&
6
+ typeof process.env.NODE_ENV === `string`
7
+ ? process.env.NODE_ENV
8
+ : undefined;
9
+ exports.IS_DEV = typeof __DEV__ !== `undefined` ? __DEV__ : rawNodeEnv === `development`;
@@ -0,0 +1 @@
1
+ export { createI18nContext } from "./createI18nContext";
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createI18nContext = void 0;
4
+ var createI18nContext_1 = require("./createI18nContext");
5
+ Object.defineProperty(exports, "createI18nContext", { enumerable: true, get: function () { return createI18nContext_1.createI18nContext; } });
File without changes