@translation-cms/sync 1.2.28 → 1.2.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/dist/api.d.ts +105 -0
  2. package/dist/api.d.ts.map +1 -0
  3. package/dist/api.js +208 -0
  4. package/dist/api.js.map +1 -0
  5. package/dist/bin.d.ts +25 -0
  6. package/dist/bin.d.ts.map +1 -0
  7. package/dist/bin.js +208 -0
  8. package/dist/bin.js.map +1 -0
  9. package/dist/commands/init.d.ts +10 -0
  10. package/dist/commands/init.d.ts.map +1 -0
  11. package/dist/commands/init.js +107 -0
  12. package/dist/commands/init.js.map +1 -0
  13. package/dist/commands/pull.d.ts +10 -0
  14. package/dist/commands/pull.d.ts.map +1 -0
  15. package/dist/commands/pull.js +35 -0
  16. package/dist/commands/pull.js.map +1 -0
  17. package/dist/commands/status.d.ts +15 -0
  18. package/dist/commands/status.d.ts.map +1 -0
  19. package/dist/commands/status.js +62 -0
  20. package/dist/commands/status.js.map +1 -0
  21. package/dist/commands/sync.d.ts +24 -0
  22. package/dist/commands/sync.d.ts.map +1 -0
  23. package/dist/commands/sync.js +131 -0
  24. package/dist/commands/sync.js.map +1 -0
  25. package/dist/commands/watch.d.ts +18 -0
  26. package/dist/commands/watch.d.ts.map +1 -0
  27. package/dist/commands/watch.js +71 -0
  28. package/dist/commands/watch.js.map +1 -0
  29. package/dist/config/config-internals/args.d.ts +11 -0
  30. package/dist/config/config-internals/args.d.ts.map +1 -0
  31. package/dist/config/config-internals/args.js +22 -0
  32. package/dist/config/config-internals/args.js.map +1 -0
  33. package/dist/config/config-internals/env.d.ts +10 -0
  34. package/dist/config/config-internals/env.d.ts.map +1 -0
  35. package/dist/config/config-internals/env.js +35 -0
  36. package/dist/config/config-internals/env.js.map +1 -0
  37. package/dist/config/config-internals/file.d.ts +11 -0
  38. package/dist/config/config-internals/file.d.ts.map +1 -0
  39. package/dist/config/config-internals/file.js +28 -0
  40. package/dist/config/config-internals/file.js.map +1 -0
  41. package/dist/config/config-internals/resolve.d.ts +21 -0
  42. package/dist/config/config-internals/resolve.d.ts.map +1 -0
  43. package/dist/config/config-internals/resolve.js +73 -0
  44. package/dist/config/config-internals/resolve.js.map +1 -0
  45. package/dist/config/config-internals/root.d.ts +9 -0
  46. package/dist/config/config-internals/root.d.ts.map +1 -0
  47. package/dist/config/config-internals/root.js +22 -0
  48. package/dist/config/config-internals/root.js.map +1 -0
  49. package/dist/config/config-internals/types.d.ts +91 -0
  50. package/dist/config/config-internals/types.d.ts.map +1 -0
  51. package/dist/config/config-internals/types.js +9 -0
  52. package/dist/config/config-internals/types.js.map +1 -0
  53. package/dist/config/resolve-config.d.ts +11 -0
  54. package/dist/config/resolve-config.d.ts.map +1 -0
  55. package/dist/config/resolve-config.js +10 -0
  56. package/dist/config/resolve-config.js.map +1 -0
  57. package/dist/core/api-internals/helpers.d.ts +9 -0
  58. package/dist/core/api-internals/helpers.d.ts.map +1 -0
  59. package/dist/core/api-internals/helpers.js +14 -0
  60. package/dist/core/api-internals/helpers.js.map +1 -0
  61. package/dist/core/api-internals/pull.d.ts +19 -0
  62. package/dist/core/api-internals/pull.d.ts.map +1 -0
  63. package/dist/core/api-internals/pull.js +269 -0
  64. package/dist/core/api-internals/pull.js.map +1 -0
  65. package/dist/core/api-internals/route-config.d.ts +13 -0
  66. package/dist/core/api-internals/route-config.d.ts.map +1 -0
  67. package/dist/core/api-internals/route-config.js +34 -0
  68. package/dist/core/api-internals/route-config.js.map +1 -0
  69. package/dist/core/api-internals/sync.d.ts +19 -0
  70. package/dist/core/api-internals/sync.d.ts.map +1 -0
  71. package/dist/core/api-internals/sync.js +139 -0
  72. package/dist/core/api-internals/sync.js.map +1 -0
  73. package/dist/core/api-internals/types.d.ts +33 -0
  74. package/dist/core/api-internals/types.d.ts.map +1 -0
  75. package/dist/core/api-internals/types.js +5 -0
  76. package/dist/core/api-internals/types.js.map +1 -0
  77. package/dist/core/api.d.ts +11 -0
  78. package/dist/core/api.d.ts.map +1 -0
  79. package/dist/core/api.js +11 -0
  80. package/dist/core/api.js.map +1 -0
  81. package/dist/core/cache-internals/format.d.ts +20 -0
  82. package/dist/core/cache-internals/format.d.ts.map +1 -0
  83. package/dist/core/cache-internals/format.js +33 -0
  84. package/dist/core/cache-internals/format.js.map +1 -0
  85. package/dist/core/cache-internals/params.d.ts +33 -0
  86. package/dist/core/cache-internals/params.d.ts.map +1 -0
  87. package/dist/core/cache-internals/params.js +155 -0
  88. package/dist/core/cache-internals/params.js.map +1 -0
  89. package/dist/core/cache-internals/pull.d.ts +17 -0
  90. package/dist/core/cache-internals/pull.d.ts.map +1 -0
  91. package/dist/core/cache-internals/pull.js +34 -0
  92. package/dist/core/cache-internals/pull.js.map +1 -0
  93. package/dist/core/cache-internals/sync.d.ts +29 -0
  94. package/dist/core/cache-internals/sync.d.ts.map +1 -0
  95. package/dist/core/cache-internals/sync.js +104 -0
  96. package/dist/core/cache-internals/sync.js.map +1 -0
  97. package/dist/core/cache-internals/types.d.ts +48 -0
  98. package/dist/core/cache-internals/types.d.ts.map +1 -0
  99. package/dist/core/cache-internals/types.js +2 -0
  100. package/dist/core/cache-internals/types.js.map +1 -0
  101. package/dist/core/cache.d.ts +13 -0
  102. package/dist/core/cache.d.ts.map +1 -0
  103. package/dist/core/cache.js +33 -0
  104. package/dist/core/cache.js.map +1 -0
  105. package/dist/core/scanner-internals/ast.d.ts +40 -0
  106. package/dist/core/scanner-internals/ast.d.ts.map +1 -0
  107. package/dist/core/scanner-internals/ast.js +100 -0
  108. package/dist/core/scanner-internals/ast.js.map +1 -0
  109. package/dist/core/scanner-internals/file-walker.d.ts +10 -0
  110. package/dist/core/scanner-internals/file-walker.d.ts.map +1 -0
  111. package/dist/core/scanner-internals/file-walker.js +23 -0
  112. package/dist/core/scanner-internals/file-walker.js.map +1 -0
  113. package/dist/core/scanner-internals/import-resolver.d.ts +21 -0
  114. package/dist/core/scanner-internals/import-resolver.d.ts.map +1 -0
  115. package/dist/core/scanner-internals/import-resolver.js +119 -0
  116. package/dist/core/scanner-internals/import-resolver.js.map +1 -0
  117. package/dist/core/scanner-internals/key-extractor.d.ts +20 -0
  118. package/dist/core/scanner-internals/key-extractor.d.ts.map +1 -0
  119. package/dist/core/scanner-internals/key-extractor.js +274 -0
  120. package/dist/core/scanner-internals/key-extractor.js.map +1 -0
  121. package/dist/core/scanner-internals/nav-config-scanner.d.ts +30 -0
  122. package/dist/core/scanner-internals/nav-config-scanner.d.ts.map +1 -0
  123. package/dist/core/scanner-internals/nav-config-scanner.js +170 -0
  124. package/dist/core/scanner-internals/nav-config-scanner.js.map +1 -0
  125. package/dist/core/scanner-internals/route-detector.d.ts +27 -0
  126. package/dist/core/scanner-internals/route-detector.d.ts.map +1 -0
  127. package/dist/core/scanner-internals/route-detector.js +78 -0
  128. package/dist/core/scanner-internals/route-detector.js.map +1 -0
  129. package/dist/core/scanner-internals/types.d.ts +60 -0
  130. package/dist/core/scanner-internals/types.d.ts.map +1 -0
  131. package/dist/core/scanner-internals/types.js +29 -0
  132. package/dist/core/scanner-internals/types.js.map +1 -0
  133. package/dist/core/scanner.d.ts +21 -0
  134. package/dist/core/scanner.d.ts.map +1 -0
  135. package/dist/core/scanner.js +162 -0
  136. package/dist/core/scanner.js.map +1 -0
  137. package/dist/index.d.ts +3 -0
  138. package/dist/index.d.ts.map +1 -0
  139. package/dist/index.js +3 -0
  140. package/dist/index.js.map +1 -0
  141. package/dist/next.d.ts +33 -0
  142. package/dist/next.d.ts.map +1 -0
  143. package/dist/next.js +110 -0
  144. package/dist/next.js.map +1 -0
  145. package/dist/preview/index.d.ts +39 -0
  146. package/dist/preview/index.d.ts.map +1 -0
  147. package/dist/preview/index.js +123 -0
  148. package/dist/preview/index.js.map +1 -0
  149. package/dist/preview/internals/highlight.d.ts +31 -0
  150. package/dist/preview/internals/highlight.d.ts.map +1 -0
  151. package/dist/preview/internals/highlight.js +184 -0
  152. package/dist/preview/internals/highlight.js.map +1 -0
  153. package/dist/preview/internals/interactions.d.ts +15 -0
  154. package/dist/preview/internals/interactions.d.ts.map +1 -0
  155. package/dist/preview/internals/interactions.js +38 -0
  156. package/dist/preview/internals/interactions.js.map +1 -0
  157. package/dist/preview/internals/interactive.d.ts +14 -0
  158. package/dist/preview/internals/interactive.d.ts.map +1 -0
  159. package/dist/preview/internals/interactive.js +122 -0
  160. package/dist/preview/internals/interactive.js.map +1 -0
  161. package/dist/preview/internals/locales.d.ts +9 -0
  162. package/dist/preview/internals/locales.d.ts.map +1 -0
  163. package/dist/preview/internals/locales.js +24 -0
  164. package/dist/preview/internals/locales.js.map +1 -0
  165. package/dist/preview/internals/state.d.ts +37 -0
  166. package/dist/preview/internals/state.d.ts.map +1 -0
  167. package/dist/preview/internals/state.js +74 -0
  168. package/dist/preview/internals/state.js.map +1 -0
  169. package/dist/preview/internals/styles.d.ts +8 -0
  170. package/dist/preview/internals/styles.d.ts.map +1 -0
  171. package/dist/preview/internals/styles.js +28 -0
  172. package/dist/preview/internals/styles.js.map +1 -0
  173. package/dist/preview/internals/types.d.ts +74 -0
  174. package/dist/preview/internals/types.d.ts.map +1 -0
  175. package/dist/preview/internals/types.js +5 -0
  176. package/dist/preview/internals/types.js.map +1 -0
  177. package/dist/preview/internals/ui-indicators.d.ts +15 -0
  178. package/dist/preview/internals/ui-indicators.d.ts.map +1 -0
  179. package/dist/preview/internals/ui-indicators.js +92 -0
  180. package/dist/preview/internals/ui-indicators.js.map +1 -0
  181. package/dist/scaffold/index.d.ts +3 -0
  182. package/dist/scaffold/index.d.ts.map +1 -0
  183. package/dist/scaffold/index.js +3 -0
  184. package/dist/scaffold/index.js.map +1 -0
  185. package/dist/scaffold/intenrals/scaffold.d.ts +24 -0
  186. package/dist/scaffold/intenrals/scaffold.d.ts.map +1 -0
  187. package/dist/scaffold/intenrals/scaffold.js +53 -0
  188. package/dist/scaffold/intenrals/scaffold.js.map +1 -0
  189. package/dist/scaffold/intenrals/templates.d.ts +10 -0
  190. package/dist/scaffold/intenrals/templates.d.ts.map +1 -0
  191. package/dist/scaffold/intenrals/templates.js +344 -0
  192. package/dist/scaffold/intenrals/templates.js.map +1 -0
  193. package/dist/scaffold/intenrals/types.d.ts +14 -0
  194. package/dist/scaffold/intenrals/types.d.ts.map +1 -0
  195. package/dist/scaffold/intenrals/types.js +5 -0
  196. package/dist/scaffold/intenrals/types.js.map +1 -0
  197. package/package.json +3 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/scaffold/intenrals/scaffold.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EACH,WAAW,EACX,QAAQ,EACR,SAAS,EACT,SAAS,EACT,YAAY,EACZ,gBAAgB,GACnB,MAAM,gBAAgB,CAAC;AAGxB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,YAAY,CAAC,IAAqB;IAC9C,MAAM,EAAE,OAAO,EAAE,SAAS,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC;IAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;IAEpD,qEAAqE;IACrE,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5C,MAAM,KAAK,GAA4C;QACnD,EAAE,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,EAAE;QAC5C,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE;QACtC,EAAE,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE;QACxC,EAAE,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE;QACxC,EAAE,GAAG,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,EAAE;QAC9C,EAAE,GAAG,EAAE,sBAAsB,EAAE,OAAO,EAAE,gBAAgB,EAAE;QAC1D,EAAE,GAAG,EAAE,sBAAsB,EAAE,OAAO,EAAE,gBAAgB,EAAE;KAC7D,CAAC;IAEF,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,KAAK,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACpC,gFAAgF;QAChF,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACnC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAClB,SAAS;QACb,CAAC;QACD,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAChC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * i18n file templates.
3
+ */
4
+ export declare const SETTINGS_TS = "import type { InitOptions } from 'i18next';\n\n// These imports resolve after running: sync-translations pull\nimport en from './dictionaries/en.json';\nimport type nl from './dictionaries/nl.json';\n\nexport const fallbackLng = 'nl' as const;\nexport const languages = ['nl', 'en'] as const;\nexport const defaultNS = 'common' as const;\nexport const cookieName = 'i18next';\n\nexport type Locale = (typeof languages)[number];\n\nexport const allNamespaces = Object.keys(\n en\n) as readonly (keyof typeof en)[];\n\nexport type Dicts = {\n en: typeof en;\n nl: typeof nl;\n};\n\n// Re-export for convenience\nexport type { AppNamespace, Namespace } from './types';\n\nexport function getOptions(\n lng: string = fallbackLng,\n ns: string | readonly string[] = allNamespaces\n): InitOptions {\n return {\n supportedLngs: [...languages],\n fallbackLng: lng,\n lng,\n fallbackNS: defaultNS,\n defaultNS,\n ns,\n resources: {},\n partialBundledLanguages: true,\n interpolation: {\n escapeValue: false,\n },\n };\n}\n";
5
+ export declare const TYPES_TS = "import type { allNamespaces, Dicts, languages } from './settings';\n\nexport type Locale = (typeof languages)[number];\nexport type AppNamespace = (typeof allNamespaces)[number];\nexport type Namespace = AppNamespace | readonly AppNamespace[];\n\ntype PluralSuffix = '_zero' | '_one' | '_two' | '_few' | '_many' | '_other';\n\ntype StripPluralSuffix<K extends string> =\n K extends `${infer Base}${PluralSuffix}` ? Base : K;\n\nexport type LeafPaths<T> = T extends {\n [key: string]: string | number | boolean | null | object;\n}\n ? {\n [K in keyof T & string]: T[K] extends\n | string\n | number\n | boolean\n | null\n ? K | StripPluralSuffix<K>\n : T[K] extends object\n ? `${K}.${LeafPaths<T[K]> & string}`\n : never;\n }[keyof T & string]\n : never;\n\nexport type I18nKey = {\n [Lang in keyof Dicts]: {\n [Ns in keyof Dicts[Lang]]: `${Ns & string}:${LeafPaths<Dicts[Lang][Ns]> & string}`;\n }[keyof Dicts[Lang]];\n}[keyof Dicts];\n\nexport type TranslationKey<\n Ns extends AppNamespace | readonly AppNamespace[] | undefined = undefined,\n> = Ns extends undefined\n ? I18nKey\n : Ns extends readonly (infer N extends AppNamespace)[]\n ? `${N}:${LeafPaths<Dicts['en'][N]> & string}`\n : Ns extends AppNamespace\n ? `${Ns}:${LeafPaths<Dicts['en'][Ns]> & string}`\n : never;\n\nexport type TranslationKeys<\n Ns extends AppNamespace | readonly AppNamespace[] | undefined,\n> = (key: TranslationKey<Ns>, options?: any) => string;\n";
6
+ export declare const CLIENT_TS = "'use client';\n\nimport { useCallback } from 'react';\nimport {\n initReactI18next,\n Trans,\n useTranslation as useTranslationOrg,\n type UseTranslationOptions,\n} from 'react-i18next';\n\nexport { Trans };\n\nimport i18next, { type i18n as I18nInstance, type TOptions } from 'i18next';\n\nimport en from './dictionaries/en.json';\nimport nl from './dictionaries/nl.json';\n\nimport {\n getOptions,\n type AppNamespace,\n type Locale,\n} from './settings';\nimport type { TranslationKeys } from './types';\n\nconst resources = { en, nl } as const;\n\ntype StringTOptions = Omit<TOptions, 'returnDetails' | 'returnObjects'>;\n\nlet i18nInstance: I18nInstance | null = null;\n\nexport async function initI18nClient(locale: Locale): Promise<I18nInstance> {\n if (i18nInstance) {\n if (i18nInstance.language !== locale) {\n await i18nInstance.changeLanguage(locale);\n }\n return i18nInstance;\n }\n\n i18nInstance = i18next.createInstance();\n\n await i18nInstance.use(initReactI18next).init({\n ...getOptions(locale),\n resources,\n });\n\n return i18nInstance;\n}\n\nexport function useTranslation<\n Ns extends AppNamespace | readonly AppNamespace[],\n>(ns: Ns, hookOptions?: UseTranslationOptions<undefined>) {\n const namespacesToLoad: AppNamespace[] = Array.isArray(ns)\n ? [...ns]\n : ns\n ? [ns]\n : [];\n\n const result = useTranslationOrg(namespacesToLoad, hookOptions);\n const { t: tOrg } = result;\n\n const tBase = useCallback(\n (key: string, tOptions?: StringTOptions): string => {\n return tOrg(key, {\n ...tOptions,\n returnDetails: false,\n returnObjects: false,\n } as TOptions) as string;\n },\n [tOrg]\n );\n\n const t = tBase as TranslationKeys<Ns>;\n const tDynamic = tBase;\n\n return {\n ...result,\n t,\n tDynamic,\n };\n}\n";
7
+ export declare const SERVER_TS = "import { initReactI18next } from 'react-i18next/initReactI18next';\nimport { cookies, headers } from 'next/headers';\nimport { createInstance, type TOptions } from 'i18next';\nimport resourcesToBackend from 'i18next-resources-to-backend';\n\nimport {\n cookieName,\n fallbackLng,\n getOptions,\n languages,\n type Locale,\n} from './settings';\nimport type { AppNamespace, I18nKey, TranslationKeys } from './types';\n\ntype StringTOptions = Omit<TOptions, 'returnDetails' | 'returnObjects'>;\n\nexport async function initI18n(locale: string) {\n const i18n = createInstance();\n\n await i18n\n .use(initReactI18next)\n .use(\n resourcesToBackend((language: string, namespace: string) =>\n import(`./dictionaries/${language}.json`).then(module => {\n return (module.default as Record<string, unknown>)[namespace] ?? {};\n })\n )\n )\n .init(getOptions(locale, []));\n\n return i18n;\n}\n\nexport async function getTranslation<\n Ns extends AppNamespace | readonly AppNamespace[],\n>(ns: Ns, options: { keyPrefix?: string } = {}) {\n const lng = await getLocale();\n const i18n = await initI18n(lng);\n\n const namespacesToLoad: readonly AppNamespace[] = ns\n ? Array.isArray(ns)\n ? ns\n : [ns]\n : [];\n\n if (namespacesToLoad.length > 0) {\n await i18n.loadNamespaces(namespacesToLoad);\n }\n\n const fixedT = i18n.getFixedT(lng, namespacesToLoad, options.keyPrefix);\n\n const t = (key: I18nKey, tOptions?: StringTOptions): string => {\n return fixedT(key, {\n ...tOptions,\n returnDetails: false,\n returnObjects: false,\n } as TOptions) as string;\n };\n\n return {\n t: t as TranslationKeys<Ns>,\n tDynamic: t,\n i18n,\n };\n}\n\nexport async function getLocale(): Promise<Locale> {\n const isSupportedLocale = (value: string): value is Locale =>\n (languages as readonly string[]).includes(value);\n\n try {\n const cookieStore = await cookies();\n const localeCookie = cookieStore.get(cookieName);\n if (localeCookie?.value && isSupportedLocale(localeCookie.value)) {\n return localeCookie.value;\n }\n\n const headersList = await headers();\n const acceptLanguage = headersList.get('accept-language') ?? '';\n const supported = new Set(\n (languages as readonly string[]).map(l => l.toLowerCase())\n );\n\n const preferences = acceptLanguage\n .split(',')\n .map((part): { code: string; q: number } | null => {\n const trimmed = part.trim();\n if (!trimmed) return null;\n const [tag = '', qPartRaw] = trimmed.split(';');\n const qPart = qPartRaw?.trim();\n const q = qPart?.startsWith('q=') ? Number(qPart.slice(2)) : 1;\n const code = (tag.trim().split('-')[0] ?? '').toLowerCase();\n if (!code || !Number.isFinite(q) || q <= 0 || q > 1) return null;\n return { code, q };\n })\n .filter((x): x is { code: string; q: number } => x !== null)\n .sort((a, b) => b.q - a.q);\n\n const best = preferences.find(x => supported.has(x.code));\n if (best) return best.code as Locale;\n } catch {\n // cookies()/headers() unavailable at build time\n }\n\n return fallbackLng;\n}\n";
8
+ export declare const PROVIDER_TSX = "'use client';\n\nimport { useEffect, useState } from 'react';\nimport { I18nextProvider } from 'react-i18next';\nimport type { i18n as I18nInstance } from 'i18next';\n\nimport { initI18nClient } from './client';\nimport { cookieName, type Locale } from './settings';\n\ninterface TranslationProviderProps {\n children: React.ReactNode;\n locale: Locale;\n}\n\n/**\n * Root provider for i18n. Add to your root layout:\n *\n * ```tsx\n * import { TranslationProvider } from '@/lib/i18n/provider';\n * import { getLocale } from '@/lib/i18n/server';\n *\n * export default async function RootLayout({ children }) {\n * const locale = await getLocale();\n * return (\n * <html lang={locale}>\n * <body>\n * <TranslationProvider locale={locale}>\n * {children}\n * </TranslationProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport function TranslationProvider({ children, locale }: TranslationProviderProps) {\n const [i18n, setI18n] = useState<I18nInstance | null>(null);\n\n useEffect(() => {\n initI18nClient(locale).then(setI18n);\n }, [locale]);\n\n useEffect(() => {\n if (!i18n) return;\n const handleLanguageChanged = (lng: string) => {\n document.documentElement.lang = lng;\n const expires = new Date();\n expires.setFullYear(expires.getFullYear() + 1);\n document.cookie = `${cookieName}=${encodeURIComponent(lng)}; expires=${expires.toUTCString()}; path=/; SameSite=Lax`;\n };\n handleLanguageChanged(i18n.language);\n i18n.on('languageChanged', handleLanguageChanged);\n return () => { i18n.off('languageChanged', handleLanguageChanged); };\n }, [i18n]);\n\n if (!i18n) return null;\n\n return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;\n}\n";
9
+ export declare const PLACEHOLDER_DICT: string;
10
+ //# sourceMappingURL=templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/scaffold/intenrals/templates.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,WAAW,gmCA2CvB,CAAC;AAEF,eAAO,MAAM,QAAQ,qlDA8CpB,CAAC;AAEF,eAAO,MAAM,SAAS,09DAgFrB,CAAC;AAEF,eAAO,MAAM,SAAS,k+GA0GrB,CAAC;AAEF,eAAO,MAAM,YAAY,o3DA2DxB,CAAC;AAEF,eAAO,MAAM,gBAAgB,QAA0C,CAAC"}
@@ -0,0 +1,344 @@
1
+ /**
2
+ * i18n file templates.
3
+ */
4
+ export const SETTINGS_TS = `import type { InitOptions } from 'i18next';
5
+
6
+ // These imports resolve after running: sync-translations pull
7
+ import en from './dictionaries/en.json';
8
+ import type nl from './dictionaries/nl.json';
9
+
10
+ export const fallbackLng = 'nl' as const;
11
+ export const languages = ['nl', 'en'] as const;
12
+ export const defaultNS = 'common' as const;
13
+ export const cookieName = 'i18next';
14
+
15
+ export type Locale = (typeof languages)[number];
16
+
17
+ export const allNamespaces = Object.keys(
18
+ en
19
+ ) as readonly (keyof typeof en)[];
20
+
21
+ export type Dicts = {
22
+ en: typeof en;
23
+ nl: typeof nl;
24
+ };
25
+
26
+ // Re-export for convenience
27
+ export type { AppNamespace, Namespace } from './types';
28
+
29
+ export function getOptions(
30
+ lng: string = fallbackLng,
31
+ ns: string | readonly string[] = allNamespaces
32
+ ): InitOptions {
33
+ return {
34
+ supportedLngs: [...languages],
35
+ fallbackLng: lng,
36
+ lng,
37
+ fallbackNS: defaultNS,
38
+ defaultNS,
39
+ ns,
40
+ resources: {},
41
+ partialBundledLanguages: true,
42
+ interpolation: {
43
+ escapeValue: false,
44
+ },
45
+ };
46
+ }
47
+ `;
48
+ export const TYPES_TS = `import type { allNamespaces, Dicts, languages } from './settings';
49
+
50
+ export type Locale = (typeof languages)[number];
51
+ export type AppNamespace = (typeof allNamespaces)[number];
52
+ export type Namespace = AppNamespace | readonly AppNamespace[];
53
+
54
+ type PluralSuffix = '_zero' | '_one' | '_two' | '_few' | '_many' | '_other';
55
+
56
+ type StripPluralSuffix<K extends string> =
57
+ K extends \`\${infer Base}\${PluralSuffix}\` ? Base : K;
58
+
59
+ export type LeafPaths<T> = T extends {
60
+ [key: string]: string | number | boolean | null | object;
61
+ }
62
+ ? {
63
+ [K in keyof T & string]: T[K] extends
64
+ | string
65
+ | number
66
+ | boolean
67
+ | null
68
+ ? K | StripPluralSuffix<K>
69
+ : T[K] extends object
70
+ ? \`\${K}.\${LeafPaths<T[K]> & string}\`
71
+ : never;
72
+ }[keyof T & string]
73
+ : never;
74
+
75
+ export type I18nKey = {
76
+ [Lang in keyof Dicts]: {
77
+ [Ns in keyof Dicts[Lang]]: \`\${Ns & string}:\${LeafPaths<Dicts[Lang][Ns]> & string}\`;
78
+ }[keyof Dicts[Lang]];
79
+ }[keyof Dicts];
80
+
81
+ export type TranslationKey<
82
+ Ns extends AppNamespace | readonly AppNamespace[] | undefined = undefined,
83
+ > = Ns extends undefined
84
+ ? I18nKey
85
+ : Ns extends readonly (infer N extends AppNamespace)[]
86
+ ? \`\${N}:\${LeafPaths<Dicts['en'][N]> & string}\`
87
+ : Ns extends AppNamespace
88
+ ? \`\${Ns}:\${LeafPaths<Dicts['en'][Ns]> & string}\`
89
+ : never;
90
+
91
+ export type TranslationKeys<
92
+ Ns extends AppNamespace | readonly AppNamespace[] | undefined,
93
+ > = (key: TranslationKey<Ns>, options?: any) => string;
94
+ `;
95
+ export const CLIENT_TS = `'use client';
96
+
97
+ import { useCallback } from 'react';
98
+ import {
99
+ initReactI18next,
100
+ Trans,
101
+ useTranslation as useTranslationOrg,
102
+ type UseTranslationOptions,
103
+ } from 'react-i18next';
104
+
105
+ export { Trans };
106
+
107
+ import i18next, { type i18n as I18nInstance, type TOptions } from 'i18next';
108
+
109
+ import en from './dictionaries/en.json';
110
+ import nl from './dictionaries/nl.json';
111
+
112
+ import {
113
+ getOptions,
114
+ type AppNamespace,
115
+ type Locale,
116
+ } from './settings';
117
+ import type { TranslationKeys } from './types';
118
+
119
+ const resources = { en, nl } as const;
120
+
121
+ type StringTOptions = Omit<TOptions, 'returnDetails' | 'returnObjects'>;
122
+
123
+ let i18nInstance: I18nInstance | null = null;
124
+
125
+ export async function initI18nClient(locale: Locale): Promise<I18nInstance> {
126
+ if (i18nInstance) {
127
+ if (i18nInstance.language !== locale) {
128
+ await i18nInstance.changeLanguage(locale);
129
+ }
130
+ return i18nInstance;
131
+ }
132
+
133
+ i18nInstance = i18next.createInstance();
134
+
135
+ await i18nInstance.use(initReactI18next).init({
136
+ ...getOptions(locale),
137
+ resources,
138
+ });
139
+
140
+ return i18nInstance;
141
+ }
142
+
143
+ export function useTranslation<
144
+ Ns extends AppNamespace | readonly AppNamespace[],
145
+ >(ns: Ns, hookOptions?: UseTranslationOptions<undefined>) {
146
+ const namespacesToLoad: AppNamespace[] = Array.isArray(ns)
147
+ ? [...ns]
148
+ : ns
149
+ ? [ns]
150
+ : [];
151
+
152
+ const result = useTranslationOrg(namespacesToLoad, hookOptions);
153
+ const { t: tOrg } = result;
154
+
155
+ const tBase = useCallback(
156
+ (key: string, tOptions?: StringTOptions): string => {
157
+ return tOrg(key, {
158
+ ...tOptions,
159
+ returnDetails: false,
160
+ returnObjects: false,
161
+ } as TOptions) as string;
162
+ },
163
+ [tOrg]
164
+ );
165
+
166
+ const t = tBase as TranslationKeys<Ns>;
167
+ const tDynamic = tBase;
168
+
169
+ return {
170
+ ...result,
171
+ t,
172
+ tDynamic,
173
+ };
174
+ }
175
+ `;
176
+ export const SERVER_TS = `import { initReactI18next } from 'react-i18next/initReactI18next';
177
+ import { cookies, headers } from 'next/headers';
178
+ import { createInstance, type TOptions } from 'i18next';
179
+ import resourcesToBackend from 'i18next-resources-to-backend';
180
+
181
+ import {
182
+ cookieName,
183
+ fallbackLng,
184
+ getOptions,
185
+ languages,
186
+ type Locale,
187
+ } from './settings';
188
+ import type { AppNamespace, I18nKey, TranslationKeys } from './types';
189
+
190
+ type StringTOptions = Omit<TOptions, 'returnDetails' | 'returnObjects'>;
191
+
192
+ export async function initI18n(locale: string) {
193
+ const i18n = createInstance();
194
+
195
+ await i18n
196
+ .use(initReactI18next)
197
+ .use(
198
+ resourcesToBackend((language: string, namespace: string) =>
199
+ import(\`./dictionaries/\${language}.json\`).then(module => {
200
+ return (module.default as Record<string, unknown>)[namespace] ?? {};
201
+ })
202
+ )
203
+ )
204
+ .init(getOptions(locale, []));
205
+
206
+ return i18n;
207
+ }
208
+
209
+ export async function getTranslation<
210
+ Ns extends AppNamespace | readonly AppNamespace[],
211
+ >(ns: Ns, options: { keyPrefix?: string } = {}) {
212
+ const lng = await getLocale();
213
+ const i18n = await initI18n(lng);
214
+
215
+ const namespacesToLoad: readonly AppNamespace[] = ns
216
+ ? Array.isArray(ns)
217
+ ? ns
218
+ : [ns]
219
+ : [];
220
+
221
+ if (namespacesToLoad.length > 0) {
222
+ await i18n.loadNamespaces(namespacesToLoad);
223
+ }
224
+
225
+ const fixedT = i18n.getFixedT(lng, namespacesToLoad, options.keyPrefix);
226
+
227
+ const t = (key: I18nKey, tOptions?: StringTOptions): string => {
228
+ return fixedT(key, {
229
+ ...tOptions,
230
+ returnDetails: false,
231
+ returnObjects: false,
232
+ } as TOptions) as string;
233
+ };
234
+
235
+ return {
236
+ t: t as TranslationKeys<Ns>,
237
+ tDynamic: t,
238
+ i18n,
239
+ };
240
+ }
241
+
242
+ export async function getLocale(): Promise<Locale> {
243
+ const isSupportedLocale = (value: string): value is Locale =>
244
+ (languages as readonly string[]).includes(value);
245
+
246
+ try {
247
+ const cookieStore = await cookies();
248
+ const localeCookie = cookieStore.get(cookieName);
249
+ if (localeCookie?.value && isSupportedLocale(localeCookie.value)) {
250
+ return localeCookie.value;
251
+ }
252
+
253
+ const headersList = await headers();
254
+ const acceptLanguage = headersList.get('accept-language') ?? '';
255
+ const supported = new Set(
256
+ (languages as readonly string[]).map(l => l.toLowerCase())
257
+ );
258
+
259
+ const preferences = acceptLanguage
260
+ .split(',')
261
+ .map((part): { code: string; q: number } | null => {
262
+ const trimmed = part.trim();
263
+ if (!trimmed) return null;
264
+ const [tag = '', qPartRaw] = trimmed.split(';');
265
+ const qPart = qPartRaw?.trim();
266
+ const q = qPart?.startsWith('q=') ? Number(qPart.slice(2)) : 1;
267
+ const code = (tag.trim().split('-')[0] ?? '').toLowerCase();
268
+ if (!code || !Number.isFinite(q) || q <= 0 || q > 1) return null;
269
+ return { code, q };
270
+ })
271
+ .filter((x): x is { code: string; q: number } => x !== null)
272
+ .sort((a, b) => b.q - a.q);
273
+
274
+ const best = preferences.find(x => supported.has(x.code));
275
+ if (best) return best.code as Locale;
276
+ } catch {
277
+ // cookies()/headers() unavailable at build time
278
+ }
279
+
280
+ return fallbackLng;
281
+ }
282
+ `;
283
+ export const PROVIDER_TSX = `'use client';
284
+
285
+ import { useEffect, useState } from 'react';
286
+ import { I18nextProvider } from 'react-i18next';
287
+ import type { i18n as I18nInstance } from 'i18next';
288
+
289
+ import { initI18nClient } from './client';
290
+ import { cookieName, type Locale } from './settings';
291
+
292
+ interface TranslationProviderProps {
293
+ children: React.ReactNode;
294
+ locale: Locale;
295
+ }
296
+
297
+ /**
298
+ * Root provider for i18n. Add to your root layout:
299
+ *
300
+ * \`\`\`tsx
301
+ * import { TranslationProvider } from '@/lib/i18n/provider';
302
+ * import { getLocale } from '@/lib/i18n/server';
303
+ *
304
+ * export default async function RootLayout({ children }) {
305
+ * const locale = await getLocale();
306
+ * return (
307
+ * <html lang={locale}>
308
+ * <body>
309
+ * <TranslationProvider locale={locale}>
310
+ * {children}
311
+ * </TranslationProvider>
312
+ * </body>
313
+ * </html>
314
+ * );
315
+ * }
316
+ * \`\`\`
317
+ */
318
+ export function TranslationProvider({ children, locale }: TranslationProviderProps) {
319
+ const [i18n, setI18n] = useState<I18nInstance | null>(null);
320
+
321
+ useEffect(() => {
322
+ initI18nClient(locale).then(setI18n);
323
+ }, [locale]);
324
+
325
+ useEffect(() => {
326
+ if (!i18n) return;
327
+ const handleLanguageChanged = (lng: string) => {
328
+ document.documentElement.lang = lng;
329
+ const expires = new Date();
330
+ expires.setFullYear(expires.getFullYear() + 1);
331
+ document.cookie = \`\${cookieName}=\${encodeURIComponent(lng)}; expires=\${expires.toUTCString()}; path=/; SameSite=Lax\`;
332
+ };
333
+ handleLanguageChanged(i18n.language);
334
+ i18n.on('languageChanged', handleLanguageChanged);
335
+ return () => { i18n.off('languageChanged', handleLanguageChanged); };
336
+ }, [i18n]);
337
+
338
+ if (!i18n) return null;
339
+
340
+ return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>;
341
+ }
342
+ `;
343
+ export const PLACEHOLDER_DICT = JSON.stringify({ common: {} }, null, 4);
344
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/scaffold/intenrals/templates.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2C1B,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8CvB,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgFxB,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0GxB,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2D3B,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Types for i18n scaffolding.
3
+ */
4
+ export interface ScaffoldOptions {
5
+ /** Absolute path to the i18n directory, e.g. /project/src/lib/i18n */
6
+ i18nDir: string;
7
+ /** Whether to overwrite existing files */
8
+ overwrite?: boolean;
9
+ }
10
+ export interface ScaffoldResult {
11
+ written: string[];
12
+ skipped: string[];
13
+ }
14
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/scaffold/intenrals/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,eAAe;IAC5B,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACrB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Types for i18n scaffolding.
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/scaffold/intenrals/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@translation-cms/sync",
3
- "version": "1.2.28",
3
+ "version": "1.2.30",
4
4
  "description": "Scan translation keys in your codebase and sync them to the Translations CMS",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -33,10 +33,10 @@
33
33
  "next.d.ts"
34
34
  ],
35
35
  "scripts": {
36
- "build": "tsc && tsc-alias",
36
+ "build": "rm -rf dist tsconfig.tsbuildinfo && tsc && tsc-alias",
37
37
  "dev": "tsc --watch",
38
38
  "typecheck": "tsc --noEmit",
39
- "release": "rm -rf dist && pnpm build && npm version patch && npm publish"
39
+ "release": "pnpm build && npm version patch && npm publish"
40
40
  },
41
41
  "keywords": [
42
42
  "translations",