glotstack 0.0.2 → 0.0.4
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 +1 -0
- package/dist/cli.js +106 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +13 -7
- package/dist/index.js +70 -81
- package/dist/index.js.map +1 -1
- package/dist/util/findConfig.d.ts +6 -0
- package/dist/util/findConfig.js +50 -0
- package/dist/util/findConfig.js.map +1 -0
- package/dist/util/object.js +1 -1
- package/dist/util/object.js.map +1 -1
- package/dist/util/yaml.d.ts +1 -0
- package/dist/util/yaml.js +18 -0
- package/dist/util/yaml.js.map +1 -0
- package/package.json +13 -7
- package/scripts/fix-shebang.sh +28 -0
- package/src/cli.tsx +103 -0
- package/src/index.tsx +80 -90
- package/src/util/findConfig.ts +26 -0
- package/src/util/object.ts +1 -2
- package/src/util/yaml.ts +11 -0
- package/tsconfig.json +1 -2
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
6
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
7
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
8
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
9
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
const commander_1 = require("commander");
|
|
17
|
+
const path_1 = __importDefault(require("path"));
|
|
18
|
+
const fs_1 = require("fs");
|
|
19
|
+
const findConfig_1 = require("./util/findConfig");
|
|
20
|
+
const process_1 = require("process");
|
|
21
|
+
const object_1 = require("./util/object");
|
|
22
|
+
const yaml_1 = require("./util/yaml");
|
|
23
|
+
function run(args) {
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
commander_1.program
|
|
26
|
+
.command('get-translations')
|
|
27
|
+
.option('--source-path [path]', 'path to en-US.json (or your canonical source json)')
|
|
28
|
+
.option('--api-origin [url]', 'glotstack api origin', 'http://localhost:4001')
|
|
29
|
+
.option('--output-dir [path]', 'path to output directory')
|
|
30
|
+
.option('--api-key [key]', 'api key for glotstack.ai')
|
|
31
|
+
.option('--project-id [id]', '(optional) specific project to use')
|
|
32
|
+
.argument('[output-locales...]', 'locales to get translations for')
|
|
33
|
+
.action((outputLocales, options, command) => __awaiter(this, void 0, void 0, function* () {
|
|
34
|
+
const configPath = (0, findConfig_1.findGlotstackConfig)((0, process_1.cwd)());
|
|
35
|
+
let config = {};
|
|
36
|
+
if (configPath != null) {
|
|
37
|
+
console.info('Loading config file at ', configPath);
|
|
38
|
+
try {
|
|
39
|
+
const text = yield fs_1.promises.readFile(configPath, 'utf-8');
|
|
40
|
+
config = JSON.parse(text);
|
|
41
|
+
console.info('Loaded config file', config);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
//pass
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const resolved = (0, object_1.merge)(config, options, { outputLocales });
|
|
48
|
+
const { apiOrigin, sourcePath, outputDir, projectId } = resolved;
|
|
49
|
+
if (resolved.outputLocales.includes('en-US')) {
|
|
50
|
+
console.warn('en-US detected in outputLocales, removing');
|
|
51
|
+
resolved.outputLocales = resolved.outputLocales.filter((x) => x !== 'en-US');
|
|
52
|
+
}
|
|
53
|
+
if (!sourcePath) {
|
|
54
|
+
throw new Error('sourcePath must be specified');
|
|
55
|
+
}
|
|
56
|
+
if (!apiOrigin) {
|
|
57
|
+
throw new Error('apiOrigin must be specified');
|
|
58
|
+
}
|
|
59
|
+
if (!outputDir) {
|
|
60
|
+
throw new Error('outputDir must be specified');
|
|
61
|
+
}
|
|
62
|
+
const url = `${apiOrigin}/api/translations`;
|
|
63
|
+
console.info(`Fetching translations from: ${url}`);
|
|
64
|
+
const absPath = path_1.default.resolve(sourcePath);
|
|
65
|
+
const fileContent = yield fs_1.promises.readFile(absPath, 'utf-8');
|
|
66
|
+
let json = null;
|
|
67
|
+
try {
|
|
68
|
+
json = (0, yaml_1.loadYaml)(fileContent);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
try {
|
|
72
|
+
json = JSON.parse(fileContent);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
console.error('Unable to parse source file ', absPath, err);
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const body = Object.assign({ locales: resolved.outputLocales, translations: json }, Object.assign({}, (projectId != null ? { projectId } : {})));
|
|
80
|
+
const headers = {
|
|
81
|
+
'Content-Type': 'application/json',
|
|
82
|
+
'Authorization': `Bearer ${resolved.apiKey}`,
|
|
83
|
+
};
|
|
84
|
+
try {
|
|
85
|
+
const res = yield fetch(url, { method: 'POST', body: JSON.stringify(body), headers });
|
|
86
|
+
if (!res.ok)
|
|
87
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
88
|
+
const data = yield res.json();
|
|
89
|
+
console.info('Received translations:', data);
|
|
90
|
+
Object.entries(data.data).map(([key, val]) => {
|
|
91
|
+
const p = `${outputDir}/${key}.json`;
|
|
92
|
+
console.info(`Writing file ${p}`);
|
|
93
|
+
fs_1.promises.writeFile(`${outputDir}/${key}.json`, JSON.stringify(val, null, 2));
|
|
94
|
+
});
|
|
95
|
+
// fs.writeFile(`${outputDir}/source.json`, JSON.stringify(json, null, 2))
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
console.error('Fetch failed:', err);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
}));
|
|
102
|
+
yield commander_1.program.parseAsync(args);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
run(process.argv);
|
|
106
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,19 +9,25 @@ export interface Translations {
|
|
|
9
9
|
}
|
|
10
10
|
export interface ContextType {
|
|
11
11
|
translations: Translations;
|
|
12
|
-
requireTranslations: (localeRegion: string) => Promise<unknown>;
|
|
13
|
-
setLocale: (locale: LocaleRegion) => Promise<unknown>;
|
|
14
12
|
locale: string | null;
|
|
13
|
+
loadTranslations: (locale: LocaleRegion) => Promise<Translations>;
|
|
14
|
+
setLocale: (locale: LocaleRegion) => void;
|
|
15
|
+
importMethod: (locale: LocaleRegion) => Promise<Translations>;
|
|
15
16
|
t: (key: string, options?: {
|
|
16
17
|
locale?: LocaleRegion;
|
|
17
18
|
}) => string;
|
|
18
19
|
}
|
|
19
20
|
export declare const GlotstackContext: React.Context<ContextType>;
|
|
20
|
-
|
|
21
|
+
interface GlotstackProviderProps {
|
|
21
22
|
children: React.ReactNode;
|
|
22
|
-
locale: string;
|
|
23
|
-
onUpdated: (value: ContextType) => void;
|
|
24
23
|
initialTranslations?: Translations;
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
initialLocale?: LocaleRegion;
|
|
25
|
+
onTranslationLoaded?: (locale: LocaleRegion, translations: Translations) => void;
|
|
26
|
+
onLocaleChange?: (locale: LocaleRegion) => void;
|
|
27
|
+
importMethod: (locale: LocaleRegion) => Promise<Translations>;
|
|
28
|
+
}
|
|
29
|
+
export declare const access: (key: string, locale: LocaleRegion, translations: Translations) => string;
|
|
30
|
+
export declare const GlotstackProvider: ({ children, initialLocale, initialTranslations, onLocaleChange, onTranslationLoaded, importMethod }: GlotstackProviderProps) => React.JSX.Element;
|
|
31
|
+
export declare const useGlotstack: () => ContextType;
|
|
27
32
|
export declare const useTranslations: (_options?: Record<never, never>) => ContextType;
|
|
33
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -32,102 +32,91 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
32
32
|
});
|
|
33
33
|
};
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.useTranslations = exports.
|
|
35
|
+
exports.useTranslations = exports.useGlotstack = exports.GlotstackProvider = exports.access = exports.GlotstackContext = void 0;
|
|
36
36
|
const React = __importStar(require("react"));
|
|
37
|
-
const object_1 = require("./util/object");
|
|
38
37
|
exports.GlotstackContext = React.createContext({
|
|
39
38
|
translations: {},
|
|
40
|
-
|
|
41
|
-
setLocale: (_locale) =>
|
|
39
|
+
loadTranslations: () => { throw new Error('no import method set'); },
|
|
40
|
+
setLocale: (_locale) => { throw new Error('import method not set'); },
|
|
42
41
|
locale: null,
|
|
43
|
-
|
|
42
|
+
importMethod: (_locale) => { throw new Error('import method not set'); },
|
|
43
|
+
t: () => { throw new Error('import method not set'); },
|
|
44
44
|
});
|
|
45
|
-
const
|
|
46
|
-
|
|
45
|
+
const access = (key, locale, translations) => {
|
|
46
|
+
var _a;
|
|
47
|
+
const access = [...key.split('.')];
|
|
48
|
+
const localeTranslations = translations === null || translations === void 0 ? void 0 : translations[locale];
|
|
49
|
+
if (localeTranslations == null) {
|
|
50
|
+
return key;
|
|
51
|
+
}
|
|
52
|
+
const value = access.reduce((acc, key) => {
|
|
53
|
+
// @ts-expect-error expected
|
|
54
|
+
return acc === null || acc === void 0 ? void 0 : acc[key];
|
|
55
|
+
}, localeTranslations);
|
|
56
|
+
return ((_a = value === null || value === void 0 ? void 0 : value.value) !== null && _a !== void 0 ? _a : key);
|
|
57
|
+
};
|
|
58
|
+
exports.access = access;
|
|
59
|
+
const GlotstackProvider = ({ children, initialLocale, initialTranslations, onLocaleChange, onTranslationLoaded, importMethod }) => {
|
|
60
|
+
if (initialLocale == null) {
|
|
61
|
+
throw new Error('initialLocale must be set');
|
|
62
|
+
}
|
|
63
|
+
const [locale, setLocale] = React.useState(initialLocale);
|
|
64
|
+
const [translations, setTranslations] = React.useState(initialTranslations);
|
|
47
65
|
const loadingRef = React.useRef({});
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return (locale) => {
|
|
54
|
-
var _a;
|
|
55
|
-
const current = translations === null || translations === void 0 ? void 0 : translations[locale];
|
|
56
|
-
if (current != null) {
|
|
57
|
-
return Promise.resolve();
|
|
66
|
+
const loadTranslations = React.useCallback((locale) => __awaiter(void 0, void 0, void 0, function* () {
|
|
67
|
+
var _a, _b;
|
|
68
|
+
try {
|
|
69
|
+
if (((_a = loadingRef.current) === null || _a === void 0 ? void 0 : _a[locale]) != null) {
|
|
70
|
+
return (yield ((_b = loadingRef.current) === null || _b === void 0 ? void 0 : _b[locale]));
|
|
58
71
|
}
|
|
59
|
-
|
|
60
|
-
|
|
72
|
+
loadingRef.current[locale] = importMethod(locale);
|
|
73
|
+
const result = yield loadingRef.current[locale];
|
|
74
|
+
if (result == null) {
|
|
75
|
+
throw new Error(`Failed to load translation ${locale} ${JSON.stringify(result)}`);
|
|
61
76
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
setTranslations((0, object_1.merge)({}, translations, { [locale]: data }));
|
|
66
|
-
}
|
|
67
|
-
}));
|
|
68
|
-
return loadingRef.current[locale];
|
|
69
|
-
};
|
|
70
|
-
}, [locale, translations]);
|
|
71
|
-
const t = React.useMemo(() => (key, options) => {
|
|
72
|
-
var _a;
|
|
73
|
-
React.useEffect(() => {
|
|
74
|
-
var _a;
|
|
75
|
-
if ((options === null || options === void 0 ? void 0 : options.locale) == null) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
(_a = requireTranslations(options === null || options === void 0 ? void 0 : options.locale)) === null || _a === void 0 ? void 0 : _a.then(() => {
|
|
79
|
-
if ((options === null || options === void 0 ? void 0 : options.locale) == null) {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
setLocale(options === null || options === void 0 ? void 0 : options.locale);
|
|
83
|
-
});
|
|
84
|
-
}, [locale]);
|
|
85
|
-
if (translations === null) {
|
|
86
|
-
return key;
|
|
77
|
+
setTranslations(Object.assign(Object.assign({}, translations), { [locale]: result }));
|
|
78
|
+
onTranslationLoaded === null || onTranslationLoaded === void 0 ? void 0 : onTranslationLoaded(locale, result);
|
|
79
|
+
return result;
|
|
87
80
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return key;
|
|
81
|
+
catch (err) {
|
|
82
|
+
console.error('Unable to import translations', err);
|
|
83
|
+
throw err;
|
|
92
84
|
}
|
|
93
|
-
|
|
94
|
-
// @ts-expect-error expected
|
|
95
|
-
return acc === null || acc === void 0 ? void 0 : acc[key];
|
|
96
|
-
}, localeTranslations);
|
|
97
|
-
return ((_a = value === null || value === void 0 ? void 0 : value.value) !== null && _a !== void 0 ? _a : key);
|
|
98
|
-
}, [translations, requireTranslations]);
|
|
99
|
-
const setLocale = React.useMemo(() => (locale) => {
|
|
100
|
-
setLocalLocale(locale);
|
|
101
|
-
return requireTranslations(locale).then(_ => {
|
|
102
|
-
onLocaleChange(valueRef.current.locale, valueRef.current);
|
|
103
|
-
});
|
|
104
|
-
}, [setLocalLocale, requireTranslations]);
|
|
105
|
-
const valueRef = React.useRef({
|
|
106
|
-
requireTranslations,
|
|
107
|
-
locale,
|
|
108
|
-
translations: initialTranslations !== null && initialTranslations !== void 0 ? initialTranslations : {},
|
|
109
|
-
setLocale,
|
|
110
|
-
t
|
|
111
|
-
});
|
|
112
|
-
const current = valueRef.current;
|
|
85
|
+
}), [importMethod, translations, onTranslationLoaded, setTranslations]);
|
|
113
86
|
React.useEffect(() => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
87
|
+
const run = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
88
|
+
onLocaleChange === null || onLocaleChange === void 0 ? void 0 : onLocaleChange(locale);
|
|
89
|
+
const result = yield loadTranslations(locale);
|
|
90
|
+
});
|
|
91
|
+
React.startTransition(() => {
|
|
92
|
+
run();
|
|
93
|
+
});
|
|
94
|
+
}, [locale]);
|
|
95
|
+
const context = React.useMemo(() => {
|
|
96
|
+
return {
|
|
117
97
|
setLocale,
|
|
98
|
+
translations: translations !== null && translations !== void 0 ? translations : {},
|
|
118
99
|
locale,
|
|
119
|
-
|
|
100
|
+
importMethod,
|
|
101
|
+
loadTranslations,
|
|
102
|
+
t: (key, opts) => {
|
|
103
|
+
React.useEffect(() => {
|
|
104
|
+
if ((opts === null || opts === void 0 ? void 0 : opts.locale) == null) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
loadTranslations(opts === null || opts === void 0 ? void 0 : opts.locale);
|
|
108
|
+
}, [locale]);
|
|
109
|
+
return (0, exports.access)(key, locale, translations !== null && translations !== void 0 ? translations : {});
|
|
110
|
+
}
|
|
120
111
|
};
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}, [requireTranslations, locale]);
|
|
128
|
-
return React.createElement(exports.GlotstackContext.Provider, { value: valueRef.current }, children);
|
|
112
|
+
}, [locale, importMethod, translations]);
|
|
113
|
+
return React.createElement(exports.GlotstackContext.Provider, { value: context }, children);
|
|
114
|
+
};
|
|
115
|
+
exports.GlotstackProvider = GlotstackProvider;
|
|
116
|
+
const useGlotstack = () => {
|
|
117
|
+
return React.useContext(exports.GlotstackContext);
|
|
129
118
|
};
|
|
130
|
-
exports.
|
|
119
|
+
exports.useGlotstack = useGlotstack;
|
|
131
120
|
const useTranslations = (_options) => {
|
|
132
121
|
const context = React.useContext(exports.GlotstackContext);
|
|
133
122
|
return context;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,6CAA8B;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recursively looks for `.glotstack.json` from the current directory up to the root.
|
|
3
|
+
* @param startDir The directory to start the search from. Defaults to process.cwd().
|
|
4
|
+
* @returns The absolute path to the file if found, or null if not found.
|
|
5
|
+
*/
|
|
6
|
+
export declare function findGlotstackConfig(startDir?: string): string | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.findGlotstackConfig = void 0;
|
|
27
|
+
const fs = __importStar(require("fs"));
|
|
28
|
+
const path = __importStar(require("path"));
|
|
29
|
+
/**
|
|
30
|
+
* Recursively looks for `.glotstack.json` from the current directory up to the root.
|
|
31
|
+
* @param startDir The directory to start the search from. Defaults to process.cwd().
|
|
32
|
+
* @returns The absolute path to the file if found, or null if not found.
|
|
33
|
+
*/
|
|
34
|
+
function findGlotstackConfig(startDir = process.cwd()) {
|
|
35
|
+
let currentDir = path.resolve(startDir);
|
|
36
|
+
while (true) {
|
|
37
|
+
const candidate = path.join(currentDir, '.glotstack.json');
|
|
38
|
+
if (fs.existsSync(candidate)) {
|
|
39
|
+
return candidate;
|
|
40
|
+
}
|
|
41
|
+
const parentDir = path.dirname(currentDir);
|
|
42
|
+
if (parentDir === currentDir) {
|
|
43
|
+
break; // Reached root
|
|
44
|
+
}
|
|
45
|
+
currentDir = parentDir;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
exports.findGlotstackConfig = findGlotstackConfig;
|
|
50
|
+
//# sourceMappingURL=findConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findConfig.js","sourceRoot":"","sources":["../../src/util/findConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAE7B;;;;GAIG;AACH,SAAgB,mBAAmB,CAAC,WAAmB,OAAO,CAAC,GAAG,EAAE;IAClE,IAAI,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAExC,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAC;QAC3D,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC3C,IAAI,SAAS,KAAK,UAAU,EAAE,CAAC;YAC7B,MAAM,CAAC,eAAe;QACxB,CAAC;QACD,UAAU,GAAG,SAAS,CAAC;IACzB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAjBD,kDAiBC"}
|
package/dist/util/object.js
CHANGED
package/dist/util/object.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object.js","sourceRoot":"","sources":["../../src/util/object.ts"],"names":[],"mappings":";;;AAAA,SAAgB,QAAQ,CAAC,GAAQ;IAC/B,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AACxE,CAAC;AAFD,4BAEC;AAED,SAAS,MAAM,CAAC,MAAW,EAAE,MAAW;IACtC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAE1D,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAE3B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnD,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;aAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAChD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAID,SAAgB,KAAK,CAAC,MAAmB,EAAE,GAAG,OAAsB;IAClE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE;QAC1C,OAAO,
|
|
1
|
+
{"version":3,"file":"object.js","sourceRoot":"","sources":["../../src/util/object.ts"],"names":[],"mappings":";;;AAAA,SAAgB,QAAQ,CAAC,GAAQ;IAC/B,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AACxE,CAAC;AAFD,4BAEC;AAED,SAAS,MAAM,CAAC,MAAW,EAAE,MAAW;IACtC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAE1D,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAE3B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnD,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;aAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAChD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAID,SAAgB,KAAK,CAAC,MAAmB,EAAE,GAAG,OAAsB;IAClE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE;QAC1C,OAAO,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACnC,CAAC,EAAE,MAAM,CAAC,CAAA;AACZ,CAAC;AAJD,sBAIC;AAED,SAAgB,OAAO,CAAC,CAAM,EAAE,CAAM;IACpC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzB,IAAI,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,KAAK,CAAC;IAElE,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACrB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC7D,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAEhD,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAnBD,0BAmBC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function loadYaml(yamlString: string): unknown;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadYaml = void 0;
|
|
7
|
+
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
8
|
+
function loadYaml(yamlString) {
|
|
9
|
+
try {
|
|
10
|
+
return js_yaml_1.default.load(yamlString);
|
|
11
|
+
}
|
|
12
|
+
catch (e) {
|
|
13
|
+
console.error('Error converting yaml to JSON', e);
|
|
14
|
+
throw new Error('Could not convert yaml to JSON');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.loadYaml = loadYaml;
|
|
18
|
+
//# sourceMappingURL=yaml.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yaml.js","sourceRoot":"","sources":["../../src/util/yaml.ts"],"names":[],"mappings":";;;;;;AAAA,sDAA2B;AAG3B,SAAgB,QAAQ,CAAC,UAAkB;IACzC,IAAI,CAAC;QACH,OAAO,iBAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,CAAC,CAAC,CAAA;QACjD,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;IACnD,CAAC;AACH,CAAC;AAPD,4BAOC"}
|
package/package.json
CHANGED
|
@@ -1,29 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glotstack",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
5
6
|
"author": "JD Cumpson",
|
|
6
7
|
"license": "MIT",
|
|
7
8
|
"private": false,
|
|
8
|
-
"dependencies": {
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"commander": "^13.1.0"
|
|
11
|
+
},
|
|
9
12
|
"devDependencies": {
|
|
13
|
+
"@types/js-yaml": "^4.0.9",
|
|
14
|
+
"@types/node": "^22.15.17",
|
|
10
15
|
"@types/react": "^19.1.3",
|
|
11
|
-
"
|
|
16
|
+
"js-yaml": "^4.1.0",
|
|
12
17
|
"typescript": "5.4.4",
|
|
13
18
|
"typescript-eslint": "^8.17.0"
|
|
14
19
|
},
|
|
15
20
|
"peerDependencies": {
|
|
16
|
-
"@types/react": "^
|
|
21
|
+
"@types/react": "^19.1.3",
|
|
17
22
|
"react": "^18.3.1"
|
|
18
23
|
},
|
|
19
24
|
"resolutions": {
|
|
20
25
|
"react": "18.3.1"
|
|
21
26
|
},
|
|
22
27
|
"bin": {
|
|
23
|
-
"glotstack": "dist/
|
|
28
|
+
"glotstack": "dist/cli.js"
|
|
24
29
|
},
|
|
25
30
|
"scripts": {
|
|
26
|
-
"build": "tsc",
|
|
27
|
-
"prepublishOnly": "yarn run build"
|
|
31
|
+
"build": "tsc && scripts/fix-shebang.sh dist/cli.js",
|
|
32
|
+
"prepublishOnly": "yarn run build",
|
|
33
|
+
"glotstack": "node dist/cli.js"
|
|
28
34
|
}
|
|
29
35
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
FILE="$1"
|
|
4
|
+
SHEBANG='#!/usr/bin/env node'
|
|
5
|
+
|
|
6
|
+
if [ -z "$FILE" ]; then
|
|
7
|
+
echo "Usage: $0 <file>"
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
if [ ! -f "$FILE" ]; then
|
|
12
|
+
echo "File not found: $FILE"
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
FIRST_LINE=$(head -n 1 "$FILE")
|
|
17
|
+
|
|
18
|
+
if [ "$FIRST_LINE" != "$SHEBANG" ]; then
|
|
19
|
+
echo "Injecting shebang into $FILE"
|
|
20
|
+
TMP_FILE=$(mktemp)
|
|
21
|
+
echo "$SHEBANG" > "$TMP_FILE"
|
|
22
|
+
cat "$FILE" >> "$TMP_FILE"
|
|
23
|
+
mv "$TMP_FILE" "$FILE"
|
|
24
|
+
else
|
|
25
|
+
echo "Shebang already present in $FILE"
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
chmod +x "$FILE"
|
package/src/cli.tsx
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Command, program } from 'commander'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { promises as fs } from 'fs'
|
|
4
|
+
import { findGlotstackConfig } from './util/findConfig'
|
|
5
|
+
import { cwd } from 'process'
|
|
6
|
+
import { merge } from './util/object'
|
|
7
|
+
import { loadYaml } from './util/yaml'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async function run(args: string[]) {
|
|
11
|
+
program
|
|
12
|
+
.command('get-translations')
|
|
13
|
+
.option('--source-path [path]', 'path to en-US.json (or your canonical source json)')
|
|
14
|
+
.option('--api-origin [url]', 'glotstack api origin', 'http://localhost:4001')
|
|
15
|
+
.option('--output-dir [path]', 'path to output directory')
|
|
16
|
+
.option('--api-key [key]', 'api key for glotstack.ai')
|
|
17
|
+
.option('--project-id [id]', '(optional) specific project to use')
|
|
18
|
+
.argument('[output-locales...]', 'locales to get translations for')
|
|
19
|
+
.action(async (outputLocales: string[], options: Record<string, any>, command: Command) => {
|
|
20
|
+
const configPath = findGlotstackConfig(cwd())
|
|
21
|
+
let config = {}
|
|
22
|
+
|
|
23
|
+
if (configPath != null) {
|
|
24
|
+
console.info('Loading config file at ', configPath)
|
|
25
|
+
try {
|
|
26
|
+
const text = await fs.readFile(configPath, 'utf-8')
|
|
27
|
+
config = JSON.parse(text)
|
|
28
|
+
console.info('Loaded config file', config)
|
|
29
|
+
} catch(err) {
|
|
30
|
+
//pass
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const resolved = merge(config, options, { outputLocales })
|
|
35
|
+
const { apiOrigin, sourcePath, outputDir, projectId} = resolved
|
|
36
|
+
|
|
37
|
+
if ((resolved.outputLocales as string[]).includes('en-US')) {
|
|
38
|
+
console.warn('en-US detected in outputLocales, removing');
|
|
39
|
+
resolved.outputLocales = resolved.outputLocales.filter((x: string) => x !== 'en-US')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!sourcePath) {
|
|
43
|
+
throw new Error('sourcePath must be specified')
|
|
44
|
+
}
|
|
45
|
+
if (!apiOrigin) {
|
|
46
|
+
throw new Error('apiOrigin must be specified')
|
|
47
|
+
}
|
|
48
|
+
if (!outputDir) {
|
|
49
|
+
throw new Error('outputDir must be specified')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const url = `${apiOrigin}/api/translations`
|
|
53
|
+
console.info(`Fetching translations from: ${url}`)
|
|
54
|
+
|
|
55
|
+
const absPath = path.resolve(sourcePath)
|
|
56
|
+
const fileContent = await fs.readFile(absPath, 'utf-8')
|
|
57
|
+
|
|
58
|
+
let json = null
|
|
59
|
+
try {
|
|
60
|
+
json = loadYaml(fileContent)
|
|
61
|
+
} catch(err) {
|
|
62
|
+
try {
|
|
63
|
+
json = JSON.parse(fileContent)
|
|
64
|
+
} catch(err) {
|
|
65
|
+
console.error('Unable to parse source file ', absPath, err)
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const body = {
|
|
71
|
+
locales: resolved.outputLocales,
|
|
72
|
+
translations: json,
|
|
73
|
+
...{ ... (projectId != null ? {projectId} : {})},
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const headers ={
|
|
77
|
+
'Content-Type': 'application/json',
|
|
78
|
+
'Authorization': `Bearer ${resolved.apiKey}` ,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const res = await fetch(url, { method: 'POST', body: JSON.stringify(body), headers })
|
|
83
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`)
|
|
84
|
+
|
|
85
|
+
const data = await res.json()
|
|
86
|
+
console.info('Received translations:', data)
|
|
87
|
+
Object.entries(data.data).map(([key, val]) => {
|
|
88
|
+
const p = `${outputDir}/${key}.json`
|
|
89
|
+
console.info(`Writing file ${p}`)
|
|
90
|
+
fs.writeFile(`${outputDir}/${key}.json`, JSON.stringify(val, null, 2))
|
|
91
|
+
})
|
|
92
|
+
// fs.writeFile(`${outputDir}/source.json`, JSON.stringify(json, null, 2))
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.error('Fetch failed:', err)
|
|
95
|
+
process.exit(1)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
await program.parseAsync(args)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
run(process.argv)
|
package/src/index.tsx
CHANGED
|
@@ -23,125 +23,115 @@ export interface Translations {
|
|
|
23
23
|
|
|
24
24
|
export interface ContextType {
|
|
25
25
|
translations: Translations
|
|
26
|
-
requireTranslations: (localeRegion: string) => Promise<unknown>
|
|
27
|
-
setLocale: (locale: LocaleRegion) => Promise<unknown>
|
|
28
26
|
locale: string | null
|
|
27
|
+
loadTranslations: (locale: LocaleRegion) => Promise<Translations>
|
|
28
|
+
setLocale: (locale: LocaleRegion) => void
|
|
29
|
+
importMethod: (locale: LocaleRegion) => Promise<Translations>
|
|
29
30
|
t: (key: string, options?: { locale?: LocaleRegion }) => string
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export const GlotstackContext = React.createContext<ContextType>({
|
|
33
34
|
translations: {},
|
|
34
|
-
|
|
35
|
-
setLocale: (_locale: LocaleRegion) =>
|
|
35
|
+
loadTranslations: () => { throw new Error('no import method set') },
|
|
36
|
+
setLocale: (_locale: LocaleRegion) => { throw new Error('import method not set') },
|
|
36
37
|
locale: null,
|
|
37
|
-
|
|
38
|
+
importMethod: (_locale: LocaleRegion) => { throw new Error('import method not set') },
|
|
39
|
+
t: () => { throw new Error('import method not set') },
|
|
38
40
|
})
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
children: React.ReactNode
|
|
42
|
+
interface GlotstackProviderProps {
|
|
43
|
+
children: React.ReactNode
|
|
42
44
|
initialTranslations?: Translations
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
React.useEffect(() => {
|
|
50
|
-
setLocalLocale(defaultLocale)
|
|
51
|
-
}, [defaultLocale])
|
|
52
|
-
|
|
53
|
-
const requireTranslations = React.useMemo(() => {
|
|
54
|
-
return (locale: string) => {
|
|
55
|
-
const current = translations?.[locale]
|
|
56
|
-
if (current != null) {
|
|
57
|
-
return Promise.resolve()
|
|
58
|
-
}
|
|
59
|
-
if (loadingRef.current?.[locale] == null) {
|
|
60
|
-
loadingRef.current[locale] = import(`domains/application/translations/${locale}.json`)
|
|
61
|
-
}
|
|
62
|
-
loadingRef.current[locale].then(async (Module) => {
|
|
63
|
-
if (Module?.default != null) {
|
|
64
|
-
const data = await (await fetch(Module.default)).json()
|
|
65
|
-
setTranslations(merge({}, translations, { [locale]: data }))
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
return loadingRef.current[locale]
|
|
69
|
-
}
|
|
70
|
-
}, [locale, translations])
|
|
45
|
+
initialLocale?: LocaleRegion
|
|
46
|
+
onTranslationLoaded?: (locale: LocaleRegion, translations: Translations) => void
|
|
47
|
+
onLocaleChange?: (locale: LocaleRegion) => void
|
|
48
|
+
importMethod: (locale: LocaleRegion) => Promise<Translations>
|
|
49
|
+
}
|
|
71
50
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
requireTranslations(options?.locale)?.then(() => {
|
|
78
|
-
if (options?.locale == null) {
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
|
-
setLocale(options?.locale)
|
|
82
|
-
})
|
|
83
|
-
}, [locale])
|
|
84
|
-
|
|
85
|
-
if (translations === null) {
|
|
86
|
-
return key
|
|
87
|
-
}
|
|
51
|
+
export const access = (key: string, locale: LocaleRegion, translations: Translations) => {
|
|
52
|
+
const access = [...key.split('.')] as [LocaleRegion, ...string[]]
|
|
53
|
+
const localeTranslations = translations?.[locale]
|
|
88
54
|
|
|
89
|
-
|
|
90
|
-
|
|
55
|
+
if (localeTranslations == null) {
|
|
56
|
+
return key
|
|
57
|
+
}
|
|
91
58
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
59
|
+
const value = access.reduce((acc: Translations[string], key) => {
|
|
60
|
+
// @ts-expect-error expected
|
|
61
|
+
return acc?.[key]
|
|
62
|
+
}, localeTranslations)
|
|
95
63
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return acc?.[key]
|
|
99
|
-
}, localeTranslations)
|
|
64
|
+
return (value?.value ?? key) as string
|
|
65
|
+
}
|
|
100
66
|
|
|
101
|
-
|
|
102
|
-
|
|
67
|
+
export const GlotstackProvider = ({ children, initialLocale, initialTranslations, onLocaleChange, onTranslationLoaded, importMethod }: GlotstackProviderProps) => {
|
|
68
|
+
if (initialLocale == null) {
|
|
69
|
+
throw new Error('initialLocale must be set')
|
|
70
|
+
}
|
|
103
71
|
|
|
104
|
-
const setLocale = React.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
onLocaleChange(valueRef.current.locale, valueRef.current)
|
|
108
|
-
})
|
|
109
|
-
}, [setLocalLocale, requireTranslations])
|
|
72
|
+
const [locale, setLocale] = React.useState<LocaleRegion>(initialLocale)
|
|
73
|
+
const [translations, setTranslations] = React.useState(initialTranslations)
|
|
74
|
+
const loadingRef = React.useRef<Record<string, Promise<Translations>>>({})
|
|
110
75
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
76
|
+
const loadTranslations = React.useCallback(async (locale: string) => {
|
|
77
|
+
try {
|
|
78
|
+
if (loadingRef.current?.[locale] != null) {
|
|
79
|
+
return (await loadingRef.current?.[locale])
|
|
80
|
+
}
|
|
81
|
+
loadingRef.current[locale] = importMethod(locale)
|
|
82
|
+
const result = await loadingRef.current[locale]
|
|
118
83
|
|
|
119
|
-
|
|
84
|
+
if (result == null) {
|
|
85
|
+
throw new Error(`Failed to load translation ${locale} ${JSON.stringify(result)}`)
|
|
86
|
+
}
|
|
87
|
+
setTranslations({ ...translations, [locale]: result })
|
|
88
|
+
onTranslationLoaded?.(locale, result)
|
|
89
|
+
return result
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error('Unable to import translations', err)
|
|
92
|
+
throw err
|
|
93
|
+
}
|
|
94
|
+
}, [importMethod, translations, onTranslationLoaded, setTranslations])
|
|
120
95
|
|
|
121
96
|
React.useEffect(() => {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
97
|
+
const run = async () => {
|
|
98
|
+
onLocaleChange?.(locale)
|
|
99
|
+
const result = await loadTranslations(locale)
|
|
100
|
+
}
|
|
101
|
+
React.startTransition(() => {
|
|
102
|
+
run()
|
|
103
|
+
})
|
|
104
|
+
}, [locale])
|
|
105
|
+
|
|
106
|
+
const context = React.useMemo(() => {
|
|
107
|
+
return {
|
|
125
108
|
setLocale,
|
|
109
|
+
translations: translations ?? {},
|
|
126
110
|
locale,
|
|
127
|
-
|
|
128
|
-
|
|
111
|
+
importMethod,
|
|
112
|
+
loadTranslations,
|
|
113
|
+
t: (key: string, opts?: { locale?: LocaleRegion }) => {
|
|
114
|
+
React.useEffect(() => {
|
|
115
|
+
if (opts?.locale == null) {
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
loadTranslations(opts?.locale)
|
|
119
|
+
}, [locale])
|
|
120
|
+
return access(key, locale, translations ?? {})
|
|
121
|
+
}
|
|
129
122
|
|
|
130
|
-
if (!isEqual(valueRef.current, current)) {
|
|
131
|
-
onUpdated?.(valueRef.current)
|
|
132
123
|
}
|
|
124
|
+
}, [locale, importMethod, translations])
|
|
133
125
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
React.useEffect(() => {
|
|
137
|
-
requireTranslations(locale)
|
|
138
|
-
}, [requireTranslations, locale])
|
|
139
|
-
|
|
140
|
-
return <GlotstackContext.Provider value={valueRef.current}>
|
|
126
|
+
return <GlotstackContext.Provider value={context}>
|
|
141
127
|
{children}
|
|
142
128
|
</GlotstackContext.Provider>
|
|
143
129
|
}
|
|
144
130
|
|
|
131
|
+
export const useGlotstack = () => {
|
|
132
|
+
return React.useContext(GlotstackContext)
|
|
133
|
+
}
|
|
134
|
+
|
|
145
135
|
|
|
146
136
|
export const useTranslations = (_options?: Record<never, never>) => {
|
|
147
137
|
const context = React.useContext(GlotstackContext)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Recursively looks for `.glotstack.json` from the current directory up to the root.
|
|
6
|
+
* @param startDir The directory to start the search from. Defaults to process.cwd().
|
|
7
|
+
* @returns The absolute path to the file if found, or null if not found.
|
|
8
|
+
*/
|
|
9
|
+
export function findGlotstackConfig(startDir: string = process.cwd()): string | null {
|
|
10
|
+
let currentDir = path.resolve(startDir);
|
|
11
|
+
|
|
12
|
+
while (true) {
|
|
13
|
+
const candidate = path.join(currentDir, '.glotstack.json');
|
|
14
|
+
if (fs.existsSync(candidate)) {
|
|
15
|
+
return candidate;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const parentDir = path.dirname(currentDir);
|
|
19
|
+
if (parentDir === currentDir) {
|
|
20
|
+
break; // Reached root
|
|
21
|
+
}
|
|
22
|
+
currentDir = parentDir;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return null;
|
|
26
|
+
}
|
package/src/util/object.ts
CHANGED
|
@@ -25,7 +25,7 @@ type MergeTarget = Record<string|number|symbol, any>
|
|
|
25
25
|
|
|
26
26
|
export function merge(target: MergeTarget, ...sources: MergeTarget[]): MergeTarget {
|
|
27
27
|
return sources.reduce((previous, current) => {
|
|
28
|
-
return
|
|
28
|
+
return _merge(previous, current);
|
|
29
29
|
}, target)
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -49,4 +49,3 @@ export function isEqual(a: any, b: any): boolean {
|
|
|
49
49
|
|
|
50
50
|
return false;
|
|
51
51
|
}
|
|
52
|
-
|
package/src/util/yaml.ts
ADDED
package/tsconfig.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"exclude": ["node_modules", "dist"],
|
|
3
|
-
"include": ["./"],
|
|
3
|
+
"include": ["./src"],
|
|
4
4
|
"files": ["./types/global.d.ts"],
|
|
5
5
|
"compilerOptions": {
|
|
6
6
|
"outDir": "dist",
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
"baseUrl": ".",
|
|
14
14
|
"paths": {
|
|
15
15
|
},
|
|
16
|
-
"rootDirs": ["./src/*"],
|
|
17
16
|
"esModuleInterop": true,
|
|
18
17
|
"skipLibCheck": true,
|
|
19
18
|
"forceConsistentCasingInFileNames": true,
|