sanity-plugin-google-translate

4.0.94.0.10
dist/index.js.map
~dist/index.js.mapModified
+1−1
Index: package/dist/index.js.map
===================================================================
--- package/dist/index.js.map
+++ package/dist/index.js.map
@@ -1,1 +1,1 @@
-{"version":3,"file":"index.js","names":["extractLanguageFromCode","code","length","toLowerCase","languageCode","split","InputProps","ObjectMember","getLanguageFromMember","member","kind","key","split","pop","elementProps","id","htmlDecode","input","doc","DOMParser","parseFromString","documentElement","textContent","Box","Button","Text","Flex","Stack","Tooltip","useToast","useCallback","useState","MemberField","MemberFieldSet","MemberFieldError","set","unset","FieldMember","InputProps","ObjectInputProps","extractLanguageFromCode","getLanguageFromMember","htmlDecode","FieldNameLangPair","GoogleTranslateSchemaOptions","TranslationConfig","collectLanguageFields","members","allLanguageFields","cur","kind","language","name","push","fieldName","fieldLang","memberCur","fieldSet","GoogleTranslateInput","props","$","_c","renderDefault","onChange","value","t0","schemaType","options","apiKey","isTranslating","setIsTranslating","toast","t1","config","content","title","status","baseLanguage","filter","code","url","URL","searchParams","target","source","length","description","toLocaleUpperCase","translations","map","item","fetch","toString","then","_temp","res_0","res","error","message","data","forEach","t2","translatedText","decoded","catch","_temp2","Promise","all","handleTranslation","member","baseMember","find","_temp3","baseFieldValue","field","baseContent","isBaseLanguage","toUpperCase","renderInput","t3","renderField","renderItem","renderPreview","t4","member_0","key","item_0","err","console","json","definePlugin","GoogleTranslateInput","GoogleTranslateSchemaOptions","googleTranslate","name","form","components","input","props","options","schemaType","translate","jsonType","Error","renderDefault"],"sources":["../src/helpers/extractLanguageFromCode.ts","../src/helpers/getLanguageFromMember.ts","../src/helpers/htmlDecode.ts","../src/GoogleTranslateInput.tsx","../src/googleTranslate.tsx"],"sourcesContent":["export function extractLanguageFromCode(code = ``): string {\n  if (code.length <= 2) {\n    return code.toLowerCase()\n  }\n\n  const [languageCode] = code.split(/[-_]/)\n  return (languageCode ?? code).toLowerCase()\n}\n","import {type InputProps, type ObjectMember} from 'sanity'\n\nexport function getLanguageFromMember(member: InputProps | ObjectMember): string {\n  // Member is undefined\n  if (!member) {\n    return ``\n  }\n\n  // Member is an object field member\n  if ('kind' in member && member.kind === 'field') {\n    return member.key.split(`-`).pop() ?? ``\n  } else if ('kind' in member) {\n    return ``\n  }\n\n  // Member is an inputProps\n  return member?.elementProps?.id.split(`.`).pop() ?? ``\n}\n","export function htmlDecode(input: string): string {\n  const doc = new DOMParser().parseFromString(input, 'text/html')\n  return doc.documentElement.textContent ?? ``\n}\n","import {Box, Button, Text, Flex, Stack, Tooltip, useToast} from '@sanity/ui'\nimport {useCallback, useState} from 'react'\nimport {MemberField, MemberFieldSet, MemberFieldError, set, unset} from 'sanity'\nimport type {FieldMember, InputProps, ObjectInputProps} from 'sanity'\n\nimport {extractLanguageFromCode} from './helpers/extractLanguageFromCode'\nimport {getLanguageFromMember} from './helpers/getLanguageFromMember'\nimport {htmlDecode} from './helpers/htmlDecode'\nimport type {FieldNameLangPair, GoogleTranslateSchemaOptions, TranslationConfig} from './types'\n\nfunction collectLanguageFields(members: ObjectInputProps['members']): FieldNameLangPair[] {\n  const allLanguageFields: FieldNameLangPair[] = []\n\n  for (const cur of members) {\n    if (cur.kind === 'field') {\n      const language = getLanguageFromMember(cur)\n      if (language && cur.name) {\n        allLanguageFields.push({\n          fieldName: cur.name,\n          fieldLang: language,\n        })\n      }\n    } else if (cur.kind === 'fieldSet') {\n      for (const memberCur of cur.fieldSet.members) {\n        if (memberCur.kind === 'field') {\n          allLanguageFields.push({\n            fieldName: memberCur.name,\n            fieldLang: getLanguageFromMember(memberCur),\n          })\n        }\n      }\n    }\n  }\n\n  return allLanguageFields\n}\n\nexport function GoogleTranslateInput(props: ObjectInputProps) {\n  const {renderDefault, members, onChange, value} = props\n  // oxlint-disable-next-line no-unsafe-type-assertion - plugin-specific object options\n  const {apiKey} = (props.schemaType.options ?? {}) as GoogleTranslateSchemaOptions\n\n  const [isTranslating, setIsTranslating] = useState(false)\n  const toast = useToast()\n\n  const handleTranslation = useCallback(\n    (config: TranslationConfig) => {\n      if (!config?.content) {\n        return toast.push({\n          title: `No content to translate`,\n          status: `warning`,\n        })\n      }\n\n      // Get all unique language field names and codes\n      // Maybe this should be recursive, but the recommendation is only to nest 1 level deep\n      // TODO: Remove hidden/filtered-out fields as this currently will write to all fields\n      let allLanguageFields = collectLanguageFields(members)\n\n      // If this isn't a \"translate all\" operation, just target the passed-in language\n      if (config.language !== config.baseLanguage) {\n        allLanguageFields = allLanguageFields.filter((code) => code.fieldName === config.language)\n      }\n\n      setIsTranslating(true)\n\n      const url = new URL(`https://translation.googleapis.com/language/translate/v2`)\n      url.searchParams.set(`key`, apiKey ?? ``)\n      url.searchParams.set(`q`, config.content)\n\n      // Language code might be a country/language pair like en_US\n      const target = extractLanguageFromCode(config.language)\n      const source = extractLanguageFromCode(config.baseLanguage)\n      url.searchParams.set(`source`, source)\n\n      if (allLanguageFields.length === 1 && target === source) {\n        return toast.push({\n          title: `Bad language pair`,\n          status: `warning`,\n          description: `Cannot translate from \"${source.toLocaleUpperCase()}\" to \"${target.toLocaleUpperCase()}\"`,\n        })\n      }\n\n      const translations = allLanguageFields.map(async (item) => {\n        url.searchParams.set(`target`, item.fieldLang)\n\n        if (item.fieldLang === source) {\n          return null\n        }\n\n        return fetch(url.toString())\n          .then((res) => res.json())\n          .then((res) => {\n            if (res.error) {\n              toast.push({\n                title: `Error`,\n                status: `error`,\n                description: res.error.message,\n              })\n              return undefined\n            }\n\n            toast.push({\n              title: `Translation Complete`,\n              status: `success`,\n              description: `Translated from \"${source.toLocaleUpperCase()}\" to \"${item.fieldLang.toLocaleUpperCase()}\"`,\n            })\n\n            const {data} = res\n\n            if (data?.translations?.length) {\n              data.translations.forEach(({translatedText}: {translatedText: string}) => {\n                // Convert html entities returned in translation to a string\n                const decoded = htmlDecode(translatedText)\n\n                // Write translation into the correct language field\n                onChange(decoded ? set(decoded, [item.fieldName]) : unset([item.fieldName]))\n              })\n            }\n\n            return undefined\n          })\n          .catch((err) => {\n            console.error(err)\n          })\n      })\n\n      return Promise.all(translations).then(() => setIsTranslating(false))\n    },\n    [apiKey, members, onChange, toast],\n  )\n\n  const renderInput = useCallback(\n    (member: InputProps) => {\n      if (!value) {\n        return renderDefault(member)\n      }\n\n      const language = getLanguageFromMember(member)\n      const baseMember = members.find((item): item is FieldMember => item.kind === 'field')\n      const baseLanguage = baseMember ? getLanguageFromMember(baseMember) : ``\n      const baseFieldValue = baseMember?.field.value\n      const baseContent = typeof baseFieldValue === 'string' ? baseFieldValue : ``\n      const isBaseLanguage = language === baseLanguage\n\n      if (!language) {\n        return renderDefault(member)\n      }\n\n      return (\n        <Flex gap={1}>\n          <Box flex={1}>{renderDefault(member)}</Box>\n          <Tooltip\n            content={\n              <Box padding={2}>\n                {isBaseLanguage ? (\n                  <Text size={1}>\n                    Translate all fields from the \"{baseLanguage.toLocaleUpperCase()}\" content\n                  </Text>\n                ) : (\n                  <Text size={1}>\n                    Translate the \"{baseLanguage.toLocaleUpperCase()}\" field content to \"\n                    {language.toLocaleUpperCase()}\"\n                  </Text>\n                )}\n              </Box>\n            }\n            fallbackPlacements={['right', 'left']}\n            placement=\"top\"\n            portal\n          >\n            <Button\n              mode=\"ghost\"\n              disabled={isTranslating || (isBaseLanguage && !member?.value)}\n              value={language}\n              text={isBaseLanguage ? `Translate All` : `Translate ${language.toUpperCase()}`}\n              onClick={() =>\n                handleTranslation({\n                  language,\n                  baseLanguage,\n                  content: baseContent,\n                })\n              }\n            />\n          </Tooltip>\n        </Flex>\n      )\n    },\n    [handleTranslation, isTranslating, members, renderDefault, value],\n  )\n\n  return (\n    <Stack gap={4}>\n      {props.members.map((member) => {\n        switch (member.kind) {\n          case 'field':\n            return (\n              <MemberField\n                key={member.key}\n                member={member}\n                // @ts-expect-error - MemberField renderInput typings do not match custom callback\n                renderInput={renderInput}\n                renderPreview={props.renderPreview}\n                renderField={props.renderField}\n                renderItem={props.renderItem}\n              />\n            )\n          case 'fieldSet':\n            return (\n              <MemberFieldSet\n                key={member.key}\n                member={member}\n                // @ts-expect-error - MemberFieldSet renderInput typings do not match custom callback\n                renderInput={renderInput}\n                renderPreview={props.renderPreview}\n                renderField={props.renderField}\n                renderItem={props.renderItem}\n              />\n            )\n          case 'error':\n            return <MemberFieldError key={member.key} member={member} />\n          default:\n            return null\n        }\n      })}\n    </Stack>\n  )\n}\n","import {definePlugin} from 'sanity'\n\nimport {GoogleTranslateInput} from './GoogleTranslateInput'\nimport type {GoogleTranslateSchemaOptions} from './types'\n\nexport const googleTranslate = definePlugin(() => {\n  return {\n    name: 'sanity-plugin-google-translate',\n    form: {\n      components: {\n        input: (props) => {\n          // oxlint-disable-next-line no-unsafe-type-assertion - plugin-specific object options\n          const options = props.schemaType.options as GoogleTranslateSchemaOptions | undefined\n\n          if (options?.translate) {\n            if (props.schemaType.jsonType !== 'object') {\n              throw new Error(\n                `The translate option is only supported on object type fields, but got ${props.schemaType.jsonType}`,\n              )\n            }\n\n            // @ts-expect-error - form input props are wider than ObjectInputProps\n            return <GoogleTranslateInput {...props} />\n          }\n\n          return props.renderDefault(props)\n        },\n      },\n    },\n  }\n})\n"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"index.js","names":["InputProps","ObjectMember","getLanguageFromMember","member","kind","key","split","pop","elementProps","id","htmlDecode","input","doc","DOMParser","parseFromString","documentElement","textContent","Box","Button","Text","Flex","Stack","useToast","Tooltip","useCallback","useState","MemberField","MemberFieldSet","MemberFieldError","set","unset","FieldMember","InputProps","ObjectInputProps","extractLanguageFromCode","getLanguageFromMember","htmlDecode","FieldNameLangPair","GoogleTranslateSchemaOptions","TranslationConfig","collectLanguageFields","members","allLanguageFields","cur","kind","language","name","push","fieldName","fieldLang","memberCur","fieldSet","GoogleTranslateInput","props","$","_c","renderDefault","onChange","value","t0","schemaType","options","apiKey","isTranslating","setIsTranslating","toast","t1","config","content","title","status","baseLanguage","filter","code","url","URL","searchParams","target","source","length","description","toLocaleUpperCase","translations","map","item","fetch","toString","then","_temp","res_0","res","error","message","data","forEach","t2","translatedText","decoded","catch","_temp2","Promise","all","handleTranslation","member","baseMember","find","_temp3","baseFieldValue","field","baseContent","isBaseLanguage","toUpperCase","renderInput","t3","renderField","renderItem","renderPreview","t4","member_0","key","item_0","err","console","json","definePlugin","GoogleTranslateInput","GoogleTranslateSchemaOptions","googleTranslate","name","form","components","input","props","options","schemaType","translate","jsonType","Error","renderDefault"],"sources":["../src/helpers/extractLanguageFromCode.ts","../src/helpers/getLanguageFromMember.ts","../src/helpers/htmlDecode.ts","../src/GoogleTranslateInput.tsx","../src/googleTranslate.tsx"],"sourcesContent":["export function extractLanguageFromCode(code = ``): string {\n  if (code.length <= 2) {\n    return code.toLowerCase()\n  }\n\n  const [languageCode] = code.split(/[-_]/)\n  return (languageCode ?? code).toLowerCase()\n}\n","import {type InputProps, type ObjectMember} from 'sanity'\n\nexport function getLanguageFromMember(member: InputProps | ObjectMember): string {\n  // Member is undefined\n  if (!member) {\n    return ``\n  }\n\n  // Member is an object field member\n  if ('kind' in member && member.kind === 'field') {\n    return member.key.split(`-`).pop() ?? ``\n  } else if ('kind' in member) {\n    return ``\n  }\n\n  // Member is an inputProps\n  return member?.elementProps?.id.split(`.`).pop() ?? ``\n}\n","export function htmlDecode(input: string): string {\n  const doc = new DOMParser().parseFromString(input, 'text/html')\n  return doc.documentElement.textContent ?? ``\n}\n","import {Box, Button, Text, Flex, Stack} from '@sanity/ui'\nimport {useToast} from '@sanity/ui/toast'\nimport {Tooltip} from '@sanity/ui/tooltip'\nimport {useCallback, useState} from 'react'\nimport {MemberField, MemberFieldSet, MemberFieldError, set, unset} from 'sanity'\nimport type {FieldMember, InputProps, ObjectInputProps} from 'sanity'\n\nimport {extractLanguageFromCode} from './helpers/extractLanguageFromCode'\nimport {getLanguageFromMember} from './helpers/getLanguageFromMember'\nimport {htmlDecode} from './helpers/htmlDecode'\nimport type {FieldNameLangPair, GoogleTranslateSchemaOptions, TranslationConfig} from './types'\n\nfunction collectLanguageFields(members: ObjectInputProps['members']): FieldNameLangPair[] {\n  const allLanguageFields: FieldNameLangPair[] = []\n\n  for (const cur of members) {\n    if (cur.kind === 'field') {\n      const language = getLanguageFromMember(cur)\n      if (language && cur.name) {\n        allLanguageFields.push({\n          fieldName: cur.name,\n          fieldLang: language,\n        })\n      }\n    } else if (cur.kind === 'fieldSet') {\n      for (const memberCur of cur.fieldSet.members) {\n        if (memberCur.kind === 'field') {\n          allLanguageFields.push({\n            fieldName: memberCur.name,\n            fieldLang: getLanguageFromMember(memberCur),\n          })\n        }\n      }\n    }\n  }\n\n  return allLanguageFields\n}\n\nexport function GoogleTranslateInput(props: ObjectInputProps) {\n  const {renderDefault, members, onChange, value} = props\n  // oxlint-disable-next-line no-unsafe-type-assertion - plugin-specific object options\n  const {apiKey} = (props.schemaType.options ?? {}) as GoogleTranslateSchemaOptions\n\n  const [isTranslating, setIsTranslating] = useState(false)\n  const toast = useToast()\n\n  const handleTranslation = useCallback(\n    (config: TranslationConfig) => {\n      if (!config?.content) {\n        return toast.push({\n          title: `No content to translate`,\n          status: `warning`,\n        })\n      }\n\n      // Get all unique language field names and codes\n      // Maybe this should be recursive, but the recommendation is only to nest 1 level deep\n      // TODO: Remove hidden/filtered-out fields as this currently will write to all fields\n      let allLanguageFields = collectLanguageFields(members)\n\n      // If this isn't a \"translate all\" operation, just target the passed-in language\n      if (config.language !== config.baseLanguage) {\n        allLanguageFields = allLanguageFields.filter((code) => code.fieldName === config.language)\n      }\n\n      setIsTranslating(true)\n\n      const url = new URL(`https://translation.googleapis.com/language/translate/v2`)\n      url.searchParams.set(`key`, apiKey ?? ``)\n      url.searchParams.set(`q`, config.content)\n\n      // Language code might be a country/language pair like en_US\n      const target = extractLanguageFromCode(config.language)\n      const source = extractLanguageFromCode(config.baseLanguage)\n      url.searchParams.set(`source`, source)\n\n      if (allLanguageFields.length === 1 && target === source) {\n        return toast.push({\n          title: `Bad language pair`,\n          status: `warning`,\n          description: `Cannot translate from \"${source.toLocaleUpperCase()}\" to \"${target.toLocaleUpperCase()}\"`,\n        })\n      }\n\n      const translations = allLanguageFields.map(async (item) => {\n        url.searchParams.set(`target`, item.fieldLang)\n\n        if (item.fieldLang === source) {\n          return null\n        }\n\n        return fetch(url.toString())\n          .then((res) => res.json())\n          .then((res) => {\n            if (res.error) {\n              toast.push({\n                title: `Error`,\n                status: `error`,\n                description: res.error.message,\n              })\n              return undefined\n            }\n\n            toast.push({\n              title: `Translation Complete`,\n              status: `success`,\n              description: `Translated from \"${source.toLocaleUpperCase()}\" to \"${item.fieldLang.toLocaleUpperCase()}\"`,\n            })\n\n            const {data} = res\n\n            if (data?.translations?.length) {\n              data.translations.forEach(({translatedText}: {translatedText: string}) => {\n                // Convert html entities returned in translation to a string\n                const decoded = htmlDecode(translatedText)\n\n                // Write translation into the correct language field\n                onChange(decoded ? set(decoded, [item.fieldName]) : unset([item.fieldName]))\n              })\n            }\n\n            return undefined\n          })\n          .catch((err) => {\n            console.error(err)\n          })\n      })\n\n      return Promise.all(translations).then(() => setIsTranslating(false))\n    },\n    [apiKey, members, onChange, toast],\n  )\n\n  const renderInput = useCallback(\n    (member: InputProps) => {\n      if (!value) {\n        return renderDefault(member)\n      }\n\n      const language = getLanguageFromMember(member)\n      const baseMember = members.find((item): item is FieldMember => item.kind === 'field')\n      const baseLanguage = baseMember ? getLanguageFromMember(baseMember) : ``\n      const baseFieldValue = baseMember?.field.value\n      const baseContent = typeof baseFieldValue === 'string' ? baseFieldValue : ``\n      const isBaseLanguage = language === baseLanguage\n\n      if (!language) {\n        return renderDefault(member)\n      }\n\n      return (\n        <Flex gap={1}>\n          <Box flex={1}>{renderDefault(member)}</Box>\n          <Tooltip\n            content={\n              <Box padding={2}>\n                {isBaseLanguage ? (\n                  <Text size={1}>\n                    Translate all fields from the \"{baseLanguage.toLocaleUpperCase()}\" content\n                  </Text>\n                ) : (\n                  <Text size={1}>\n                    Translate the \"{baseLanguage.toLocaleUpperCase()}\" field content to \"\n                    {language.toLocaleUpperCase()}\"\n                  </Text>\n                )}\n              </Box>\n            }\n            fallbackPlacements={['right', 'left']}\n            placement=\"top\"\n            portal\n          >\n            <Button\n              mode=\"ghost\"\n              disabled={isTranslating || (isBaseLanguage && !member?.value)}\n              value={language}\n              text={isBaseLanguage ? `Translate All` : `Translate ${language.toUpperCase()}`}\n              onClick={() =>\n                handleTranslation({\n                  language,\n                  baseLanguage,\n                  content: baseContent,\n                })\n              }\n            />\n          </Tooltip>\n        </Flex>\n      )\n    },\n    [handleTranslation, isTranslating, members, renderDefault, value],\n  )\n\n  return (\n    <Stack gap={4}>\n      {props.members.map((member) => {\n        switch (member.kind) {\n          case 'field':\n            return (\n              <MemberField\n                key={member.key}\n                member={member}\n                // @ts-expect-error - MemberField renderInput typings do not match custom callback\n                renderInput={renderInput}\n                renderPreview={props.renderPreview}\n                renderField={props.renderField}\n                renderItem={props.renderItem}\n              />\n            )\n          case 'fieldSet':\n            return (\n              <MemberFieldSet\n                key={member.key}\n                member={member}\n                // @ts-expect-error - MemberFieldSet renderInput typings do not match custom callback\n                renderInput={renderInput}\n                renderPreview={props.renderPreview}\n                renderField={props.renderField}\n                renderItem={props.renderItem}\n              />\n            )\n          case 'error':\n            return <MemberFieldError key={member.key} member={member} />\n          default:\n            return null\n        }\n      })}\n    </Stack>\n  )\n}\n","import {definePlugin} from 'sanity'\n\nimport {GoogleTranslateInput} from './GoogleTranslateInput'\nimport type {GoogleTranslateSchemaOptions} from './types'\n\nexport const googleTranslate = definePlugin(() => {\n  return {\n    name: 'sanity-plugin-google-translate',\n    form: {\n      components: {\n        input: (props) => {\n          // oxlint-disable-next-line no-unsafe-type-assertion - plugin-specific object options\n          const options = props.schemaType.options as GoogleTranslateSchemaOptions | undefined\n\n          if (options?.translate) {\n            if (props.schemaType.jsonType !== 'object') {\n              throw new Error(\n                `The translate option is only supported on object type fields, but got ${props.schemaType.jsonType}`,\n              )\n            }\n\n            // @ts-expect-error - form input props are wider than ObjectInputProps\n            return <GoogleTranslateInput {...props} />\n          }\n\n          return props.renderDefault(props)\n        },\n      },\n    },\n  }\n})\n"],"mappings":"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"}
\ No newline at end of file