localize-ai 1.0.0
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/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +25 -0
- package/dist/cli.js.map +1 -0
- package/dist/context/LanguageContext.d.ts +9 -0
- package/dist/context/LanguageContext.d.ts.map +1 -0
- package/dist/context/LanguageContext.js +5 -0
- package/dist/context/LanguageContext.js.map +1 -0
- package/dist/context/LanguageContextProvider.d.ts +5 -0
- package/dist/context/LanguageContextProvider.d.ts.map +1 -0
- package/dist/context/LanguageContextProvider.js +38 -0
- package/dist/context/LanguageContextProvider.js.map +1 -0
- package/dist/generate.d.ts +2 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +16 -0
- package/dist/generate.js.map +1 -0
- package/dist/hooks/useTranslations.d.ts +7 -0
- package/dist/hooks/useTranslations.d.ts.map +1 -0
- package/dist/hooks/useTranslations.js +17 -0
- package/dist/hooks/useTranslations.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/loadConfig.d.ts +2 -0
- package/dist/loadConfig.d.ts.map +1 -0
- package/dist/loadConfig.js +14 -0
- package/dist/loadConfig.js.map +1 -0
- package/dist/react.d.ts +3 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +3 -0
- package/dist/react.js.map +1 -0
- package/dist/scripts/cleanTranslations.d.ts +2 -0
- package/dist/scripts/cleanTranslations.d.ts.map +1 -0
- package/dist/scripts/cleanTranslations.js +27 -0
- package/dist/scripts/cleanTranslations.js.map +1 -0
- package/dist/scripts/extractText.d.ts +2 -0
- package/dist/scripts/extractText.d.ts.map +1 -0
- package/dist/scripts/extractText.js +48 -0
- package/dist/scripts/extractText.js.map +1 -0
- package/dist/scripts/translate.d.ts +2 -0
- package/dist/scripts/translate.d.ts.map +1 -0
- package/dist/scripts/translate.js +115 -0
- package/dist/scripts/translate.js.map +1 -0
- package/package.json +54 -0
- package/readme.md +223 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import { generateRuntimeConfig } from "./generate.js";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
const command = process.argv[2];
|
|
9
|
+
if (command === "init") {
|
|
10
|
+
generateRuntimeConfig();
|
|
11
|
+
}
|
|
12
|
+
else if (command === "translate") {
|
|
13
|
+
console.log("๐ Running localization pipeline...");
|
|
14
|
+
const extractPath = path.join(__dirname, "scripts", "extractText.js");
|
|
15
|
+
const cleanPath = path.join(__dirname, "scripts", "cleanTranslations.js");
|
|
16
|
+
const translatePath = path.join(__dirname, "scripts", "translate.js");
|
|
17
|
+
execSync(`node "${extractPath}"`, { stdio: "inherit" });
|
|
18
|
+
execSync(`node "${cleanPath}"`, { stdio: "inherit" });
|
|
19
|
+
execSync(`node "${translatePath}"`, { stdio: "inherit" });
|
|
20
|
+
console.log("โ
Done");
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
console.log("โ Unknown command");
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAC3C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEhC,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;IACvB,qBAAqB,EAAE,CAAC;AAC1B,CAAC;KACI,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;IACjC,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IAEnD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;IACtE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,sBAAsB,CAAC,CAAC;IAC1E,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;IAEtE,QAAQ,CAAC,SAAS,WAAW,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IACxD,QAAQ,CAAC,SAAS,SAAS,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IACtD,QAAQ,CAAC,SAAS,aAAa,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IAE1D,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACxB,CAAC;KACI,CAAC;IACJ,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type LanguageContextType = {
|
|
2
|
+
lang: string;
|
|
3
|
+
supportedLangs: string[];
|
|
4
|
+
setLang: (lang: string) => void;
|
|
5
|
+
translations: Record<string, Record<string, string>>;
|
|
6
|
+
};
|
|
7
|
+
declare const LanguageContext: import("react").Context<LanguageContextType | null>;
|
|
8
|
+
export default LanguageContext;
|
|
9
|
+
//# sourceMappingURL=LanguageContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LanguageContext.d.ts","sourceRoot":"","sources":["../../src/context/LanguageContext.ts"],"names":[],"mappings":"AAEA,KAAK,mBAAmB,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAExD,CAAC;AAGF,QAAA,MAAM,eAAe,qDAAkD,CAAC;AAExE,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LanguageContext.js","sourceRoot":"","sources":["../../src/context/LanguageContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAUlD,2BAA2B;AAC3B,MAAM,eAAe,GAAG,aAAa,CAA6B,IAAI,CAAC,CAAC;AAExE,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LanguageContextProvider.d.ts","sourceRoot":"","sources":["../../src/context/LanguageContextProvider.tsx"],"names":[],"mappings":"AAGA,iBAAS,uBAAuB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,kDA6C3E;AAED,eAAe,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import LanguageContext from "./LanguageContext.js";
|
|
4
|
+
function LanguageContextProvider({ children }) {
|
|
5
|
+
const [lang, setLang] = useState("en");
|
|
6
|
+
const [supportedLangs, setSupportedLangs] = useState([]);
|
|
7
|
+
const [translations, setTranslations] = useState({});
|
|
8
|
+
const [isReady, setIsReady] = useState(false);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
async function load() {
|
|
11
|
+
try {
|
|
12
|
+
const configRes = await fetch("/localize.runtime.json");
|
|
13
|
+
const config = await configRes.json();
|
|
14
|
+
setSupportedLangs(config.supportedLangs || []);
|
|
15
|
+
setLang(config.supportedLangs?.includes(config.sourceLanguage)
|
|
16
|
+
? config.sourceLanguage
|
|
17
|
+
: "en");
|
|
18
|
+
const transRes = await fetch("/translations.json");
|
|
19
|
+
if (!transRes.ok)
|
|
20
|
+
throw new Error("No translations");
|
|
21
|
+
const data = await transRes.json();
|
|
22
|
+
setTranslations(data);
|
|
23
|
+
setIsReady(true);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
console.warn("โ ๏ธ Failed to load localization", err);
|
|
27
|
+
setTranslations({});
|
|
28
|
+
setIsReady(true);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
load();
|
|
32
|
+
}, []);
|
|
33
|
+
if (!isReady)
|
|
34
|
+
return null;
|
|
35
|
+
return (_jsx(LanguageContext.Provider, { value: { lang, setLang, supportedLangs, translations }, children: children }));
|
|
36
|
+
}
|
|
37
|
+
export default LanguageContextProvider;
|
|
38
|
+
//# sourceMappingURL=LanguageContextProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LanguageContextProvider.js","sourceRoot":"","sources":["../../src/context/LanguageContextProvider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,eAAe,MAAM,sBAAsB,CAAC;AAEnD,SAAS,uBAAuB,CAAC,EAAE,QAAQ,EAAiC;IAC1E,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAS,IAAI,CAAC,CAAC;IAC/C,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAW,EAAE,CAAC,CAAC;IACnE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAM,EAAE,CAAC,CAAC;IAC1D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE9C,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,UAAU,IAAI;YACjB,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,wBAAwB,CAAC,CAAC;gBACxD,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;gBAEtC,iBAAiB,CAAC,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC;gBAC/C,OAAO,CACL,MAAM,CAAC,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC;oBACpD,CAAC,CAAC,MAAM,CAAC,cAAc;oBACvB,CAAC,CAAC,IAAI,CACT,CAAC;gBAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,oBAAoB,CAAC,CAAC;gBACnD,IAAI,CAAC,QAAQ,CAAC,EAAE;oBAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;gBAErD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACnC,eAAe,CAAC,IAAI,CAAC,CAAC;gBAEtB,UAAU,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;gBACpD,eAAe,CAAC,EAAE,CAAC,CAAC;gBACpB,UAAU,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QAED,IAAI,EAAE,CAAC;IACT,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,OAAO,CACL,KAAC,eAAe,CAAC,QAAQ,IACvB,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAErD,QAAQ,GACgB,CAC5B,CAAC;AACJ,CAAC;AAED,eAAe,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAIA,wBAAsB,qBAAqB,kBAe1C"}
|
package/dist/generate.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { loadUserConfig } from "./loadConfig.js";
|
|
4
|
+
export async function generateRuntimeConfig() {
|
|
5
|
+
const config = await loadUserConfig();
|
|
6
|
+
// Transform config if needed
|
|
7
|
+
const runtimeConfig = {
|
|
8
|
+
sourceLanguage: config.sourceLanguage,
|
|
9
|
+
supportedLangs: config.translationLanguages,
|
|
10
|
+
apikey: config.apikey
|
|
11
|
+
};
|
|
12
|
+
const outputPath = path.resolve(process.cwd(), "localize.runtime.json");
|
|
13
|
+
fs.writeFileSync(outputPath, JSON.stringify(runtimeConfig, null, 2));
|
|
14
|
+
console.log("โ
localize.runtime.json generated successfully!");
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,MAAM,MAAM,GAAG,MAAM,cAAc,EAAE,CAAC;IAEtC,6BAA6B;IAC7B,MAAM,aAAa,GAAG;QACpB,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,cAAc,EAAE,MAAM,CAAC,oBAAoB;QAC3C,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC;IAEF,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,uBAAuB,CAAC,CAAC;IAExE,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAErE,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTranslations.d.ts","sourceRoot":"","sources":["../../src/hooks/useTranslations.ts"],"names":[],"mappings":"AAGA,wBAAgB,cAAc;aASZ,MAAM,KAAG,MAAM;;;;EAShC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import LanguageContext from "../context/LanguageContext.js";
|
|
3
|
+
export function useTranslation() {
|
|
4
|
+
const context = useContext(LanguageContext);
|
|
5
|
+
if (!context) {
|
|
6
|
+
throw new Error("useTranslation must be used within LanguageContextProvider");
|
|
7
|
+
}
|
|
8
|
+
const { lang, translations, setLang, supportedLangs } = context;
|
|
9
|
+
function t(key) {
|
|
10
|
+
const entry = translations[key];
|
|
11
|
+
if (!entry)
|
|
12
|
+
return key;
|
|
13
|
+
return entry[lang] || entry["en"] || key;
|
|
14
|
+
}
|
|
15
|
+
return { t, setLang, lang, supportedLangs };
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=useTranslations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTranslations.js","sourceRoot":"","sources":["../../src/hooks/useTranslations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,eAAe,MAAM,+BAA+B,CAAC;AAE5D,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;IAE5C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC;IAEhE,SAAS,CAAC,CAAC,GAAW;QACpB,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;QAEhC,IAAI,CAAC,KAAK;YAAE,OAAO,GAAG,CAAC;QAEvB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC;IAC3C,CAAC;IAED,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;AAC9C,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadConfig.d.ts","sourceRoot":"","sources":["../src/loadConfig.ts"],"names":[],"mappings":"AAGA,wBAAsB,cAAc,iBAUnC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { pathToFileURL } from "url";
|
|
3
|
+
export async function loadUserConfig() {
|
|
4
|
+
const configPath = path.resolve(process.cwd(), "localize.config.js");
|
|
5
|
+
try {
|
|
6
|
+
const config = await import(pathToFileURL(configPath).href);
|
|
7
|
+
return config.default;
|
|
8
|
+
}
|
|
9
|
+
catch (err) {
|
|
10
|
+
console.error("โ Failed to load localize.config.js");
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=loadConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadConfig.js","sourceRoot":"","sources":["../src/loadConfig.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,oBAAoB,CAAC,CAAC;IAErE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5D,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/react.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC1F,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanTranslations.d.ts","sourceRoot":"","sources":["../../src/scripts/cleanTranslations.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
const ROOT = process.cwd();
|
|
4
|
+
const INPUT = path.join(ROOT, "extractedText.json");
|
|
5
|
+
const OUTPUT = path.join(ROOT, "cleaned_strings.json");
|
|
6
|
+
function isValid(text) {
|
|
7
|
+
if (!text || text.length < 3)
|
|
8
|
+
return false;
|
|
9
|
+
if (/^\d+$/.test(text))
|
|
10
|
+
return false;
|
|
11
|
+
if (text.includes("@"))
|
|
12
|
+
return false;
|
|
13
|
+
if (/^[^a-zA-Z]+$/.test(text))
|
|
14
|
+
return false;
|
|
15
|
+
if (text.includes("\\\\"))
|
|
16
|
+
return false;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (!fs.existsSync(INPUT)) {
|
|
20
|
+
console.error("โ translations_en.json not found");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
const raw = JSON.parse(fs.readFileSync(INPUT, "utf-8"));
|
|
24
|
+
const cleaned = raw.filter(isValid);
|
|
25
|
+
fs.writeFileSync(OUTPUT, JSON.stringify(cleaned, null, 2));
|
|
26
|
+
console.log(`โ
Cleaned strings: ${cleaned.length}`);
|
|
27
|
+
//# sourceMappingURL=cleanTranslations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanTranslations.js","sourceRoot":"","sources":["../../src/scripts/cleanTranslations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;AAE3B,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC,CAAC;AACpD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;AAEvD,SAAS,OAAO,CAAC,IAAY;IAC3B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;IAC1B,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,GAAG,GAAa,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAElE,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAEpC,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAE3D,OAAO,CAAC,GAAG,CAAC,sBAAsB,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractText.d.ts","sourceRoot":"","sources":["../../src/scripts/extractText.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
const ROOT = process.cwd(); // โ
user project
|
|
4
|
+
const SRC_DIR = path.join(ROOT, "src");
|
|
5
|
+
const OUTPUT_FILE = path.join(ROOT, "extractedText.json");
|
|
6
|
+
let existingStrings = new Set();
|
|
7
|
+
if (fs.existsSync(OUTPUT_FILE)) {
|
|
8
|
+
existingStrings = new Set(JSON.parse(fs.readFileSync(OUTPUT_FILE, "utf-8")));
|
|
9
|
+
}
|
|
10
|
+
const newStrings = new Set();
|
|
11
|
+
function isValidText(text) {
|
|
12
|
+
if (!text || text.length < 2)
|
|
13
|
+
return false;
|
|
14
|
+
if (/^\d+$/.test(text))
|
|
15
|
+
return false;
|
|
16
|
+
if (text.includes("@"))
|
|
17
|
+
return false;
|
|
18
|
+
if (/^[A-Z0-9-]+$/.test(text))
|
|
19
|
+
return false;
|
|
20
|
+
if (/โน|\$|โฌ/.test(text))
|
|
21
|
+
return false;
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
function scanDir(dir) {
|
|
25
|
+
const files = fs.readdirSync(dir);
|
|
26
|
+
for (const file of files) {
|
|
27
|
+
const fullPath = path.join(dir, file);
|
|
28
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
29
|
+
scanDir(fullPath);
|
|
30
|
+
}
|
|
31
|
+
else if (/\.(tsx|jsx|ts|js)$/.test(file)) {
|
|
32
|
+
const content = fs.readFileSync(fullPath, "utf-8");
|
|
33
|
+
const regex = /t\(\s*["'`]([^"'`]+)["'`]\s*(?:,\s*[a-zA-Z0-9_.]+)?\s*\)/g;
|
|
34
|
+
let match;
|
|
35
|
+
while ((match = regex.exec(content)) !== null) {
|
|
36
|
+
const text = match[1].trim();
|
|
37
|
+
if (isValidText(text) && !existingStrings.has(text)) {
|
|
38
|
+
newStrings.add(text);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
scanDir(SRC_DIR);
|
|
45
|
+
const updated = [...existingStrings, ...newStrings];
|
|
46
|
+
fs.writeFileSync(OUTPUT_FILE, JSON.stringify(updated, null, 2));
|
|
47
|
+
console.log(`โ
Added ${newStrings.size} new strings`);
|
|
48
|
+
//# sourceMappingURL=extractText.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractText.js","sourceRoot":"","sources":["../../src/scripts/extractText.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,iBAAiB;AAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACvC,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAC,oBAAoB,CAAC,CAAC;AAEzD,IAAI,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;AAExC,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;IAC/B,eAAe,GAAG,IAAI,GAAG,CACvB,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAClD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;AAErC,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3C,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACrC,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACtC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,OAAO,CAAC,GAAW;IAC1B,MAAM,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAElC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAEtC,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YACxC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpB,CAAC;aAAM,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAEnD,MAAM,KAAK,GACT,2DAA2D,CAAC;YAE9D,IAAI,KAA6B,CAAC;YAExC,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC;gBAE9B,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;oBACpD,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC;QACG,CAAC;IACH,CAAC;AACH,CAAC;AAED,OAAO,CAAC,OAAO,CAAC,CAAC;AAEjB,MAAM,OAAO,GAAG,CAAC,GAAG,eAAe,EAAE,GAAG,UAAU,CAAC,CAAC;AAEpD,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAEhE,OAAO,CAAC,GAAG,CAAC,WAAW,UAAU,CAAC,IAAI,cAAc,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../src/scripts/translate.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import dotenv from "dotenv";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
5
|
+
dotenv.config({ path: path.resolve(process.cwd(), ".env") }); // โ
USER ENV
|
|
6
|
+
const ROOT = process.cwd();
|
|
7
|
+
const runtimeConfigPath = path.join(ROOT, "localize.runtime.json");
|
|
8
|
+
if (!fs.existsSync(runtimeConfigPath)) {
|
|
9
|
+
console.error("โ localize.runtime.json not found. Run init first.");
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const runtimeConfig = JSON.parse(fs.readFileSync(runtimeConfigPath, "utf-8"));
|
|
13
|
+
const { sourceLanguage, supportedLangs, apikey } = runtimeConfig;
|
|
14
|
+
const API_KEY = process.env[apikey];
|
|
15
|
+
if (!API_KEY) {
|
|
16
|
+
console.error("โ GEMINI_API_KEY missing in .env");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const genAI = new GoogleGenerativeAI(API_KEY);
|
|
20
|
+
const INPUT = path.join(ROOT, "cleaned_strings.json");
|
|
21
|
+
const OUTPUT = path.join(ROOT, "public", "translations.json");
|
|
22
|
+
const CHUNK_SIZE = 100;
|
|
23
|
+
function chunk(arr, size) {
|
|
24
|
+
const res = [];
|
|
25
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
26
|
+
res.push(arr.slice(i, i + size));
|
|
27
|
+
}
|
|
28
|
+
return res;
|
|
29
|
+
}
|
|
30
|
+
// ๐ฅ NEW: detect missing languages per text
|
|
31
|
+
function getMissingLangs(text, existing, supportedLangs) {
|
|
32
|
+
const existingEntry = existing[text] || {};
|
|
33
|
+
return supportedLangs.filter((lang) => !existingEntry[lang]);
|
|
34
|
+
}
|
|
35
|
+
function buildPrompt(chunk, existing, supportedLangs) {
|
|
36
|
+
const instructions = chunk.map((text) => {
|
|
37
|
+
const missing = getMissingLangs(text, existing, supportedLangs);
|
|
38
|
+
return `"${text}": {
|
|
39
|
+
"${sourceLanguage}": "${text}",
|
|
40
|
+
${missing.map((l) => `"${l}": "..."`).join(",\n ")}
|
|
41
|
+
}`;
|
|
42
|
+
});
|
|
43
|
+
return `
|
|
44
|
+
You are a translation engine.
|
|
45
|
+
|
|
46
|
+
Translate ONLY the missing languages.
|
|
47
|
+
|
|
48
|
+
Rules:
|
|
49
|
+
- Return ONLY valid JSON
|
|
50
|
+
- No explanation
|
|
51
|
+
- No markdown
|
|
52
|
+
- Do NOT overwrite existing translations
|
|
53
|
+
|
|
54
|
+
Structure:
|
|
55
|
+
{
|
|
56
|
+
${instructions.join(",\n")}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
Input:
|
|
60
|
+
${JSON.stringify(chunk)}
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
async function translateChunk(model, chunkData, existing, supportedLangs) {
|
|
64
|
+
const prompt = buildPrompt(chunkData, existing, supportedLangs);
|
|
65
|
+
const result = await model.generateContent(prompt);
|
|
66
|
+
const response = await result.response;
|
|
67
|
+
let text = response.text();
|
|
68
|
+
text = text.replace(/```json|```/g, "").trim();
|
|
69
|
+
return JSON.parse(text);
|
|
70
|
+
}
|
|
71
|
+
async function run() {
|
|
72
|
+
const model = genAI.getGenerativeModel({
|
|
73
|
+
model: "gemini-2.5-flash",
|
|
74
|
+
});
|
|
75
|
+
const texts = JSON.parse(fs.readFileSync(INPUT, "utf-8"));
|
|
76
|
+
let existing = {};
|
|
77
|
+
if (fs.existsSync(OUTPUT)) {
|
|
78
|
+
existing = JSON.parse(fs.readFileSync(OUTPUT, "utf-8"));
|
|
79
|
+
}
|
|
80
|
+
const filtered = texts.filter((t) => {
|
|
81
|
+
const existingEntry = existing[t];
|
|
82
|
+
// new string โ translate
|
|
83
|
+
if (!existingEntry)
|
|
84
|
+
return true;
|
|
85
|
+
// check if any language missing
|
|
86
|
+
const missingLangs = supportedLangs.filter((lang) => !existingEntry[lang]);
|
|
87
|
+
return missingLangs.length > 0;
|
|
88
|
+
});
|
|
89
|
+
const chunks = chunk(filtered, CHUNK_SIZE);
|
|
90
|
+
let final = { ...existing };
|
|
91
|
+
for (const [i, chunk] of chunks.entries()) {
|
|
92
|
+
console.log(`๐ Chunk ${i + 1}/${chunks.length}`);
|
|
93
|
+
try {
|
|
94
|
+
const res = await translateChunk(model, chunk, existing, supportedLangs);
|
|
95
|
+
for (const key in res) {
|
|
96
|
+
if (!final[key]) {
|
|
97
|
+
final[key] = res[key];
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
final[key] = {
|
|
101
|
+
...final[key],
|
|
102
|
+
...res[key], // only adds missing languages
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
console.log("โ chunk failed");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
fs.writeFileSync(OUTPUT, JSON.stringify(final, null, 2));
|
|
112
|
+
console.log("โ
translations.json created");
|
|
113
|
+
}
|
|
114
|
+
run();
|
|
115
|
+
//# sourceMappingURL=translate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translate.js","sourceRoot":"","sources":["../../src/scripts/translate.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,aAAa;AAE3E,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;AAC3B,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAAC;AACnE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;IACtC,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AACD,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAC9B,EAAE,CAAC,YAAY,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAC5C,CAAC;AACF,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC;AAEjE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAEpC,IAAI,CAAC,OAAO,EAAE,CAAC;IACb,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,KAAK,GAAG,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAE9C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;AACtD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAC,QAAQ,EAAC,mBAAmB,CAAC,CAAC;AAE5D,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB,SAAS,KAAK,CAAC,GAAa,EAAE,IAAY;IACxC,MAAM,GAAG,GAAG,EAAE,CAAC;IACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC;QAC1C,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,4CAA4C;AAC5C,SAAS,eAAe,CAAC,IAAY,EAAE,QAAa,EAAE,cAAwB;IAC5E,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IAC3C,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,WAAW,CAAC,KAAe,EAAE,QAAa,EAAE,cAAwB;IAC3E,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACtC,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;QAEhE,OAAO,IAAI,IAAI;SACV,cAAc,OAAO,IAAI;QAC1B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC;MAC/D,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO;;;;;;;;;;;;;IAaL,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC;;;;EAI1B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;CACtB,CAAC;AACF,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,KAAU,EAAE,SAAmB,EAAE,QAAa,EAAE,cAAwB;IACpG,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;IAEhE,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC;IAEvC,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAE/C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,KAAK,UAAU,GAAG;IAChB,MAAM,KAAK,GAAG,KAAK,CAAC,kBAAkB,CAAC;QACrC,KAAK,EAAE,kBAAkB;KAC1B,CAAC,CAAC;IAEH,MAAM,KAAK,GAAa,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IAEpE,IAAI,QAAQ,GAAwB,EAAE,CAAC;IAEvC,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,CAAC;IAID,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACpC,MAAM,aAAa,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAElC,yBAAyB;QACzB,IAAI,CAAC,aAAa;YAAE,OAAO,IAAI,CAAC;QAEhC,gCAAgC;QAChC,MAAM,YAAY,GAAG,cAAc,CAAC,MAAM,CACxC,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CACvC,CAAC;QAEF,OAAO,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAE3C,IAAI,KAAK,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;IAE5B,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5C,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAElD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;YACzE,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;gBAC1B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;oBAChB,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;gBACxB,CAAC;qBAAM,CAAC;oBACN,KAAK,CAAC,GAAG,CAAC,GAAG;wBACX,GAAG,KAAK,CAAC,GAAG,CAAC;wBACb,GAAG,GAAG,CAAC,GAAG,CAAC,EAAE,8BAA8B;qBAC5C,CAAC;gBACJ,CAAC;YACH,CAAC;QACC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAEC,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAEzD,OAAO,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;AAC7C,CAAC;AAED,GAAG,EAAE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "localize-ai",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "plug and play localization package",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": ["dist"],
|
|
9
|
+
"module": "dist/index.js",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"localize-ai": "dist/cli.js"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"localization",
|
|
25
|
+
"i18n",
|
|
26
|
+
"translation",
|
|
27
|
+
"react-i18n",
|
|
28
|
+
"ai-translation",
|
|
29
|
+
"gemini"
|
|
30
|
+
],
|
|
31
|
+
"author": "Parth Gupta",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@google/generative-ai": "^0.24.1",
|
|
35
|
+
"dotenv": "^17.3.1"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"react": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"typescript": "^5.9.3",
|
|
42
|
+
"@types/node": "^25.5.0",
|
|
43
|
+
"@types/react": "^19.2.14",
|
|
44
|
+
"react": "^19.2.4"
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/Parth-Gupta05/localize-ai.git"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/Parth-Gupta05/localize-ai",
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/Parth-Gupta05/localize-ai/issues"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# ๐ localize-ai
|
|
2
|
+
|
|
3
|
+
**Plug & Play AI-powered localization for React apps**
|
|
4
|
+
|
|
5
|
+
Automatically extract, translate, and serve multilingual UI โ powered by AI โก
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## โจ Features
|
|
10
|
+
|
|
11
|
+
* ๐ Auto-extract static text from your codebase (`t("...")`)
|
|
12
|
+
* ๐ AI-powered translations (Gemini)
|
|
13
|
+
* โก Incremental translation (only new strings + languages)
|
|
14
|
+
* ๐ง Zero config runtime (auto loads translations)
|
|
15
|
+
* โ๏ธ React hooks + context out of the box
|
|
16
|
+
* ๐ธ Cost optimized (no re-translation of existing content)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## ๐ฆ Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install localize-ai
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## โ๏ธ Setup (2 steps)
|
|
29
|
+
|
|
30
|
+
### 1๏ธโฃ Create config
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
// localize.config.js
|
|
34
|
+
export default {
|
|
35
|
+
sourceLanguage: "en",
|
|
36
|
+
translationLanguages: ["hi", "fr"],
|
|
37
|
+
provider: "gemini",
|
|
38
|
+
apikey: "VITE_GEMINI_API_KEY"
|
|
39
|
+
};
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
### 2๏ธโฃ Add your API key
|
|
45
|
+
|
|
46
|
+
```env
|
|
47
|
+
VITE_GEMINI_API_KEY=your_api_key_here
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## ๐ Usage
|
|
53
|
+
|
|
54
|
+
### Step 1: Initialize
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx localize-ai init
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
### Step 2: Extract & translate
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx localize-ai translate
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This will:
|
|
69
|
+
|
|
70
|
+
* scan your codebase
|
|
71
|
+
* extract `t("text")`
|
|
72
|
+
* generate translations
|
|
73
|
+
* create:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
public/translations.json
|
|
77
|
+
public/localize.runtime.json
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### Step 3: Wrap your app
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import { LanguageContextProvider } from "localize-ai";
|
|
86
|
+
|
|
87
|
+
<LanguageContextProvider>
|
|
88
|
+
<App />
|
|
89
|
+
</LanguageContextProvider>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
### Step 4: Use translations
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { useTranslation } from "localize-ai";
|
|
98
|
+
|
|
99
|
+
function App() {
|
|
100
|
+
const { t } = useTranslation();
|
|
101
|
+
|
|
102
|
+
return <h1>{t("Hello world")}</h1>;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## ๐ Change language
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
const { setLang } = useTranslation();
|
|
112
|
+
|
|
113
|
+
setLang("hi"); // switch language
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## ๐ง How it works
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
Code โ Extract โ Clean โ AI Translate โ JSON โ React Context โ UI
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
* Only new strings are translated
|
|
125
|
+
* Only missing languages are generated
|
|
126
|
+
* Existing translations are reused
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## ๐ Output
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
public/
|
|
134
|
+
โโโ translations.json
|
|
135
|
+
โโโ localize.runtime.json
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## โก Example
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
<h1>{t("Get started")}</h1>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
โก๏ธ Automatically becomes:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"Get started": {
|
|
151
|
+
"en": "Get started",
|
|
152
|
+
"hi": "เคถเฅเคฐเฅ เคเคฐเฅเค",
|
|
153
|
+
"fr": "Commencer"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## ๐ CLI Commands
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npx localize-ai init # generate runtime config
|
|
164
|
+
npx localize-ai translate # extract + translate
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## ๐ฏ Why localize-ai?
|
|
170
|
+
|
|
171
|
+
| Feature | localize-ai | traditional i18n |
|
|
172
|
+
| ------------------- | ----------- | ---------------- |
|
|
173
|
+
| Auto extraction | โ
| โ |
|
|
174
|
+
| AI translation | โ
| โ |
|
|
175
|
+
| Incremental updates | โ
| โ |
|
|
176
|
+
| Setup time | โก minutes | โณ hours |
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## โ ๏ธ Notes
|
|
181
|
+
|
|
182
|
+
* Only static strings inside `t("...")` are extracted
|
|
183
|
+
* Ensure `public/` folder exists
|
|
184
|
+
* API key is required only for translation step
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## ๐บ Roadmap
|
|
189
|
+
|
|
190
|
+
* [ ] AST-based extraction (no regex)
|
|
191
|
+
* [ ] Multi-provider support (OpenAI, etc.)
|
|
192
|
+
* [ ] Lazy loading translations
|
|
193
|
+
* [ ] CLI UI improvements
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## ๐ค Contributing
|
|
198
|
+
|
|
199
|
+
PRs welcome! Feel free to open issues or suggest improvements.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## ๐ License
|
|
204
|
+
|
|
205
|
+
MIT
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## ๐ฌ Author
|
|
210
|
+
|
|
211
|
+
**Parth Gupta**
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## โญ Support
|
|
216
|
+
|
|
217
|
+
If you like this project:
|
|
218
|
+
|
|
219
|
+
๐ Star the repo
|
|
220
|
+
๐ Share with developers
|
|
221
|
+
๐ Give feedback
|
|
222
|
+
|
|
223
|
+
---
|