diff --git a/packages/locale-generator/README.md b/packages/locale-generator/README.md new file mode 100644 index 0000000..f7942a7 --- /dev/null +++ b/packages/locale-generator/README.md @@ -0,0 +1 @@ +- TODO diff --git a/packages/locale-generator/package.json b/packages/locale-generator/package.json new file mode 100644 index 0000000..404375f --- /dev/null +++ b/packages/locale-generator/package.json @@ -0,0 +1,53 @@ +{ + "name": "@dorami/locale-generator", + "version": "0.0.0", + "main": "./src/index.ts", + "module": "./src/index.ts", + "types": "./src/index.ts", + "publishConfig": { + "main": "./index.mjs", + "module": "./index.mjs", + "types": "./index.d.mts", + "exports": { + ".": { + "types": "./index.d.mts", + "import": "./index.mjs" + } + }, + "directory": "dist", + "linkDirectory": false, + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "scripts": { + "build": "NODE_ENV=production INPUT_DIR=./ OUTPUT_DIR=dist/ pnpm run build:package", + "build:package": "pnpm run build:prebuild && tsup && pnpm run build:postbuild", + "build:prebuild": "tsx ./scripts/prebuild.ts", + "build:postbuild": "tsx ./scripts/postbuild.ts", + "dev:link": "pnpm link --global && npm link" + }, + "dependencies": { + "lodash-es": "^4.17.21", + "properties-file": "^3.5.4", + "yaml": "^2.4.5" + }, + "devDependencies": { + "tsup": "^8.2.4", + "tsx": "^4.16.2" + }, + "author": "Qwlabs", + "homepage": "https://github.com/qwlabs/dorami", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/qwlabs/dorami.git", + "directory": "packages/locale-generator" + }, + "bugs": { + "url": "https://github.com/qwlabs/dorami/issues" + }, + "engines": { + "node": ">=20.17.0", + "pnpm": ">=9" + } +} \ No newline at end of file diff --git a/packages/locale-generator/scripts/postbuild.ts b/packages/locale-generator/scripts/postbuild.ts new file mode 100644 index 0000000..3ce9d3f --- /dev/null +++ b/packages/locale-generator/scripts/postbuild.ts @@ -0,0 +1,11 @@ +import * as path from 'node:path'; +import * as fs from 'fs-extra'; +import { clearPackageJson, resolvePath } from '../../../scripts/build-helper'; + +const { __dirname, __workspace, OUTPUT_DIR } = resolvePath(import.meta.url); + +fs.copySync(path.resolve(__dirname, '../package.json'), `${OUTPUT_DIR}/package.json`); +fs.copySync(path.resolve(__dirname, '../README.md'), `${OUTPUT_DIR}/README.md`); +fs.copySync(path.resolve(__workspace, './LICENSE.md'), `${OUTPUT_DIR}/LICENSE.md`); + +clearPackageJson(path.resolve(__dirname, `../${OUTPUT_DIR}/package.json`)); diff --git a/packages/locale-generator/scripts/prebuild.ts b/packages/locale-generator/scripts/prebuild.ts new file mode 100644 index 0000000..c74f4b3 --- /dev/null +++ b/packages/locale-generator/scripts/prebuild.ts @@ -0,0 +1,5 @@ +import * as path from 'node:path'; +import { removeBuild, resolvePath, updatePackageJson } from '../../../scripts/build-helper'; + +removeBuild(import.meta.url); +updatePackageJson(path.resolve(resolvePath(import.meta.url).__dirname, '../package.json')); diff --git a/packages/locale-generator/src/index.ts b/packages/locale-generator/src/index.ts new file mode 100644 index 0000000..a655d7b --- /dev/null +++ b/packages/locale-generator/src/index.ts @@ -0,0 +1,56 @@ +import { writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { glob } from 'glob'; +import { assign } from 'lodash-es'; +import { lookupLoader } from './loaders'; + +export interface BuildLocaleEntry { + locale: string; + includes: string[]; +} + +interface BuildLocaleMessages { + locale: string; + messages: Record; +} + +const loadFile = (file: string): Promise> => { + const loader = lookupLoader(file); + if (!loader) { + return Promise.resolve({}); + } + return loader.load(file); +}; + +const loadLocale = async (entry: BuildLocaleEntry): Promise => { + const files = await glob(entry.includes); + const allMessages: Record = await Promise.all(files.map(loadFile)).then((values) => { + return assign({}, ...values); + }); + return { + locale: entry.locale, + messages: allMessages, + }; +}; + +const loadLocales = (entries: BuildLocaleEntry[]): Promise>> => { + return new Promise((resolve) => { + const promises = entries.map(loadLocale); + Promise.all(promises).then((localeMessages) => { + const result: Record> = {}; + localeMessages.forEach((localeMessage) => { + result[localeMessage.locale] = localeMessage.messages; + }); + resolve(result); + }); + }); +}; + +export const generateLocale = async (outputDir: string, entries: BuildLocaleEntry[]) => { + const loadedLocales = await loadLocales(entries); + for (const locale in loadedLocales) { + const messages = loadedLocales[locale]; + const filePath = join(outputDir, `${locale}.json`); + writeFileSync(filePath, JSON.stringify(messages), { encoding: 'utf-8' }); + } +}; diff --git a/packages/locale-generator/src/loader.ts b/packages/locale-generator/src/loader.ts new file mode 100644 index 0000000..0dfdc16 --- /dev/null +++ b/packages/locale-generator/src/loader.ts @@ -0,0 +1,4 @@ +export interface Loader { + load: (file: string) => Promise>; + supported: (file: string) => boolean; +} diff --git a/packages/locale-generator/src/loaders/index.ts b/packages/locale-generator/src/loaders/index.ts new file mode 100644 index 0000000..559805e --- /dev/null +++ b/packages/locale-generator/src/loaders/index.ts @@ -0,0 +1,9 @@ +import type { Loader } from '../loader.ts'; +import { YamlLoader } from './yaml-loader.ts'; +import { PropertiesLoader } from './properties-loader.ts'; + +const LOADERS: Loader[] = [new YamlLoader(), new PropertiesLoader()]; + +export const lookupLoader = (file: string): Loader | undefined => { + return LOADERS.find((loader) => loader.supported(file)); +}; diff --git a/packages/locale-generator/src/loaders/properties-loader.ts b/packages/locale-generator/src/loaders/properties-loader.ts new file mode 100644 index 0000000..b491e46 --- /dev/null +++ b/packages/locale-generator/src/loaders/properties-loader.ts @@ -0,0 +1,13 @@ +import { readFileSync } from 'node:fs'; +import { getProperties } from 'properties-file'; +import type { Loader } from '../loader.ts'; + +export class PropertiesLoader implements Loader { + async load(file: string): Promise> { + return getProperties(readFileSync(file, { encoding: 'utf-8' })) as unknown as Record; + } + + supported(file: string): boolean { + return file.endsWith('.properties'); + } +} diff --git a/packages/locale-generator/src/loaders/yaml-loader.ts b/packages/locale-generator/src/loaders/yaml-loader.ts new file mode 100644 index 0000000..d96b8fa --- /dev/null +++ b/packages/locale-generator/src/loaders/yaml-loader.ts @@ -0,0 +1,14 @@ +import type { Loader } from '../loader.ts'; + +export class YamlLoader implements Loader { + load(file: string): Promise> { + // TODO + return Promise.resolve({}); + // parse(readFileSync(file, 'utf-8')) + // return Promise.resolve(undefined); + } + + supported(file: string): boolean { + return file.endsWith('.yaml'); + } +} diff --git a/packages/locale-generator/tsconfig.json b/packages/locale-generator/tsconfig.json new file mode 100644 index 0000000..e25b24e --- /dev/null +++ b/packages/locale-generator/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Node", + "baseUrl": ".", + }, + "exclude": ["node_modules", "dist"] +} diff --git a/packages/locale-generator/tsup.config.ts b/packages/locale-generator/tsup.config.ts new file mode 100644 index 0000000..6302f95 --- /dev/null +++ b/packages/locale-generator/tsup.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['esm'], + dts: true, + splitting: false, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f00967..4146a84 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,6 +88,26 @@ importers: version: 4.19.1 publishDirectory: dist + packages/locale-generator: + dependencies: + lodash-es: + specifier: ^4.17.21 + version: 4.17.21 + properties-file: + specifier: ^3.5.4 + version: 3.5.8 + yaml: + specifier: ^2.4.5 + version: 2.5.1 + devDependencies: + tsup: + specifier: ^8.2.4 + version: 8.3.0(jiti@1.21.6)(postcss@8.4.47)(tsx@4.19.1)(typescript@5.6.2)(yaml@2.5.1) + tsx: + specifier: ^4.16.2 + version: 4.19.1 + publishDirectory: dist + packages: '@antfu/eslint-config@3.7.3': @@ -1573,6 +1593,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -1923,6 +1946,9 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + properties-file@3.5.8: + resolution: {integrity: sha512-Z5SvLBJqxWd0LOiVXMXjP8R+wkCOE9UsFlZNUQo5c3gb5Y0RnXQFCz0/8kk0OInv8+ygN31BUfFqK2YNjZtRVg==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3809,6 +3835,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash-es@4.17.21: {} + lodash.merge@4.6.2: {} lodash.sortby@4.7.0: {} @@ -4313,6 +4341,8 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + properties-file@3.5.8: {} + punycode@2.3.1: {} queue-microtask@1.2.3: {}