Skip to content

Commit

Permalink
feat: added i18n, validation and schema packages
Browse files Browse the repository at this point in the history
  • Loading branch information
kedrzu committed Jul 3, 2023
1 parent 94e531a commit 644f6f5
Show file tree
Hide file tree
Showing 95 changed files with 6,766 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/i18n/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import config from '../../jest.config.js';
export default config;
20 changes: 20 additions & 0 deletions packages/i18n/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "@nzyme/i18n",
"version": "1.0.0",
"type": "module",
"main": "./src/index.ts",
"module": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"sideEffects": false,
"repository": "https://github.com/kedrzu/lesscms.git",
"author": "Michał Kędrzyński <m.kedrzynski@gmail.com>",
"private": true,
"dependencies": {
"@nzyme/ioc": "1.0.0",
"@nzyme/utils": "1.0.0"
}
}
9 changes: 9 additions & 0 deletions packages/i18n/src/LocaleProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineInjectable } from '@nzyme/ioc';

export interface LocaleProvider {
locale: string;
}

export const LocaleProvider = defineInjectable<LocaleProvider>({
name: 'LocaleProvider',
});
1 change: 1 addition & 0 deletions packages/i18n/src/Translatable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Translatable = string | ((lang?: string) => string);
34 changes: 34 additions & 0 deletions packages/i18n/src/TranslationModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Translatable } from './Translatable.js';

export type TranslationModuleConfig<TKeys extends string> = Record<
string,
Record<TKeys, string | undefined> | undefined
>;

const DEFAULT_LANG = 'en';

export class TranslationModule<TKey extends string> {
constructor(public readonly translations: TranslationModuleConfig<TKey>) {}

public get(key: TKey, lang: string | null): string;
public get(key: TKey): Translatable;
public get(key: TKey, lang?: string | null): Translatable | string {
if (lang !== undefined) {
return this.getValue(key, lang);
} else {
return lang => this.getValue(key, lang || DEFAULT_LANG);
}
}

public getValue(key: TKey, lang?: string | null) {
if (!lang) {
lang = DEFAULT_LANG;
}

const translations = this.translations[lang] ?? this.translations[DEFAULT_LANG];
return translations?.[key] || '';
}
}

export type TranslationKeys<TModule extends TranslationModule<string>> =
TModule extends TranslationModule<infer TKey> ? TKey : never;
51 changes: 51 additions & 0 deletions packages/i18n/src/Translator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { defineService } from '@nzyme/ioc';
import { FunctionOnly } from '@nzyme/types';
import { formatWith } from '@nzyme/utils';

import { LocaleProvider } from './LocaleProvider.js';
import { Translatable } from './Translatable.js';
import { TranslationModule } from './TranslationModule.js';
import { translate } from './utils.js';

export interface Translator {
(value: Translatable, params?: Record<string, unknown>): string | undefined;

forModule<TKey extends string>(module: TranslationModule<TKey>): TranslatorForModule<TKey>;
}

export interface TranslatorForModule<TKey extends string> {
(key: TKey, params?: Record<string, unknown>): string;
(value: Translatable, params?: Record<string, unknown>): string;
}

export const Translator = defineService({
deps: {
localeProvider: LocaleProvider,
},
setup({ localeProvider }): Translator {
const translator = (<FunctionOnly<Translator>>function (value, params) {
return translate(value, {
params,
locale: localeProvider.locale,
});
}) as Translator;

translator.forModule = <TKey extends string>(module: TranslationModule<TKey>) => {
return (keyOrTranslatable, params) => {
const locale = localeProvider.locale;
const message =
typeof keyOrTranslatable === 'string'
? module.get(keyOrTranslatable as TKey, locale) || keyOrTranslatable
: keyOrTranslatable(locale);

if (params) {
return formatWith(message, params);
}

return message;
};
};

return translator;
},
});
5 changes: 5 additions & 0 deletions packages/i18n/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './TranslationModule.js';
export * from './Translatable.js';
export * from './Translator.js';
export * from './LocaleProvider.js';
export * from './utils.js';
18 changes: 18 additions & 0 deletions packages/i18n/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { formatWith } from '@nzyme/utils';

import { Translatable } from './Translatable.js';

export interface TranslateArgs {
locale?: string;
params?: Record<string, unknown>;
}

export function translate(value: Translatable, args?: TranslateArgs) {
const params = args?.params;
const message = typeof value === 'string' ? value : value(args?.locale);
if (params) {
return formatWith(message, params);
}

return message;
}
4 changes: 4 additions & 0 deletions packages/i18n/tsconfig.eslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"include": ["./src/**/*.ts", "./tests/**/*.ts"]
}
16 changes: 16 additions & 0 deletions packages/i18n/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*.ts", "src/**/*.json"],
"references": [
{
"path": "../ioc/tsconfig.json"
},
{
"path": "../utils/tsconfig.json"
}
]
}
31 changes: 31 additions & 0 deletions packages/schema/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@nzyme/schema",
"version": "1.0.0",
"type": "module",
"main": "./src/index.ts",
"module": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./*": "./src/*"
},
"sideEffects": false,
"repository": "https://github.com/kedrzu/lesscms.git",
"author": "Michał Kędrzyński <m.kedrzynski@gmail.com>",
"private": true,
"dependencies": {
"@nzyme/i18n": "1.0.0",
"@nzyme/validation": "1.0.0",
"@nzyme/ioc": "1.0.0",
"@nzyme/types": "1.0.0",
"@nzyme/utils": "1.0.0",
"graphql": "^16.6.0",
"graphql-scalars": "^1.22.2",
"tslib": "^2.5.3"
},
"depcheck": {
"ignoreFiles": [
"tests"
]
}
}
39 changes: 39 additions & 0 deletions packages/schema/src/ApiMethod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Translatable } from '@nzyme/i18n';

import { InputObjectSchema, InputObjectSchemaAny } from './InputObjectSchema.js';
import { Schema, SchemaAny } from './Schema.js';

export type ApiMethodType = 'query' | 'mutation';

export interface ApiMethodConfig<TInput extends InputObjectSchemaAny, TResult extends SchemaAny> {
type: ApiMethodType;
description?: string | Translatable;
input: TInput;
result: TResult;
/** Symbol to easily identify API method. */
symbol?: symbol;
}

export class ApiMethod<
TInput extends InputObjectSchemaAny = InputObjectSchema,
TResult extends SchemaAny = Schema,
> {
public readonly type: ApiMethodType;
public readonly input: TInput;
public readonly result: TResult;
public readonly description: Translatable;
public readonly symbol?: symbol;

constructor(config: ApiMethodConfig<TInput, TResult>) {
this.type = config.type;
this.input = config.input;
this.result = config.result;
this.description = config.description || '';
this.symbol = config.symbol;
}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type ApiMethodAny = ApiMethod<any, any>;
export type ApiMethods = Record<string, ApiMethod>;
export type ApiMethodsAny = Record<string, ApiMethodAny>;
Loading

0 comments on commit 644f6f5

Please sign in to comment.