From b39760d9d79f4315cbcffa5ea64627565b56c880 Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Thu, 22 Jan 2026 16:07:18 -0700 Subject: [PATCH] add i18n helper to sdk --- sdk/package/lib/i18n/index.ts | 74 +++++++++++++++++++++++++++++++++++ sdk/package/lib/index.ts | 1 + 2 files changed, 75 insertions(+) create mode 100644 sdk/package/lib/i18n/index.ts diff --git a/sdk/package/lib/i18n/index.ts b/sdk/package/lib/i18n/index.ts new file mode 100644 index 000000000..39b3c9d22 --- /dev/null +++ b/sdk/package/lib/i18n/index.ts @@ -0,0 +1,74 @@ +/** + * Internationalization (i18n) utilities for StartOS packages. + * + * @example + * ```typescript + * // In package's i18n/index.ts: + * import { setupI18n } from '@start9labs/start-sdk' + * import defaultDict, { DEFAULT_LANG } from './dictionaries/default' + * import translations from './dictionaries/translations' + * + * export const i18n = setupI18n(defaultDict, translations, DEFAULT_LANG) + * ``` + */ + +type ParamValue = string | number | Date + +/** + * Creates a typed i18n function for a package. + * + * @param defaultDict - The default language dictionary mapping strings to numeric indices + * @param translations - Translation dictionaries for each supported locale + * @param defaultLang - The default language code (e.g., 'en_US') + * @returns A typed i18n function that accepts dictionary keys and optional parameters + */ +export function setupI18n< + Dict extends Record, + Translations extends Record>, +>(defaultDict: Dict, translations: Translations, defaultLang: string) { + const lang = process.env.LANG?.replace(/\.UTF-8$/, "") || defaultLang + + // Convert locale format from en_US to en-US for Intl APIs + const intlLocale = lang.replace("_", "-") + + function getTranslation(): Record | null { + if (lang === defaultLang) return null + + const availableLangs = Object.keys(translations) as (keyof Translations)[] + + const match = + availableLangs.find((l) => l === lang) ?? + availableLangs.find((l) => String(l).startsWith(lang.split("_")[0] + "_")) + + return match ? (translations[match] as Record) : null + } + + const translation = getTranslation() + + function formatValue(value: ParamValue): string { + if (typeof value === "number") { + return new Intl.NumberFormat(intlLocale).format(value) + } + if (value instanceof Date) { + return new Intl.DateTimeFormat(intlLocale).format(value) + } + return value + } + + return function i18n( + key: keyof Dict, + params?: Record, + ): string { + let result = translation + ? translation[defaultDict[key as string]] + : (key as string) + + if (params) { + for (const [paramName, value] of Object.entries(params)) { + result = result.replace(`\${${paramName}}`, formatValue(value)) + } + } + + return result + } +} diff --git a/sdk/package/lib/index.ts b/sdk/package/lib/index.ts index d367368af..f7a9bcb4c 100644 --- a/sdk/package/lib/index.ts +++ b/sdk/package/lib/index.ts @@ -23,6 +23,7 @@ export { matches, utils, } +export { setupI18n } from "./i18n" export * as T from "./types" export { Daemons } from "./mainFn/Daemons" export { SubContainer } from "./util/SubContainer"