{"id":16931544,"url":"https://github.com/romelperez/ukti","last_synced_at":"2025-03-22T12:31:15.513Z","repository":{"id":196239341,"uuid":"695586723","full_name":"romelperez/ukti","owner":"romelperez","description":"~1kB Type-safe i18n and l10n JavaScript utility.","archived":false,"fork":false,"pushed_at":"2024-03-24T21:19:39.000Z","size":675,"stargazers_count":16,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-18T11:04:32.510Z","etag":null,"topics":["i18n","internationalization","l10n","localization"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/romelperez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"romelperez"}},"created_at":"2023-09-23T16:10:51.000Z","updated_at":"2024-12-15T16:29:12.000Z","dependencies_parsed_at":null,"dependency_job_id":"097098b9-919e-4577-a5dd-db0e739978cc","html_url":"https://github.com/romelperez/ukti","commit_stats":{"total_commits":46,"total_committers":1,"mean_commits":46.0,"dds":0.0,"last_synced_commit":"db166973ddb022113a7a211a728fd97bd876f810"},"previous_names":["romelperez/ukti"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romelperez%2Fukti","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romelperez%2Fukti/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romelperez%2Fukti/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/romelperez%2Fukti/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/romelperez","download_url":"https://codeload.github.com/romelperez/ukti/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244959409,"owners_count":20538625,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["i18n","internationalization","l10n","localization"],"created_at":"2024-10-13T20:44:13.800Z","updated_at":"2025-03-22T12:31:14.990Z","avatar_url":"https://github.com/romelperez.png","language":"TypeScript","funding_links":["https://github.com/sponsors/romelperez"],"categories":[],"sub_categories":[],"readme":"![](https://github.com/romelperez/ukti/raw/main/ukti.png)\n\n# Ukti (उक्ति)\n\n[![version](https://img.shields.io/npm/v/ukti)](https://npmjs.org/package/ukti)\n[![tests](https://github.com/romelperez/ukti/workflows/tests/badge.svg)](https://github.com/romelperez/ukti/actions)\n[![codefactor](https://www.codefactor.io/repository/github/romelperez/ukti/badge)](https://www.codefactor.io/repository/github/romelperez/ukti)\n[![npm bundle size](https://img.shields.io/bundlephobia/minzip/ukti.svg)](https://bundlephobia.com/package/ukti)\n[![downloads](https://img.shields.io/npm/dm/ukti.svg)](https://npmjs.org/package/ukti)\n[![github stars](https://img.shields.io/github/stars/romelperez/ukti.svg?style=social\u0026label=stars)](https://github.com/romelperez/ukti)\n[![license](https://img.shields.io/github/license/romelperez/ukti.svg)](https://github.com/romelperez/ukti/blob/main/LICENSE)\n\n~1kB Type-safe i18n and l10n JavaScript utility.\n\nUkti uses [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) for language codes\nand [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) for region codes\nby default.\n\n\"Ukti\" from Sanskrit \"उक्ति\" translates Speech or Language.\n\n## Install\n\nFor any ESM and CommonJS JavaScript environment. If TypeScript is used, version 4.5+ is required.\n\n```bash\nnpm i ukti\n```\n\nFor UMD version:\n\n```ts\nimport { createUktiTranslator } from 'ukti/build/umd/ukti.umd.cjs'\n```\n\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/ukti/build/umd/ukti.umd.cjs\" /\u003e\n```\n\n```html\n\u003cscript src=\"https://unpkg.com/ukti/build/umd/ukti.umd.cjs\" /\u003e\n```\n\n## Basic Usage\n\nUkti accepts any [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)\nlanguage codes for translations.\n\n```ts\nimport { createUktiTranslator } from 'ukti'\n\ntype Definition = {\n  title: undefined\n  form: {\n    label: undefined\n    error: [{ name: string }]\n  }\n}\n\nconst translator = createUktiTranslator\u003cDefinition\u003e({\n  translations: {\n    en: {\n      title: 'Language',\n      form: {\n        label: 'Type your language',\n        error: 'The language {{name}} is invalid.'\n      }\n    },\n    es: {\n      title: 'Idioma',\n      form: {\n        label: 'Escribe tu idioma',\n        error: 'El idioma {{name}} no está soportado.'\n      }\n    }\n  }\n})\n\nconst t = translator('en')\n\nconsole.log(t.title()) // 'Language'\nconsole.log(t.form.label()) // 'Type your language'\nconsole.log(t.form.error({ name: 'Spanglish' })) // 'The language Spanglish is invalid.'\n```\n\nIf the language used is not defined in the translations, the default language is used.\nIf no configured, `'en'` (English) is used.\n\nThe translations object definition can only have two levels of depth for simplicity.\n\n## Constraints\n\nThe available languages and default language can be specified to constraint the translations.\nAll translations are optional, except the default one. They can be any string but\n[ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) codes are recommended.\n\n```ts\nimport { createUktiTranslator } from 'ukti'\n\ntype Definition = {\n  name: undefined\n}\ntype Languages = 'es' | 'fr' | 'hi'\ntype LanguagesDefault = 'es'\n\nconst translator = createUktiTranslator\u003cDefinition, Languages, LanguagesDefault\u003e({\n  languageDefault: 'es',\n  translations: {\n    es: {\n      name: 'Nombre'\n    },\n    fr: {\n      name: 'Nom'\n    }\n  }\n})\n\nconst t = translator('hi')\n\nconsole.log(t.name()) // 'Nombre'\n```\n\nIf the specified language to be used is not defined (`'hi'`) then the default\nlanguage (`'es'`) is used.\n\nLanguage translations are optional (except the default one) but all definition\nproperties have to be specified in each one.\n\nIf there is an incomplete language translation defined (such as partially defined),\nan empty string is returned when trying to translate it. This is to prevent\ninconsistent translations with some parts in one language and others in another one.\n\n## Regionalization\n\nUkti can optionally have regions by language.\n[ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) country codes\nlist is used by default.\n\n```ts\nimport { createUktiTranslator } from 'ukti'\n\ntype Definition = {\n  friend: undefined\n}\n\nconst translator = createUktiTranslator\u003cDefinition\u003e({\n  translations: {\n    en: {\n      friend: 'Friend',\n      regions: {\n        US: {\n          // United States\n          friend: 'Dude'\n        },\n        CA: {\n          // Canada\n          friend: 'Buddy'\n        }\n      }\n    },\n    es: {\n      friend: 'Amigo',\n      regions: {\n        CO: {\n          // Colombia\n          friend: 'Parce'\n        },\n        VN: {\n          // Venezuela\n          friend: 'Pana'\n        }\n      }\n    }\n  }\n})\n\nconst t = translator('es', 'CO') // Spanish from Colombia\n\nconsole.log(t.friend()) // 'Parce'\n```\n\nIf an unknown region is specified, the general language translation is used.\n\nCustom regions can be specified similar to the [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag) spec.\n\n```ts\nimport { type UktiLanguages, createUktiTranslator } from 'ukti'\n\ntype Definition = {\n  friend: undefined\n}\ntype LanguageDefault = 'en'\ntype Regions = 'USA' | 'Canada'\n\nconst translator = createUktiTranslator\u003cDefinition, UktiLanguages, LanguageDefault, Regions\u003e({\n  translations: {\n    en: {\n      friend: 'Friend',\n      regions: {\n        USA: {\n          friend: 'Dude'\n        },\n        Canada: {\n          friend: 'Buddy'\n        }\n      }\n    }\n  }\n})\n\nconst t = translator('en', 'Canada')\n\nconsole.log(t.friend()) // 'Buddy'\n```\n\n## Templates\n\nTranslations texts are templates supporting variables interpolations.\ne.g. displaying different words based on conditions.\n\n```ts\nimport { createUktiTranslator } from 'ukti'\n\ntype Definition = {\n  stock: [{ qty: number }]\n}\n\nconst translator = createUktiTranslator\u003cDefinition\u003e({\n  translations: {\n    en: {\n      stock: \"There {{qty == 1 ? 'is' : 'are'}} {{qty}} product{{qty == 1 ? '' : 's'}} available\"\n    }\n  }\n})\n\nconst t = translator('en')\n\nconsole.log(t.stock({ qty: 1 })) // 'There is 1 product available'\nconsole.log(t.stock({ qty: 3 })) // 'There are 3 products available'\n```\n\nThe variables can be formatted using the native JavaScript [`Intl`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl)\nobject methods before providing them to the translator.\n\n```ts\nimport { createUktiTranslator } from 'ukti'\n\ntype Definition = {\n  list: [{ items: string; length: number; location: string }]\n}\n\nconst translator = createUktiTranslator\u003cDefinition\u003e({\n  translations: {\n    en: {\n      list: 'The land vehicle{{length == 1 ? \"\" : \"s\"}} used {{length == 1 ? \"is\" : \"are\"}} {{items}} in the {{location}}.'\n    }\n  }\n})\n\nconst t = translator('en')\n\nconst items = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' }).format([\n  'motorcycle',\n  'bus',\n  'car'\n])\n\nconsole.log(t.list({ items, length: items.length, location: 'countryside' }))\n// 'The land vehicles used are motorcycle, bus, and car in the countryside.'\n\nconsole.log(t.list({ items: 'car', length: 1, location: 'city' }))\n// 'The land vehicle used is car in the city.'\n```\n\nUkti supports the comparison operators `==`, `===`, `!=`, `!==`, `\u003e`, `\u003e=`, `\u003c`, `\u003c=`\nin the template conditionals. All comparators are strict, so `==` and `===` are interchangeably.\n\nIf the template requires variables but they are not provided or `undefined` when\ncalling the translation, an empty string is returned to prevent incorrect translations.\nAn error message is logged in the console too.\n\n## Modularization\n\nUkti provides some type utilities to allow modularization in translations.\n\n```ts\nimport {\n  type UktiLanguages, // Language code list of ISO 639-1\n  type UktiRegions, // Countries code list of ISO 3166-1 alpha-2\n  type UktiTranslations, // UktiTranslations\u003cDefinition, Languages?, DefaultLocale = 'en', Regions?\u003e\n  type UktiTranslation, // UktiTranslation\u003cDefinition, Regions?\u003e\n  type UktiTranslationData, // UktiTranslationData\u003cDefinition\u003e\n  type UktiTranslationDataPartial, // UktiTranslationDataPartial\u003cDefinition\u003e\n  createUktiTranslator\n} from 'ukti'\n\ntype Definition = {\n  friend: undefined\n}\n\nconst translation_EN_Core: UktiTranslationData\u003cDefinition\u003e = {\n  friend: 'Friend'\n}\n\nconst translation_EN_US: UktiTranslationDataPartial\u003cDefinition\u003e = {\n  friend: 'Dude'\n}\n\nconst translation_EN_CA: UktiTranslationDataPartial\u003cDefinition\u003e = {\n  friend: 'Buddy'\n}\n\nconst translation_EN: UktiTranslation\u003cDefinition, UktiRegions\u003e = {\n  ...translation_EN_Core,\n  regions: {\n    US: translation_EN_US,\n    CA: translation_EN_CA\n  }\n}\n\ntype LanguageDefault = 'en'\n\nconst translations: UktiTranslations\u003cDefinition, UktiLanguages, LanguageDefault, UktiRegions\u003e = {\n  en: translation_EN\n}\n\nconst translator = createUktiTranslator\u003cDefinition, UktiLanguages, LanguageDefault, UktiRegions\u003e({\n  translations\n})\n\nconst language = 'en' as const satisfies UktiLanguages\nconst region = 'CA' as const satisfies UktiRegions\nconst t = translator(language, region)\n\nconsole.log(t.friend()) // 'Buddy'\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fromelperez%2Fukti","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fromelperez%2Fukti","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fromelperez%2Fukti/lists"}