diff --git a/.changeset/odd-hornets-taste.md b/.changeset/odd-hornets-taste.md new file mode 100644 index 000000000..831017718 --- /dev/null +++ b/.changeset/odd-hornets-taste.md @@ -0,0 +1,8 @@ +--- +"@replexica/spec": minor +"@replexica/sdk": minor +"replexica": minor +"@replexica/cli": minor +--- + +Update locale code resolution logic diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..7a55a5090 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true \ No newline at end of file diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 38183fd3d..2054269f6 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,5 +1,5 @@ import Z from 'zod'; -import { sourceLocaleSchema, targetLocaleSchema, allLocalesSchema } from '@replexica/spec'; +import { localeCodeSchema } from '@replexica/spec'; import { createId } from "@paralleldrive/cuid2"; const engineParamsSchema = Z.object({ @@ -24,12 +24,12 @@ const payloadSchema = Z.record( ); const localizationParamsSchema = Z.object({ - sourceLocale: sourceLocaleSchema, - targetLocale: targetLocaleSchema, + sourceLocale: localeCodeSchema, + targetLocale: localeCodeSchema, }); const referenceSchema = Z.record( - allLocalesSchema, + localeCodeSchema, payloadSchema, ); @@ -178,4 +178,4 @@ export class ReplexicaEngine { return 0; } } -} \ No newline at end of file +} diff --git a/packages/spec/src/config.ts b/packages/spec/src/config.ts index 622619d1a..5a7debb75 100644 --- a/packages/spec/src/config.ts +++ b/packages/spec/src/config.ts @@ -1,11 +1,11 @@ import Z from 'zod'; -import { allLocalesSchema, sourceLocaleSchema, targetLocaleSchema } from './locales'; +import { localeCodeSchema } from './locales'; import { bucketTypeSchema } from './formats'; // common export const localeSchema = Z.object({ - source: sourceLocaleSchema, - targets: Z.array(targetLocaleSchema), + source: localeCodeSchema, + targets: Z.array(localeCodeSchema), }); // factories @@ -139,7 +139,7 @@ export const configV1_1Definition = extendConfigDefinition(configV1Definition, { export const configV1_2Definition = extendConfigDefinition(configV1_1Definition, { createSchema: (baseSchema) => baseSchema.extend({ locale: localeSchema.extend({ - extraSource: allLocalesSchema.optional(), + extraSource: localeCodeSchema.optional(), }), }), createDefaultValue: (baseDefaultValue) => ({ @@ -166,4 +166,4 @@ export function parseI18nConfig(rawConfig: unknown) { } } -export const defaultConfig = LATEST_CONFIG_DEFINITION.defaultValue; \ No newline at end of file +export const defaultConfig = LATEST_CONFIG_DEFINITION.defaultValue; diff --git a/packages/spec/src/locales.ts b/packages/spec/src/locales.ts index 9713cf7b3..7a5ae893e 100644 --- a/packages/spec/src/locales.ts +++ b/packages/spec/src/locales.ts @@ -1,258 +1,74 @@ import Z from 'zod'; -// Core locales -const coreFullLocales = [ - 'en-US', // English (United States) - 'en-GB', // English (United Kingdom) - 'en-AU', // English (Australia) - 'en-CA', // English (Canada) - - 'es-ES', // Spanish (Spain) - 'es-MX', // Spanish (Mexico) - 'es-AR', // Spanish (Argentina) - - 'fr-FR', // French (France) - 'fr-CA', // French (Canada) - 'fr-BE', // French (Belgium) - - 'de-DE', // German (Germany) - 'de-AT', // German (Austria) - 'de-CH', // German (Switzerland) - - 'pt-PT', // Portuguese (Portugal) - 'pt-BR', // Portuguese (Brazil) - - 'it-IT', // Italian (Italy) - 'it-CH', // Italian (Switzerland) - - 'ru-RU', // Russian (Russia) - 'ru-BY', // Russian (Belarus) - - 'zh-Hans-CN', // Simplified Chinese (China) - 'zh-Hant-TW', // Traditional Chinese (Taiwan) - 'zh-Hant-HK', // Traditional Chinese (Hong Kong) - - 'ar-EG', // Arabic (Egypt) - 'ar-SA', // Arabic (Saudi Arabia) - 'ar-AE', // Arabic (United Arab Emirates) - 'ar-MA', // Arabic (Morocco) - - 'nl-NL', // Dutch (Netherlands) - 'nl-BE', // Dutch (Belgium) - - 'sv-SE', // Swedish (Sweden) - - 'pl-PL', // Polish (Poland) - - 'vi-VN', // Vietnamese (Vietnam) - - 'id-ID', // Indonesian (Indonesia) - - 'ms-MY', // Malay (Malaysia) - - 'th-TH', // Thai (Thailand) - - 'fi-FI', // Finnish (Finland) - - 'ca-ES', // Catalan (Spain) - 'ja-JP', // Japanese (Japan) - 'uk-UA', // Ukrainian (Ukraine) - 'hi-IN', // Hindi (India) - 'ko-KR', // Korean (South Korea) - 'tr-TR', // Turkish (Turkey) -] as const; -const coreShortcutLocales = [ - 'en', // English - 'es', // Spanish - 'fr', // French - 'ca', // Catalan - 'ja', // Japanese - 'de', // German - 'pt', // Portuguese - 'it', // Italian - 'ru', // Russian - 'uk', // Ukrainian - 'hi', // Hindi - 'zh', // Simplified Chinese - 'zh-Hans', // Simplified Chinese - 'ko', // Korean - 'tr', // Turkish - 'ar', // Arabic - 'nl', // Dutch - 'pl', // Polish - 'vi', // Vietnamese - 'id', // Indonesian - 'ms', // Malay - 'th', // Thai - 'fi', // Finnish -] as const; -const coreLocales = [ - ...coreFullLocales, - ...coreShortcutLocales, -] as const; - -// Source locales -const sourceOnlyFullLocales = [ - 'cs-CZ', // Czech (Czech Republic) - 'zh-Hant-HK', // Traditional Chinese (Hong Kong) - 'sk-SK', // Slovak (Slovakia) -] as const; -const sourceOnlyShortcutLocales = [ - 'cs', // Czech - 'zh-Hant', // Traditional Chinese - 'sk', // Slovak -] as const; -const sourceOnlyLocales = [ - ...sourceOnlyFullLocales, - ...sourceOnlyShortcutLocales, -] as const; - -const sourceFullLocales = [ - ...coreFullLocales, - ...sourceOnlyFullLocales, -] as const; - -const sourceShortcutLocales = [ - ...coreShortcutLocales, - ...sourceOnlyShortcutLocales, -] as const; - -export const sourceLocales = [ - ...coreLocales, - ...sourceOnlyLocales, -] as const; - - - -// Target locales -const targetOnlyFullLocales = [ - 'shyriiwook', // Shyriiwook -] as const; -const targetOnlyShortcutLocales = [ -] as const; -const targetOnlyLocales = [ - ...targetOnlyFullLocales, - ...targetOnlyShortcutLocales, -] as const; - -const targetFullLocales = [ - ...coreFullLocales, - ...targetOnlyFullLocales, -] as const; - - -const targetShortcutLocales = [ - ...coreShortcutLocales, - ...targetOnlyShortcutLocales, -] as const; -export const targetLocales = [ - ...coreLocales, - ...targetOnlyLocales, -] as const; - - -// Locale resolution map - -const coreLocaleResolutionMap: Record = { - en: 'en-US', - es: 'es-ES', - fr: 'fr-FR', - ca: 'ca-ES', - ja: 'ja-JP', - de: 'de-DE', - pt: 'pt-PT', - it: 'it-IT', - ru: 'ru-RU', - uk: 'uk-UA', - hi: 'hi-IN', - zh: 'zh-Hans-CN', - 'zh-Hans': 'zh-Hans-CN', - ko: 'ko-KR', - tr: 'tr-TR', - ar: 'ar-EG', - nl: 'nl-NL', - pl: 'pl-PL', - vi: 'vi-VN', - id: 'id-ID', - ms: 'ms-MY', - th: 'th-TH', - fi: 'fi-FI', -}; - -const sourceOnlyLocaleResolutionMap: Record = { - cs: 'cs-CZ', - 'zh-Hant': 'zh-Hant-HK', - sk: 'sk-SK', -}; - -const targetOnlyLocaleResolutionMap: Record = { -}; - -const sourceLocaleResolutionMap: Record = { - ...coreLocaleResolutionMap, - ...sourceOnlyLocaleResolutionMap, -}; - -const targetLocaleResolutionMap: Record = { - ...coreLocaleResolutionMap, - ...targetOnlyLocaleResolutionMap, -}; - -const localeResolutionMap: Record = { - ...sourceLocaleResolutionMap, - ...targetLocaleResolutionMap, -}; - -// Exports - -// All locales - -export const shortcutLocales = [ - ...coreShortcutLocales, - ...sourceOnlyShortcutLocales, - ...targetOnlyShortcutLocales, -] as const; - -export const fullLocales = [ - ...coreFullLocales, - ...sourceOnlyFullLocales, - ...targetOnlyFullLocales, -] as const; - -export const allLocales = [ - ...coreLocales, - ...sourceOnlyLocales, - ...targetOnlyLocales, -] as const; - -// Schemas - -export const sourceFullLocaleSchema = Z.enum(sourceFullLocales); -export const sourceLocaleSchema = Z.enum(sourceLocales); -export const targetFullLocaleSchema = Z.enum(targetFullLocales); -export const targetLocaleSchema = Z.enum(targetLocales); - -export const allLocalesSchema = Z.enum(allLocales); - -// Shortcut resolvers - -export const resolveSourceLocale = (locale: typeof sourceLocales[number]): typeof sourceFullLocales[number] => { - if (sourceFullLocales.includes(locale as typeof sourceFullLocales[number])) { - return locale as typeof sourceFullLocales[number]; - } - return sourceLocaleResolutionMap[locale as typeof sourceShortcutLocales[number]]; +const localeMap = { + ur: ['ur-PK'], + vi: ['vi-VN'], + tr: ['tr-TR'], + ta: ['ta-IN'], + sr: ['sr-RS'], + hu: ['hu-HU'], + he: ['he-IL'], + et: ['et-EE'], + el: ['el-GR'], + da: ['da-DK'], + az: ['az-AZ'], + th: ['th-TH'], + sv: ['sv-SE'], + en: ['en-US', 'en-GB', 'en-AU', 'en-CA'], + es: ['es-ES', 'es-419', 'es-MX', 'es-AR'], + fr: ['fr-FR', 'fr-CA', 'fr-BE'], + ca: ['ca-ES'], + ja: ['ja-JP'], + de: ['de-DE', 'de-AT', 'de-CH'], + pt: ['pt-PT', 'pt-BR'], + it: ['it-IT', 'it-CH'], + ru: ['ru-RU', 'ru-BY'], + uk: ['uk-UA'], + hi: ['hi-IN'], + zh: ['zh-CN', 'zh-TW', 'zh-HK', 'zh-Hans', 'zh-Hant', 'zh-Hant-HK', 'zh-Hant-TW', 'zh-Hans-CN'], + ko: ['ko-KR'], + ar: ['ar-EG', 'ar-SA', 'ar-AE', 'ar-MA'], + bg: ['bg-BG'], + cs: ['cs-CZ'], + nl: ['nl-NL', 'nl-BE'], + pl: ['pl-PL'], + id: ['id-ID'], + ms: ['ms-MY'], + fi: ['fi-FI'], + eu: ['eu-ES'], + hr: ['hr-HR'], + iw: ['iw-IL'], + km: ['km-KH'], + lv: ['lv-LV'], + no: ['no-NO'], + ro: ['ro-RO'], + sk: ['sk-SK'], +} as const; + +export type LocaleCodeShort = keyof typeof localeMap; +export type LocaleCodeFull = typeof localeMap[LocaleCodeShort][number]; +export type LocaleCode = LocaleCodeShort | LocaleCodeFull; + +export const localeCodeSchema = Z.string().refine((value) => { + const existingShortLocaleCode = Object.keys(localeMap).includes(value); + if (existingShortLocaleCode) { return true; } + + const existingFullLocaleCode = Object.values(localeMap).flat().includes(value as any); + if (existingFullLocaleCode) { return true; } + + return false; +}); + +export const resolveLocaleCode = (value: LocaleCode): LocaleCodeFull => { + const existingFullLocaleCode = Object.values(localeMap).flat().includes(value as any); + if (existingFullLocaleCode) { return value as LocaleCodeFull; } + + const existingShortLocaleCode = Object.keys(localeMap).includes(value); + if (existingShortLocaleCode) { + const correspondingFullLocales = localeMap[value as LocaleCodeShort]; + const fallbackFullLocale = correspondingFullLocales[0]; + return fallbackFullLocale; + } + + throw new Error(`Invalid locale code: ${value}`); }; - -export const resolveTargetLocale = (locale: typeof targetLocales[number]): typeof targetFullLocales[number] => { - if (targetFullLocales.includes(locale as typeof targetFullLocales[number])) { - return locale as typeof targetFullLocales[number]; - } - return targetLocaleResolutionMap[locale as typeof targetShortcutLocales[number]]; -}; - -export const resolveLocale = (locale: typeof allLocales[number]): typeof fullLocales[number] => { - if (fullLocales.includes(locale as typeof fullLocales[number])) { - return locale as typeof fullLocales[number]; - } - return localeResolutionMap[locale as typeof shortcutLocales[number]]; -} \ No newline at end of file