Skip to content

Commit

Permalink
refactor: make getLocales function simpler
Browse files Browse the repository at this point in the history
  • Loading branch information
SrIzan10 committed Nov 30, 2024
1 parent 292b913 commit 89807ba
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 116 deletions.
8 changes: 8 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"useTabs": false,
"printWidth": 800,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "es5",
"semi": true
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@
"tsup": "^8.0.1",
"typescript": "^5.3.3",
"vitest": "^1.6.0"
}
},
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
44 changes: 26 additions & 18 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
import getAllLocales from './util/getAllLocales.js'
import stringToJsonKey from './util/stringToJsonKey.js'
import { InterpolationObject, LocalizationOptions } from './util/types'
import getLocales from './util/getLocales';
import { Interpolations, LocalizationOptions } from './util/types';

export class Localization {
private initOptions: LocalizationOptions
private initLocales: LocalizationOptions['locales']
private initOptions: LocalizationOptions;
private initLocales: LocalizationOptions['locales'];

constructor(options: LocalizationOptions) {
this.initOptions = options
this.initLocales = JSON.parse(JSON.stringify(options.locales))
}
constructor(options: LocalizationOptions) {
this.initOptions = options;
this.initLocales = JSON.parse(JSON.stringify(options.locales));
}

t(key: string, interp?: InterpolationObject, lang = this.initOptions.defaultLocale) {
return stringToJsonKey(key, this.initLocales, lang, this.initOptions.fallbackLocale, interp)
}
t(key: string, interp?: Interpolations, lang = this.initOptions.defaultLocale) {
return getLocales({
key,
locales: this.initLocales,
defaultLang: lang,
fallbackLang: this.initOptions.fallbackLocale,
interpolations: interp,
}) as string;
}

localizationFor(key: string) {
return getAllLocales(key, this.initOptions.locales)
}
localizationFor(key: string) {
return getLocales({
key,
locales: this.initLocales,
}) as Record<string, string>;
}

changeLanguage(lang: string) {
this.initOptions.defaultLocale = lang
}
changeLanguage(lang: string) {
this.initOptions.defaultLocale = lang;
}
}
1 change: 1 addition & 0 deletions src/test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// The file is here because tsconfig is set to compile all files in src folder, and not the tests folder
import { Localization } from "./index";

export const locales = new Localization({
Expand Down
35 changes: 0 additions & 35 deletions src/util/getAllLocales.ts

This file was deleted.

43 changes: 43 additions & 0 deletions src/util/getLocales.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { interpolate } from "./interpolate";
import { Interpolations, LocaleObject, Locales } from "./types";

export default function getLocales(args: Args): string | Record<string, string> {
const { key, locales, defaultLang, fallbackLang, interpolations } = args;
const translations: Record<string, string> = {};

for (const lang in locales) {
// Navigate through nested keys
let value = locales[lang];
for (const k of key.split(".")) {
value = value[k] as LocaleObject;
if (!value) break;
}

// Handle both string values and object values
if (value) {
if (typeof value === "object") {
const innerKey = Object.keys(value)[0];
const interpolatedKey = interpolate(innerKey, interpolations);
translations[lang] = value[interpolatedKey] as string;
} else if (typeof value === "string") {
translations[lang] = interpolate(value, interpolations);
}
}
}

// If defaultLang is provided, return single translation with fallback
if (defaultLang) {
return translations[defaultLang] || translations[fallbackLang!] || key;
}

// Otherwise return all translations
return translations;
}

type Args = {
key: string,
locales: Locales,
defaultLang?: string,
fallbackLang?: string,
interpolations?: Interpolations
}
12 changes: 12 additions & 0 deletions src/util/interpolate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import sanitize from './sanitize';
import { Interpolations } from './types';

export function interpolate(text: string, interpolations?: Interpolations) {
if (!interpolations) return text;

for (const key in interpolations) {
// sanitize the key to prevent XSS
text = text.replaceAll(`{{${key}}}`, sanitize(interpolations[key] as string));
}
return text;
}
10 changes: 0 additions & 10 deletions src/util/replaceInterps.ts

This file was deleted.

11 changes: 11 additions & 0 deletions src/util/sanitize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function sanitize(text: string): string {
return text.replace(
/[&<>]/g,
(char) =>
({
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
})[char] || char,
);
}
17 changes: 0 additions & 17 deletions src/util/sanitizeText.ts

This file was deleted.

7 changes: 0 additions & 7 deletions src/util/stringToJsonKey.ts

This file was deleted.

37 changes: 11 additions & 26 deletions src/util/types.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,17 @@
// json types
export type JSONValue =
| string
| number
| boolean
| JSONObject
| JSONArray;

export interface JSONObject {
[x: string]: JSONValue;
}

export interface JSONArray extends Array<JSONValue> { }

export interface LocalizationOptions {
locales: Locales;
defaultLocale: string;
fallbackLocale: string;
}

export interface InterpolationObject {
[key: string]: string | (() => boolean) | undefined;
pluralChecker?: string | (() => boolean);
}

export type LocaleObject = {
[key: string]: string | LocaleObject;
};

export type Locales = {
[lang: string]: LocaleObject;
};
};

export interface LocalizationOptions {
locales: Locales;
defaultLocale: string;
fallbackLocale: string;
}

export type Interpolations = {
[key: string]: string;
};
2 changes: 1 addition & 1 deletion tests/get.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { it, expect } from "vitest";
import { locales } from "../src/test.js";
import { locales } from "../src/test";

it("should give multiple keys correctly for both langs", () => {
expect(locales.t("hello", {}, 'en')).toBe("Hello");
Expand Down
2 changes: 1 addition & 1 deletion tests/getInterps.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { it, expect } from "vitest";
import { locales } from "../src/test.js";
import { locales } from "../src/test";

it("should return values with interpolations replaced", () => {
expect(locales.t("interp.hello", { int: 'noway' }, 'en')).toBe("Hello noway");
Expand Down

0 comments on commit 89807ba

Please sign in to comment.