diff --git a/packages/config/src/core.mts b/packages/config/src/core.mts index 282dfb13..b5756f1a 100644 --- a/packages/config/src/core.mts +++ b/packages/config/src/core.mts @@ -17,6 +17,7 @@ export const applyDefaultValues = async ( adapter: undefined, generateOnlyTypes: false, + maskTranslationText: true, banner: '/* eslint-disable */', runAfterGenerator: undefined, // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/config/src/types.mts b/packages/config/src/types.mts index 08d6793c..426bf846 100644 --- a/packages/config/src/types.mts +++ b/packages/config/src/types.mts @@ -39,6 +39,7 @@ export type GeneratorConfig = { adapterFileName?: string generateOnlyTypes?: boolean + maskTranslationText?: boolean banner?: string runAfterGenerator?: string | undefined @@ -57,6 +58,7 @@ export type GeneratorConfigWithDefaultValues = GeneratorConfig & { esmImports: EsmImportsOption generateOnlyTypes: boolean + maskTranslationText: boolean banner: string runAfterGenerator: string | undefined } diff --git a/packages/generator/README.md b/packages/generator/README.md index 097b1eaf..f2198a29 100644 --- a/packages/generator/README.md +++ b/packages/generator/README.md @@ -205,23 +205,24 @@ You can set options for the generator inside a `.typesafe-i18n.json`-file in you The available options are: -| key | type | default value | -| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------- | -| [adapter](#adapter) | `'angular' \| 'node' \| 'react' \| 'solid' \| 'svelte' \| 'vue' \| undefined` | `undefined` | -| [adapters](#adapters) | `Array<'angular' \| 'node' \| 'react' \| 'solid' \| 'svelte' \| 'vue'> \| undefined` | `undefined` -| [baseLocale](#baselocale) | `string` | `'en'` | -| [outputFormat](#outputformat) | `'TypeScript'` | `'JavaScript'` | `'TypeScript'` | -| [esmImports](#esmimports) | `boolean | '.js' | 'fileEnding' | `false` | -| [generateOnlyTypes](#generateonlytypes) | `boolean` | `false` | -| [runAfterGenerator](#runaftergenerator) | `string` | `undefined` | `undefined` | -| [banner](#banner) | `string` | `'/* eslint-disable */'` | -| [outputPath](#outputpath) | `string` | `'./src/i18n/'` | -| [typesFileName](#typesfilename) | `string` | `'i18n-types'` | -| [utilFileName](#utilfilename) | `string` | `'i18n-util'` | -| [formattersTemplateFileName](#formatterstemplatefilename) | `string` | `'formatters'` | -| [typesTemplateFileName](#typestemplatefilename) | `string` | `'custom-types'` | -| [adapterFileName](#adapterfilename) | `string` | `undefined` | `undefined` | -| [tempPath](#temppath) | `string` | `'./node_modules/typesafe-i18n/temp-output/'` | +| key | type | default value | +| --------------------------------------------------------- |--------------------------------------------------------------------------------------| --------------------------------------------- | +| [adapter](#adapter) | `'angular' \| 'node' \| 'react' \| 'solid' \| 'svelte' \| 'vue' \| undefined` | `undefined` | +| [adapters](#adapters) | `Array<'angular' \| 'node' \| 'react' \| 'solid' \| 'svelte' \| 'vue'> \| undefined` | `undefined` +| [baseLocale](#baselocale) | `string` | `'en'` | +| [outputFormat](#outputformat) | `'TypeScript'` | `'JavaScript'` | `'TypeScript'` | +| [esmImports](#esmimports) | `boolean` \| `'.js'` \| `'fileEnding'` | `false` | +| [generateOnlyTypes](#generateonlytypes) | `boolean` | `false` | +| [maskTranslationText](#masktranslationtext) | `boolean` | `true` | +| [runAfterGenerator](#runaftergenerator) | `string` | `undefined` | `undefined` | +| [banner](#banner) | `string` | `'/* eslint-disable */'` | +| [outputPath](#outputpath) | `string` | `'./src/i18n/'` | +| [typesFileName](#typesfilename) | `string` | `'i18n-types'` | +| [utilFileName](#utilfilename) | `string` | `'i18n-util'` | +| [formattersTemplateFileName](#formatterstemplatefilename) | `string` | `'formatters'` | +| [typesTemplateFileName](#typestemplatefilename) | `string` | `'custom-types'` | +| [adapterFileName](#adapterfilename) | `string` | `undefined` | `undefined` | +| [tempPath](#temppath) | `string` | `'./node_modules/typesafe-i18n/temp-output/'` | #### `adapter` @@ -252,6 +253,16 @@ Set this option to `'fileEnding'` if the module import needs to be done with a ` If you don't want to use the auto-generated helpers and instead write your own wrappers, you can set this option to `true`. +#### `maskTranslationText` + +Controls whether zero-width space characters (U+200B) are inserted between characters in JSDoc comments for Translation types. + +When enabled (default), this prevents code search tools from matching the generated type file JSDoc comments when searching for translation text, directing you instead to the actual translation source files. The zero-width spaces are invisible but keep the text readable in IDE tooltips. + +Some code analysis tools may have issues with these invisible characters. If you encounter problems, you can disable this feature by setting this option to `false`. + +**Note**: This only affects the `Translation` type JSDoc comments in generated type files, not the `TranslationFunctions` type. + #### `runAfterGenerator` This hook allows you to e.g. run a code formatting/linting command after the generator completes. When a command is provided, a [`child_process`](https://nodejs.org/api/child_process.html#child_processexeccommand-options-callback) gets spawned with that command e.g. `npm run prettier --write "src/i18n"` diff --git a/packages/generator/src/files/generate-types.mts b/packages/generator/src/files/generate-types.mts index ef978a21..96806a82 100644 --- a/packages/generator/src/files/generate-types.mts +++ b/packages/generator/src/files/generate-types.mts @@ -55,7 +55,15 @@ const doesATranslationContainParams = (p: ParsedResult[]): boolean => ) const getTypes = ( - { translations, baseLocale, locales, typesTemplateFileName, banner, namespaces }: GenerateTypesType, + { + translations, + baseLocale, + locales, + typesTemplateFileName, + banner, + namespaces, + maskTranslationText, + }: GenerateTypesType, logger: Logger, ) => { const usesNamespaces = !!namespaces.length @@ -69,7 +77,13 @@ const getTypes = ( const jsDocsInfo = createJsDocsMapping(parsedTranslations) - const translationType = createTranslationType(parsedTranslations, jsDocsInfo, 'RootTranslation', namespaces) + const translationType = createTranslationType( + parsedTranslations, + jsDocsInfo, + 'RootTranslation', + namespaces, + maskTranslationText, + ) const shouldImportRequiredParamsType = supportsTemplateLiteralTypes && doesATranslationContainParams(parsedTranslations) diff --git a/packages/generator/src/files/generate-types/translations.mts b/packages/generator/src/files/generate-types/translations.mts index 70c18e91..1f0448f5 100644 --- a/packages/generator/src/files/generate-types/translations.mts +++ b/packages/generator/src/files/generate-types/translations.mts @@ -16,6 +16,7 @@ export const createTranslationType = ( jsDocInfo: JsDocInfos, nameOfType: string, namespaces: string[] = [], + maskTranslationText = true, ): string => { const parsedTranslationsWithoutNamespaces = parsedTranslations.filter((parsedResult) => { const keys = Object.keys(parsedResult) @@ -24,7 +25,7 @@ export const createTranslationType = ( const translationType = `type ${nameOfType} = ${wrapObjectType(parsedTranslationsWithoutNamespaces, () => mapToString(parsedTranslationsWithoutNamespaces, (parsedResultEntry) => - createTranslationTypeEntry(parsedResultEntry, jsDocInfo), + createTranslationTypeEntry(parsedResultEntry, jsDocInfo, maskTranslationText), ), )}` @@ -46,6 +47,8 @@ export const createTranslationType = ( isArray(parsedTranslations) ? parsedTranslations : [parsedTranslations], jsDocInfo, getTypeNameForNamespace(namespace), + [], + maskTranslationText, ) ) }) @@ -55,12 +58,16 @@ export const createTranslationType = ( ${namespaceTranslationsTypes.join(NEW_LINE + NEW_LINE)}` } -const createTranslationTypeEntry = (resultEntry: ParsedResult, jsDocInfo: JsDocInfos): string => { +const createTranslationTypeEntry = ( + resultEntry: ParsedResult, + jsDocInfo: JsDocInfos, + maskTranslationText: boolean, +): string => { if (isParsedResultEntry(resultEntry)) { const { key, args, parentKeys } = resultEntry const nestedKey = getNestedKey(key, parentKeys) - const jsDocString = createJsDocsString(jsDocInfo[nestedKey] as JsDocInfo, true, false, true) + const jsDocString = createJsDocsString(jsDocInfo[nestedKey] as JsDocInfo, true, false, maskTranslationText) const translationType = generateTranslationType(args) return ` @@ -68,7 +75,7 @@ const createTranslationTypeEntry = (resultEntry: ParsedResult, jsDocInfo: JsDocI } return processNestedParsedResult(resultEntry, (parsedResultEntry) => - createTranslationTypeEntry(parsedResultEntry, jsDocInfo), + createTranslationTypeEntry(parsedResultEntry, jsDocInfo, maskTranslationText), ) } diff --git a/packages/generator/test/generated/mask-translation-text-disabled/de/index.js b/packages/generator/test/generated/mask-translation-text-disabled/de/index.js new file mode 100644 index 00000000..aed7d238 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-disabled/de/index.js @@ -0,0 +1,13 @@ +// @ts-check + +/** + * @typedef { import('../types.actual').Translation } Translation + */ + +/** @type { Translation } */ +const de = { + // this is an example Translation, just rename or delete this folder if you want + HI: 'Hallo {name}! Bitte hinterlasse einen Stern, wenn dir das Projekt gefällt: https://github.com/ivanhofer/typesafe-i18n', +} + +module.exports = de diff --git a/packages/generator/test/generated/mask-translation-text-disabled/en/index.js b/packages/generator/test/generated/mask-translation-text-disabled/en/index.js new file mode 100644 index 00000000..a3d11869 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-disabled/en/index.js @@ -0,0 +1,13 @@ +// @ts-check + +/** + * @typedef { import('../types.actual').BaseTranslation } BaseTranslation + */ + +/** @type { BaseTranslation } */ +const en = { + // TODO: your translations go here + HI: 'Hi {name:string}! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n', +} + +module.exports = en diff --git a/packages/generator/test/generated/mask-translation-text-disabled/formatters-template.expected.js b/packages/generator/test/generated/mask-translation-text-disabled/formatters-template.expected.js new file mode 100644 index 00000000..1997db52 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-disabled/formatters-template.expected.js @@ -0,0 +1,20 @@ +// @ts-check + +/** + * @typedef { import('typesafe-i18n').FormattersInitializer } FormattersInitializer + * @typedef { import('./types.actual').Locales } Locales + * @typedef { import('./types.actual').Formatters } Formatters + */ + +/** + * @param { Locales } locale + * @return { Formatters } + */ +export const initFormatters = (locale) => { + /** @type { Formatters } */ + const formatters = { + // add your formatter functions here + } + + return formatters +} diff --git a/packages/generator/test/generated/mask-translation-text-disabled/types-template.expected.d.ts b/packages/generator/test/generated/mask-translation-text-disabled/types-template.expected.d.ts new file mode 100644 index 00000000..af488c20 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-disabled/types-template.expected.d.ts @@ -0,0 +1 @@ +// use this file to export your custom types; these types will be imported by './types.actual.d.ts' \ No newline at end of file diff --git a/packages/generator/test/generated/mask-translation-text-disabled/types.expected.d.ts b/packages/generator/test/generated/mask-translation-text-disabled/types.expected.d.ts new file mode 100644 index 00000000..dd5f58e2 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-disabled/types.expected.d.ts @@ -0,0 +1,41 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +/* eslint-disable */ +import type { BaseTranslation as BaseTranslationType, LocalizedString, RequiredParams } from 'typesafe-i18n' + +import type { name } from './types-template.actual' + +export type BaseTranslation = BaseTranslationType +export type BaseLocale = 'en' + +export type Locales = + | 'en' + +export type Translation = RootTranslation + +export type Translations = RootTranslation + +type RootTranslation = { + /** + * Hi {0} + * @param {name} 0 + */ + HELLO: RequiredParams<'0'> + /** + * Welcome to {app}! + * @param {unknown} app + */ + WELCOME: RequiredParams<'app'> +} + +export type TranslationFunctions = { + /** + * Hi {0} + */ + HELLO: (arg0: name) => LocalizedString + /** + * Welcome to {app}! + */ + WELCOME: (arg: { app: unknown }) => LocalizedString +} + +export type Formatters = {} diff --git a/packages/generator/test/generated/mask-translation-text-disabled/util.expected.async.js b/packages/generator/test/generated/mask-translation-text-disabled/util.expected.async.js new file mode 100644 index 00000000..e5ee2ab3 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-disabled/util.expected.async.js @@ -0,0 +1,49 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +// @ts-check +/* eslint-disable */ + +/** + * @typedef { import('./types.actual').Locales } Locales + * @typedef { import('./types.actual').Translations } Translations + */ + +import { initFormatters } from './formatters-template.actual' + +import { loadedFormatters, loadedLocales, locales } from './util.actual' + +const localeTranslationLoaders = { + en: () => import('./en'), +} + +/** + * @param { Locales } locale + * @param { Partial } dictionary + * @return { Translations } + */ +const updateDictionary = (locale, dictionary) => + loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary } + +/** + * @param { Locales } locale + * @return { Promise } + */ +export const importLocaleAsync = async (locale) => + /** @type { Translations } */ (/** @type { unknown } */ ((await localeTranslationLoaders[locale]()).default)) + +/** + * @param { Locales } locale + * @return { Promise } + */ +export const loadLocaleAsync = async (locale) => { + updateDictionary(locale, await importLocaleAsync(locale)) + loadFormatters(locale) +} + +export const loadAllLocalesAsync = () => Promise.all(locales.map(loadLocaleAsync)) + +/** + * @param { Locales } locale + * @return { void } + */ +export const loadFormatters = (locale) => + void (loadedFormatters[locale] = initFormatters(locale)) diff --git a/packages/generator/test/generated/mask-translation-text-disabled/util.expected.js b/packages/generator/test/generated/mask-translation-text-disabled/util.expected.js new file mode 100644 index 00000000..c11b64b3 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-disabled/util.expected.js @@ -0,0 +1,69 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +// @ts-check +/* eslint-disable */ + +/** + * @typedef { import('typesafe-i18n').TranslateByString } TranslateByString + * @typedef { import('typesafe-i18n').LocaleTranslations } LocaleTranslations + * @typedef { import('typesafe-i18n').LocaleTranslationFunctions } LocaleTranslationFunctions + * @typedef { import('typesafe-i18n/detectors').LocaleDetector } LocaleDetector + * @typedef { import('./types.actual').Locales } Locales + * @typedef { import('./types.actual').Formatters } Formatters + * @typedef { import('./types.actual').Translations } Translations + * @typedef { import('./types.actual').TranslationFunctions } TranslationFunctions + */ + +import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n' + +import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors' +import { initExtendDictionary } from 'typesafe-i18n/utils' + +/** @type { Locales } */ +export const baseLocale = 'en' + +/** @type { Locales[] } */ +export const locales = [ + 'en' +] + +/** + * @param { string } locale + * @return { locale is Locales } + */ +export const isLocale = (locale) => locales.includes(/** @type { Locales } */ (locale)) + +export const loadedLocales = /** @type { Record } */ ({}) + +export const loadedFormatters = /** @type { Record } */ ({}) + +/** @type { ReturnType> } */ +export const extendDictionary = initExtendDictionary() + +/** + * @param { Locales } locale + * @return { TranslateByString } + */ +export const i18nString = (locale) => initI18nString(locale, loadedFormatters[locale]) + +/** + * @param { Locales } locale + * @return { TranslationFunctions } + */ +export const i18nObject = (locale) => + initI18nObject( + locale, + loadedLocales[locale], + loadedFormatters[locale] + ) + +/** + * @return { LocaleTranslationFunctions } + */ +export const i18n = () => + initI18n(loadedLocales, loadedFormatters) + +/** + * @param { LocaleDetector[] } detectors + * @return { Locales } + */ +export const detectLocale = (...detectors) => detectLocaleFn(baseLocale, locales, ...detectors) diff --git a/packages/generator/test/generated/mask-translation-text-disabled/util.expected.sync.js b/packages/generator/test/generated/mask-translation-text-disabled/util.expected.sync.js new file mode 100644 index 00000000..3120f3bb --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-disabled/util.expected.sync.js @@ -0,0 +1,41 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +// @ts-check +/* eslint-disable */ + +/** + * @typedef { import('./types.actual').Locales } Locales + * @typedef { import('./types.actual').Translations } Translations + */ + +import { initFormatters } from './formatters-template.actual' + +import { loadedFormatters, loadedLocales, locales } from './util.actual' + +import en from './en' + +const localeTranslations = { + en, +} + +/** + * @param { Locales } locale + * @return { void } + */ +export const loadLocale = (locale) => { + if (loadedLocales[locale]) return + + loadedLocales[locale] = /** @type { Translations } */ (/** @type { unknown } */ (localeTranslations[locale])) + loadFormatters(locale) +} + +/** + * @return { void } + */ +export const loadAllLocales = () => locales.forEach(loadLocale) + +/** + * @param { Locales } locale + * @return { void } + */ +export const loadFormatters = (locale) => + void (loadedFormatters[locale] = initFormatters(locale)) diff --git a/packages/generator/test/generated/mask-translation-text-enabled/de/index.js b/packages/generator/test/generated/mask-translation-text-enabled/de/index.js new file mode 100644 index 00000000..aed7d238 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-enabled/de/index.js @@ -0,0 +1,13 @@ +// @ts-check + +/** + * @typedef { import('../types.actual').Translation } Translation + */ + +/** @type { Translation } */ +const de = { + // this is an example Translation, just rename or delete this folder if you want + HI: 'Hallo {name}! Bitte hinterlasse einen Stern, wenn dir das Projekt gefällt: https://github.com/ivanhofer/typesafe-i18n', +} + +module.exports = de diff --git a/packages/generator/test/generated/mask-translation-text-enabled/en/index.js b/packages/generator/test/generated/mask-translation-text-enabled/en/index.js new file mode 100644 index 00000000..a3d11869 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-enabled/en/index.js @@ -0,0 +1,13 @@ +// @ts-check + +/** + * @typedef { import('../types.actual').BaseTranslation } BaseTranslation + */ + +/** @type { BaseTranslation } */ +const en = { + // TODO: your translations go here + HI: 'Hi {name:string}! Please leave a star if you like this project: https://github.com/ivanhofer/typesafe-i18n', +} + +module.exports = en diff --git a/packages/generator/test/generated/mask-translation-text-enabled/formatters-template.expected.js b/packages/generator/test/generated/mask-translation-text-enabled/formatters-template.expected.js new file mode 100644 index 00000000..1997db52 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-enabled/formatters-template.expected.js @@ -0,0 +1,20 @@ +// @ts-check + +/** + * @typedef { import('typesafe-i18n').FormattersInitializer } FormattersInitializer + * @typedef { import('./types.actual').Locales } Locales + * @typedef { import('./types.actual').Formatters } Formatters + */ + +/** + * @param { Locales } locale + * @return { Formatters } + */ +export const initFormatters = (locale) => { + /** @type { Formatters } */ + const formatters = { + // add your formatter functions here + } + + return formatters +} diff --git a/packages/generator/test/generated/mask-translation-text-enabled/types-template.expected.d.ts b/packages/generator/test/generated/mask-translation-text-enabled/types-template.expected.d.ts new file mode 100644 index 00000000..af488c20 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-enabled/types-template.expected.d.ts @@ -0,0 +1 @@ +// use this file to export your custom types; these types will be imported by './types.actual.d.ts' \ No newline at end of file diff --git a/packages/generator/test/generated/mask-translation-text-enabled/types.expected.d.ts b/packages/generator/test/generated/mask-translation-text-enabled/types.expected.d.ts new file mode 100644 index 00000000..d5b7ec12 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-enabled/types.expected.d.ts @@ -0,0 +1,41 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +/* eslint-disable */ +import type { BaseTranslation as BaseTranslationType, LocalizedString, RequiredParams } from 'typesafe-i18n' + +import type { name } from './types-template.actual' + +export type BaseTranslation = BaseTranslationType +export type BaseLocale = 'en' + +export type Locales = + | 'en' + +export type Translation = RootTranslation + +export type Translations = RootTranslation + +type RootTranslation = { + /** + * H​i​ ​{​0​} + * @param {name} 0 + */ + HELLO: RequiredParams<'0'> + /** + * W​e​l​c​o​m​e​ ​t​o​ ​{​a​p​p​}​! + * @param {unknown} app + */ + WELCOME: RequiredParams<'app'> +} + +export type TranslationFunctions = { + /** + * Hi {0} + */ + HELLO: (arg0: name) => LocalizedString + /** + * Welcome to {app}! + */ + WELCOME: (arg: { app: unknown }) => LocalizedString +} + +export type Formatters = {} diff --git a/packages/generator/test/generated/mask-translation-text-enabled/util.expected.async.js b/packages/generator/test/generated/mask-translation-text-enabled/util.expected.async.js new file mode 100644 index 00000000..e5ee2ab3 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-enabled/util.expected.async.js @@ -0,0 +1,49 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +// @ts-check +/* eslint-disable */ + +/** + * @typedef { import('./types.actual').Locales } Locales + * @typedef { import('./types.actual').Translations } Translations + */ + +import { initFormatters } from './formatters-template.actual' + +import { loadedFormatters, loadedLocales, locales } from './util.actual' + +const localeTranslationLoaders = { + en: () => import('./en'), +} + +/** + * @param { Locales } locale + * @param { Partial } dictionary + * @return { Translations } + */ +const updateDictionary = (locale, dictionary) => + loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary } + +/** + * @param { Locales } locale + * @return { Promise } + */ +export const importLocaleAsync = async (locale) => + /** @type { Translations } */ (/** @type { unknown } */ ((await localeTranslationLoaders[locale]()).default)) + +/** + * @param { Locales } locale + * @return { Promise } + */ +export const loadLocaleAsync = async (locale) => { + updateDictionary(locale, await importLocaleAsync(locale)) + loadFormatters(locale) +} + +export const loadAllLocalesAsync = () => Promise.all(locales.map(loadLocaleAsync)) + +/** + * @param { Locales } locale + * @return { void } + */ +export const loadFormatters = (locale) => + void (loadedFormatters[locale] = initFormatters(locale)) diff --git a/packages/generator/test/generated/mask-translation-text-enabled/util.expected.js b/packages/generator/test/generated/mask-translation-text-enabled/util.expected.js new file mode 100644 index 00000000..c11b64b3 --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-enabled/util.expected.js @@ -0,0 +1,69 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +// @ts-check +/* eslint-disable */ + +/** + * @typedef { import('typesafe-i18n').TranslateByString } TranslateByString + * @typedef { import('typesafe-i18n').LocaleTranslations } LocaleTranslations + * @typedef { import('typesafe-i18n').LocaleTranslationFunctions } LocaleTranslationFunctions + * @typedef { import('typesafe-i18n/detectors').LocaleDetector } LocaleDetector + * @typedef { import('./types.actual').Locales } Locales + * @typedef { import('./types.actual').Formatters } Formatters + * @typedef { import('./types.actual').Translations } Translations + * @typedef { import('./types.actual').TranslationFunctions } TranslationFunctions + */ + +import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n' + +import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors' +import { initExtendDictionary } from 'typesafe-i18n/utils' + +/** @type { Locales } */ +export const baseLocale = 'en' + +/** @type { Locales[] } */ +export const locales = [ + 'en' +] + +/** + * @param { string } locale + * @return { locale is Locales } + */ +export const isLocale = (locale) => locales.includes(/** @type { Locales } */ (locale)) + +export const loadedLocales = /** @type { Record } */ ({}) + +export const loadedFormatters = /** @type { Record } */ ({}) + +/** @type { ReturnType> } */ +export const extendDictionary = initExtendDictionary() + +/** + * @param { Locales } locale + * @return { TranslateByString } + */ +export const i18nString = (locale) => initI18nString(locale, loadedFormatters[locale]) + +/** + * @param { Locales } locale + * @return { TranslationFunctions } + */ +export const i18nObject = (locale) => + initI18nObject( + locale, + loadedLocales[locale], + loadedFormatters[locale] + ) + +/** + * @return { LocaleTranslationFunctions } + */ +export const i18n = () => + initI18n(loadedLocales, loadedFormatters) + +/** + * @param { LocaleDetector[] } detectors + * @return { Locales } + */ +export const detectLocale = (...detectors) => detectLocaleFn(baseLocale, locales, ...detectors) diff --git a/packages/generator/test/generated/mask-translation-text-enabled/util.expected.sync.js b/packages/generator/test/generated/mask-translation-text-enabled/util.expected.sync.js new file mode 100644 index 00000000..3120f3bb --- /dev/null +++ b/packages/generator/test/generated/mask-translation-text-enabled/util.expected.sync.js @@ -0,0 +1,41 @@ +// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. +// @ts-check +/* eslint-disable */ + +/** + * @typedef { import('./types.actual').Locales } Locales + * @typedef { import('./types.actual').Translations } Translations + */ + +import { initFormatters } from './formatters-template.actual' + +import { loadedFormatters, loadedLocales, locales } from './util.actual' + +import en from './en' + +const localeTranslations = { + en, +} + +/** + * @param { Locales } locale + * @return { void } + */ +export const loadLocale = (locale) => { + if (loadedLocales[locale]) return + + loadedLocales[locale] = /** @type { Translations } */ (/** @type { unknown } */ (localeTranslations[locale])) + loadFormatters(locale) +} + +/** + * @return { void } + */ +export const loadAllLocales = () => locales.forEach(loadLocale) + +/** + * @param { Locales } locale + * @return { void } + */ +export const loadFormatters = (locale) => + void (loadedFormatters[locale] = initFormatters(locale)) diff --git a/packages/generator/test/generator.test.ts b/packages/generator/test/generator.test.ts index 3c6a5bd9..66060292 100644 --- a/packages/generator/test/generator.test.ts +++ b/packages/generator/test/generator.test.ts @@ -224,6 +224,24 @@ testGeneratedOutput( { outputFormat: 'JavaScript' }, ) +testGeneratedOutput( + 'mask-translation-text-disabled', + { + HELLO: 'Hi {0:name}', + WELCOME: 'Welcome to {app}!', + }, + { outputFormat: 'JavaScript', maskTranslationText: false }, +) + +testGeneratedOutput( + 'mask-translation-text-enabled', + { + HELLO: 'Hi {0:name}', + WELCOME: 'Welcome to {app}!', + }, + { outputFormat: 'JavaScript', maskTranslationText: true }, +) + testGeneratedOutput('formatters-with-dashes', { FORMATTER: '{0|custom-formatter|and-another}' }) testGeneratedOutput('formatters-with-spaces', { FORMATTER: '{0| custom formatter | and another }' })