i18nya 0.1.0 → 0.1.2
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.
- package/README.md +84 -0
- package/astro-i18nya/README.md +55 -7
- package/astro-i18nya/babel.config.js +8 -0
- package/astro-i18nya/jest.config.ts +7 -0
- package/astro-i18nya/package-lock.json +9265 -2163
- package/astro-i18nya/package.json +46 -3
- package/astro-i18nya/src/Trans.test.tsx +20 -0
- package/astro-i18nya/src/Trans.tsx +9 -6
- package/astro-i18nya/src/index.ts +12 -6
- package/astro-i18nya/src/util.ts +11 -1
- package/astro-i18nya/tsconfig.json +21 -7
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -13
- package/dist/index.js.map +1 -1
- package/examples/hello/__tests__/index.test.ts +2 -1
- package/package.json +7 -1
- package/src/index.ts +38 -25
- package/tsconfig.json +2 -2
|
@@ -1,15 +1,58 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro-i18nya",
|
|
3
|
-
"
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Astro integration for i18nya: i18n as small as a cat's paw",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
4
7
|
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"test": "jest"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/FyraLabs/i18nya.git"
|
|
15
|
+
},
|
|
16
|
+
"jest": {
|
|
17
|
+
"testPathIgnorePatterns": [
|
|
18
|
+
"<rootDir>/dist"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"astro",
|
|
23
|
+
"astro-integration",
|
|
24
|
+
"internationalization",
|
|
25
|
+
"i18n",
|
|
26
|
+
"translation",
|
|
27
|
+
"localization",
|
|
28
|
+
"l10n",
|
|
29
|
+
"globalization"
|
|
30
|
+
],
|
|
31
|
+
"author": "madonuko <mado@fyralabs.com>",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/FyraLabs/i18nya/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/FyraLabs/i18nya#readme",
|
|
5
37
|
"devDependencies": {
|
|
6
|
-
"@
|
|
38
|
+
"@babel/core": "^7.28.5",
|
|
39
|
+
"@babel/preset-env": "^7.28.5",
|
|
40
|
+
"@babel/preset-react": "^7.28.5",
|
|
41
|
+
"@babel/preset-typescript": "^7.28.5",
|
|
42
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
43
|
+
"@testing-library/react": "^16.3.0",
|
|
44
|
+
"@types/jest": "^30.0.0",
|
|
45
|
+
"@types/node": "^24.10.1",
|
|
46
|
+
"babel-jest": "^30.2.0",
|
|
47
|
+
"jest": "^30.2.0",
|
|
48
|
+
"jest-environment-jsdom": "^30.2.0",
|
|
49
|
+
"react-test-renderer": "^19.2.0",
|
|
50
|
+
"typescript": "^5.9.3"
|
|
7
51
|
},
|
|
8
52
|
"peerDependencies": {
|
|
9
53
|
"typescript": "^5"
|
|
10
54
|
},
|
|
11
55
|
"dependencies": {
|
|
12
|
-
"@astrojs/react": "^4.4.2",
|
|
13
56
|
"astro": "^5.16.0",
|
|
14
57
|
"i18nya": "^0.1.0",
|
|
15
58
|
"react": "^19.2.0"
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import "@testing-library/jest-dom";
|
|
3
|
+
import Trans from "./Trans";
|
|
4
|
+
|
|
5
|
+
describe("<Trans />", () => {
|
|
6
|
+
it("renders nested elements according to translation string", () => {
|
|
7
|
+
const translation = "Hello <1>user</1>, welcome to <2><1>my site</1></2>.";
|
|
8
|
+
render(
|
|
9
|
+
<Trans t={translation}>
|
|
10
|
+
<b />
|
|
11
|
+
<a href="https://example.com" />
|
|
12
|
+
</Trans>,
|
|
13
|
+
);
|
|
14
|
+
expect(screen.getByText("my site")).toBeInTheDocument();
|
|
15
|
+
const bold = screen.getByText("user");
|
|
16
|
+
expect(bold.tagName).toBe("B");
|
|
17
|
+
const link = screen.getByText("my site").closest("a");
|
|
18
|
+
expect(link).toHaveAttribute("href", "https://example.com");
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -13,6 +13,9 @@ type Props = {
|
|
|
13
13
|
* A fake version of `<Trans>` in `react-i18next` with *very different* behaviour.
|
|
14
14
|
*
|
|
15
15
|
* You feed in a list of empty elements in `<Trans>`. The structure will follow the translation strings.
|
|
16
|
+
*
|
|
17
|
+
* IMPORTANT: It is **HIGHLY RECOMMENDED** to use `experimentalReactChildren: true` for `@astrojs/react` in your astro config,
|
|
18
|
+
* otherwise this will not work correctly outside of `.tsx` files.
|
|
16
19
|
*
|
|
17
20
|
* @example ```tsx
|
|
18
21
|
* <Trans t={t("test", { user: "John" })}>
|
|
@@ -31,11 +34,11 @@ type Props = {
|
|
|
31
34
|
*/
|
|
32
35
|
export default (({ children, t }: Props) => {
|
|
33
36
|
// find /<\/(\d+)>/g, where group 1 parse to int is largest
|
|
34
|
-
const maxTagId =
|
|
35
|
-
.match(/<\/(\d+)>/g)
|
|
36
|
-
|
|
37
|
+
const maxTagId = Math.max(
|
|
38
|
+
...t.match(/<\/(\d+)>/g).map((i) => parseInt(i.slice(2, -1))),
|
|
39
|
+
);
|
|
37
40
|
const inputs = Children.toArray(children).filter((c) => isValidElement(c));
|
|
38
|
-
if (maxTagId ?? 0 > inputs.length) {
|
|
41
|
+
if ((maxTagId ?? 0) > inputs.length) {
|
|
39
42
|
return t; // syntax error
|
|
40
43
|
}
|
|
41
44
|
|
|
@@ -50,7 +53,7 @@ export default (({ children, t }: Props) => {
|
|
|
50
53
|
if (t.substring(ch_idx, ch_idx + 2) == "</") {
|
|
51
54
|
let j = 0;
|
|
52
55
|
while (t[++j + ch_idx] != ">" && j + ch_idx < t.length);
|
|
53
|
-
const tag = Number.parseInt(t.substring(ch_idx
|
|
56
|
+
const tag = Number.parseInt(t.substring(++ch_idx + 1, (ch_idx += j)));
|
|
54
57
|
if (Number.isNaN(tag)) {
|
|
55
58
|
elms.push(t.substring(ch_idx - j - 1, ch_idx));
|
|
56
59
|
continue;
|
|
@@ -67,7 +70,7 @@ export default (({ children, t }: Props) => {
|
|
|
67
70
|
if (t[ch_idx] == "<") {
|
|
68
71
|
let j = 0;
|
|
69
72
|
while (t[++j + ch_idx] != ">" && j + ch_idx < t.length);
|
|
70
|
-
const tag = Number.parseInt(t.substring(ch_idx
|
|
73
|
+
const tag = Number.parseInt(t.substring(++ch_idx, (ch_idx += j)));
|
|
71
74
|
if (Number.isNaN(tag)) {
|
|
72
75
|
elms.push(t.substring(ch_idx - j - 1, ch_idx));
|
|
73
76
|
continue;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { I18Nya } from "i18nya";
|
|
2
2
|
import type { AstroIntegration } from "astro";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
export { Trans, listLang, getLangName };
|
|
3
|
+
import Trans from "./Trans.js";
|
|
4
|
+
import { listLang, getLangName, makeGetStaticPaths } from "./util.js";
|
|
5
|
+
export { Trans, listLang, getLangName, makeGetStaticPaths };
|
|
7
6
|
|
|
8
|
-
export default function
|
|
7
|
+
export default function astroI18nya<T extends string | number | symbol>(
|
|
8
|
+
i18nya: I18Nya<T>,
|
|
9
|
+
): AstroIntegration {
|
|
9
10
|
return {
|
|
10
11
|
name: "astro-i18nya",
|
|
11
12
|
hooks: {
|
|
@@ -14,9 +15,14 @@ export default function (i18nya: I18Nya): AstroIntegration {
|
|
|
14
15
|
i18n: {
|
|
15
16
|
defaultLocale: i18nya.config.defaultLang,
|
|
16
17
|
locales: Object.keys(i18nya.translations),
|
|
18
|
+
routing: {
|
|
19
|
+
prefixDefaultLocale: false,
|
|
20
|
+
redirectToDefaultLocale: true,
|
|
21
|
+
fallbackType: "redirect",
|
|
22
|
+
},
|
|
17
23
|
},
|
|
18
24
|
// required for <Trans>
|
|
19
|
-
integrations: [react({ experimentalReactChildren: true })],
|
|
25
|
+
// integrations: [react({ experimentalReactChildren: true })],
|
|
20
26
|
});
|
|
21
27
|
},
|
|
22
28
|
},
|
package/astro-i18nya/src/util.ts
CHANGED
|
@@ -15,10 +15,20 @@ export const getLangName = (lang: string, displayLang?: string) =>
|
|
|
15
15
|
* @param displayLang in what language should the name of the language be shown. By default, use native language names.
|
|
16
16
|
* @returns a map of language locale → language name
|
|
17
17
|
*/
|
|
18
|
-
export const listLang =
|
|
18
|
+
export const listLang = <T extends string | number | symbol>(
|
|
19
|
+
i18nya: I18Nya<T>,
|
|
20
|
+
displayLang?: string,
|
|
21
|
+
) =>
|
|
19
22
|
new Map(
|
|
20
23
|
Object.keys(i18nya.translations).map((l) => [
|
|
21
24
|
l,
|
|
22
25
|
getLangName(l.replace("_", "-"), displayLang),
|
|
23
26
|
]),
|
|
24
27
|
);
|
|
28
|
+
|
|
29
|
+
export const makeGetStaticPaths =
|
|
30
|
+
<T extends string | number | symbol>(i18nya: I18Nya<T>) =>
|
|
31
|
+
async () =>
|
|
32
|
+
Object.keys(i18nya.translations)
|
|
33
|
+
.filter((lang) => lang !== i18nya.config.defaultLang)
|
|
34
|
+
.map((lang) => ({ params: { lang: lang } }));
|
|
@@ -1,29 +1,43 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"extends": "astro/tsconfigs/base",
|
|
3
|
+
"exclude": ["./dist", "*.config.*", "**/*.test.*"],
|
|
3
4
|
"compilerOptions": {
|
|
4
5
|
"rootDir": "./src",
|
|
5
6
|
"outDir": "./dist",
|
|
6
7
|
|
|
7
8
|
// Environment setup & latest features
|
|
8
|
-
"lib": ["ESNext"],
|
|
9
|
+
"lib": ["ESNext", "DOM"],
|
|
9
10
|
"target": "ESNext",
|
|
10
11
|
"module": "Preserve",
|
|
11
12
|
"moduleDetection": "force",
|
|
12
13
|
"jsx": "react-jsx",
|
|
14
|
+
"types": ["node"],
|
|
13
15
|
"allowJs": true,
|
|
14
16
|
|
|
15
|
-
//
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
17
|
+
// Output settings
|
|
18
|
+
"sourceMap": true,
|
|
19
|
+
"declaration": true,
|
|
20
|
+
"declarationMap": true,
|
|
21
|
+
|
|
22
|
+
// Module resolution
|
|
23
|
+
// "moduleResolution": "node16",
|
|
24
|
+
// "allowImportingTsExtensions": true,
|
|
25
|
+
"noEmit": false,
|
|
26
|
+
"allowImportingTsExtensions": false,
|
|
27
|
+
"verbatimModuleSyntax": false,
|
|
28
|
+
"isolatedModules": true,
|
|
29
|
+
"resolveJsonModule": true,
|
|
30
|
+
"esModuleInterop": true,
|
|
20
31
|
|
|
21
32
|
// Best practices
|
|
22
33
|
"strict": true,
|
|
23
34
|
"skipLibCheck": true,
|
|
24
35
|
"noFallthroughCasesInSwitch": true,
|
|
25
36
|
"noUncheckedIndexedAccess": true,
|
|
37
|
+
"strictNullChecks": false,
|
|
38
|
+
// "exactOptionalPropertyTypes": true,
|
|
26
39
|
"noImplicitOverride": true,
|
|
40
|
+
"noUncheckedSideEffectImports": true,
|
|
27
41
|
|
|
28
42
|
// Some stricter flags (disabled by default)
|
|
29
43
|
"noUnusedLocals": false,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type I18NyaKey = string | number | symbol;
|
|
2
|
+
export type I18NyaConfig<T extends I18NyaKey> = {
|
|
2
3
|
/** Path to directory containing language files */
|
|
3
4
|
langDir: string;
|
|
4
5
|
/** Locale ID for default language */
|
|
5
6
|
defaultLang: string;
|
|
6
7
|
fallbackLangs?: Record<string, string>;
|
|
8
|
+
/** Feed in `import.meta.glob("./langs/*.json", { eager: true })` */
|
|
9
|
+
viteImports?: Record<string, {
|
|
10
|
+
default: Record<T, string>;
|
|
11
|
+
}>;
|
|
7
12
|
};
|
|
8
|
-
export type I18Nya = {
|
|
9
|
-
translations: Record<string, Record<
|
|
10
|
-
makeT: (lang: string) => (key:
|
|
11
|
-
config: I18NyaConfig
|
|
13
|
+
export type I18Nya<T extends I18NyaKey> = {
|
|
14
|
+
translations: Record<string, Record<T, string>>;
|
|
15
|
+
makeT: (lang: string) => (key: T, its?: Interpolations) => string;
|
|
16
|
+
config: I18NyaConfig<T>;
|
|
12
17
|
};
|
|
13
18
|
export type Interpolations = Record<string, string | {
|
|
14
19
|
toString(): string;
|
|
15
20
|
}>;
|
|
16
|
-
export declare const init: (config: I18NyaConfig) => Promise<I18Nya
|
|
21
|
+
export declare const init: <T extends string | number | symbol = string>(config: I18NyaConfig<T>) => Promise<I18Nya<T>>;
|
|
22
|
+
export default init;
|
|
17
23
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjD,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,SAAS,IAAI;IAC9C,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC,CAAC;CAC9D,CAAC;AAEF,MAAM,MAAM,MAAM,CAAC,CAAC,SAAS,SAAS,IAAI;IACxC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,cAAc,KAAK,MAAM,CAAC;IAClE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,QAAQ,IAAI,MAAM,CAAA;CAAE,CAAC,CAAC;AAG7E,eAAO,MAAM,IAAI,GAAU,CAAC,SAAS,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,EACpE,QAAQ,YAAY,CAAC,CAAC,CAAC,uBAoCxB,CAAC;AACF,eAAe,IAAI,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.init = void 0;
|
|
4
|
-
const fs_1 = require("fs");
|
|
5
1
|
const opts = { with: { type: "json" } };
|
|
6
|
-
const init = async (config) => {
|
|
7
|
-
const { langDir, defaultLang: rootLang, fallbackLangs: fb = {}, } = config;
|
|
2
|
+
export const init = async (config) => {
|
|
3
|
+
const { langDir, defaultLang: rootLang = "en", fallbackLangs: fb = {}, viteImports = undefined, } = config;
|
|
8
4
|
let i18nya = {
|
|
9
5
|
translations: {},
|
|
10
6
|
makeT: (l) => (k, its = {}) => {
|
|
@@ -16,16 +12,23 @@ const init = async (config) => {
|
|
|
16
12
|
s = s.replace(`{{${k}}}`, `${v}`);
|
|
17
13
|
return s;
|
|
18
14
|
},
|
|
19
|
-
config
|
|
15
|
+
config,
|
|
20
16
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
if (viteImports) {
|
|
18
|
+
for (const [k, v] of Object.entries(viteImports))
|
|
19
|
+
i18nya.translations[k.slice(langDir.length + 1, -5)] = v.default;
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
const { readdirSync } = await import("fs");
|
|
23
|
+
const { resolve } = await import("path");
|
|
24
|
+
for (const entry of readdirSync(langDir, { withFileTypes: true })) {
|
|
25
|
+
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
26
|
+
const imp = await import(resolve(langDir, entry.name), opts);
|
|
27
|
+
i18nya.translations[entry.name.slice(0, -5)] = imp.default;
|
|
28
|
+
}
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
31
|
return i18nya;
|
|
29
32
|
};
|
|
30
|
-
|
|
33
|
+
export default init;
|
|
31
34
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAmBA,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;AAExC,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EACvB,MAAuB,EACvB,EAAE;IACF,MAAM,EACJ,OAAO,EACP,WAAW,EAAE,QAAQ,GAAG,IAAI,EAC5B,aAAa,EAAE,EAAE,GAAG,EAAE,EACtB,WAAW,GAAG,SAAS,GACxB,GAAG,MAAM,CAAC;IACX,IAAI,MAAM,GAAc;QACtB,YAAY,EAAE,EAAE;QAChB,KAAK,EACH,CAAC,CAAC,EAAE,EAAE,CACN,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,EAAE,EAAE;YACd,IAAI,CAAqB,CAAC;YAC1B,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,QAAQ;gBAC9D,IAAI,CAAC,KAAK,QAAQ;oBAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;YACvC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;gBACtC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACpC,OAAO,CAAC,CAAC;QACX,CAAC;QACH,MAAM;KACP,CAAC;IACF,IAAI,WAAW,EAAE,CAAC;QAChB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;YAC9C,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;IACrE,CAAC;SAAM,CAAC;QACN,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;QACzC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAClE,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;gBAC7D,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC;YAC7D,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AACF,eAAe,IAAI,CAAC"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { init } from "../../../src/index";
|
|
2
2
|
import path from "path";
|
|
3
|
+
import type defaultTranslations from "../langs/en.json";
|
|
3
4
|
|
|
4
5
|
describe("i18nya init and makeT", () => {
|
|
5
6
|
it('should return correct value for "hello"', async () => {
|
|
6
7
|
const langDir = path.resolve(__dirname, "../langs");
|
|
7
|
-
const { makeT } = await init({ defaultLang: "en", langDir });
|
|
8
|
+
const { makeT } = await init<keyof typeof defaultTranslations>({ defaultLang: "en", langDir });
|
|
8
9
|
|
|
9
10
|
expect(makeT("en")("hello")).toBe("Hello, World!");
|
|
10
11
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "i18nya",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "i18n as small as a cat's paw",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -26,6 +26,12 @@
|
|
|
26
26
|
"url": "https://github.com/FyraLabs/i18nya/issues"
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://github.com/FyraLabs/i18nya#readme",
|
|
29
|
+
"jest": {
|
|
30
|
+
"testPathIgnorePatterns": [
|
|
31
|
+
"<rootDir>/astro-i18nya",
|
|
32
|
+
"<rootDir>/dist"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
29
35
|
"devDependencies": {
|
|
30
36
|
"@babel/core": "^7.28.5",
|
|
31
37
|
"@babel/preset-env": "^7.28.5",
|
package/src/index.ts
CHANGED
|
@@ -1,47 +1,60 @@
|
|
|
1
|
-
|
|
1
|
+
export type I18NyaKey = string | number | symbol;
|
|
2
2
|
|
|
3
|
-
export type I18NyaConfig = {
|
|
3
|
+
export type I18NyaConfig<T extends I18NyaKey> = {
|
|
4
4
|
/** Path to directory containing language files */
|
|
5
5
|
langDir: string;
|
|
6
6
|
/** Locale ID for default language */
|
|
7
7
|
defaultLang: string;
|
|
8
8
|
fallbackLangs?: Record<string, string>;
|
|
9
|
+
/** Feed in `import.meta.glob("./langs/*.json", { eager: true })` */
|
|
10
|
+
viteImports?: Record<string, { default: Record<T, string> }>;
|
|
9
11
|
};
|
|
10
12
|
|
|
11
|
-
export type I18Nya = {
|
|
12
|
-
translations: Record<string, Record<
|
|
13
|
-
makeT: (lang: string) => (key:
|
|
14
|
-
config: I18NyaConfig
|
|
15
|
-
}
|
|
13
|
+
export type I18Nya<T extends I18NyaKey> = {
|
|
14
|
+
translations: Record<string, Record<T, string>>;
|
|
15
|
+
makeT: (lang: string) => (key: T, its?: Interpolations) => string;
|
|
16
|
+
config: I18NyaConfig<T>;
|
|
17
|
+
};
|
|
16
18
|
|
|
17
19
|
export type Interpolations = Record<string, string | { toString(): string }>;
|
|
18
20
|
const opts = { with: { type: "json" } };
|
|
19
21
|
|
|
20
|
-
export const init = async
|
|
22
|
+
export const init = async <T extends string | number | symbol = string>(
|
|
23
|
+
config: I18NyaConfig<T>,
|
|
24
|
+
) => {
|
|
21
25
|
const {
|
|
22
26
|
langDir,
|
|
23
|
-
defaultLang: rootLang,
|
|
27
|
+
defaultLang: rootLang = "en",
|
|
24
28
|
fallbackLangs: fb = {},
|
|
29
|
+
viteImports = undefined,
|
|
25
30
|
} = config;
|
|
26
|
-
let i18nya: I18Nya = {
|
|
31
|
+
let i18nya: I18Nya<T> = {
|
|
27
32
|
translations: {},
|
|
28
|
-
makeT:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
makeT:
|
|
34
|
+
(l) =>
|
|
35
|
+
(k, its = {}) => {
|
|
36
|
+
let s: string | undefined;
|
|
37
|
+
for (; !(s = i18nya.translations[l]?.[k]); l = fb[l] ?? rootLang)
|
|
38
|
+
if (l === rootLang) return String(k);
|
|
39
|
+
for (const [k, v] of Object.entries(its))
|
|
40
|
+
s = s.replace(`{{${k}}}`, `${v}`);
|
|
41
|
+
return s;
|
|
42
|
+
},
|
|
43
|
+
config,
|
|
38
44
|
};
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
if (viteImports) {
|
|
46
|
+
for (const [k, v] of Object.entries(viteImports))
|
|
47
|
+
i18nya.translations[k.slice(langDir.length + 1, -5)] = v.default;
|
|
48
|
+
} else {
|
|
49
|
+
const { readdirSync } = await import("fs");
|
|
50
|
+
const { resolve } = await import("path");
|
|
51
|
+
for (const entry of readdirSync(langDir, { withFileTypes: true })) {
|
|
52
|
+
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
53
|
+
const imp = await import(resolve(langDir, entry.name), opts);
|
|
54
|
+
i18nya.translations[entry.name.slice(0, -5)] = imp.default;
|
|
55
|
+
}
|
|
44
56
|
}
|
|
45
57
|
}
|
|
46
58
|
return i18nya;
|
|
47
59
|
};
|
|
60
|
+
export default init;
|
package/tsconfig.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"exclude": ["./dist", "./astro-i18nya", "./examples"],
|
|
2
|
+
"exclude": ["./dist", "./astro-i18nya", "./examples", "*.config.*"],
|
|
3
3
|
// Visit https://aka.ms/tsconfig to read more about this file
|
|
4
4
|
"compilerOptions": {
|
|
5
5
|
// File Layout
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
// Environment Settings
|
|
10
10
|
// See also https://aka.ms/tsconfig/module
|
|
11
|
-
"module": "
|
|
11
|
+
"module": "Preserve",
|
|
12
12
|
"target": "esnext",
|
|
13
13
|
// "types": [],
|
|
14
14
|
// For nodejs:
|