[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/messages/ko.json from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/catalog-translation.action-buttons.save": "저장",
  "admin/catalog-translation.action-buttons.cancel": "취소",
  "admin/catalog-translation.action-buttons.edit": "편집",
  "admin/catalog-translation.entry-type.collection": "컬렉션",
  "admin/catalog-translation.errors.not-found-header": "{항목}을(를) 찾을 수 없습니다.",
  "admin/catalog-translation.errors.not-found-message": "{entry} ID {entryId} 을(를) 찾을 수 없습니다.",
  "admin/catalog-translation.errors.error-header": "{entry} 정보를 가져오는 동안 오류가 발생했습니다.",
  "admin/catalog-translation.errors.error-message": "검색한 ${entry} 정보를 가져오는 동안 오류가 발생했습니다. 다시 시도하세요.",
  "admin/catalog-translation.navigation.label": "카탈로그 번역",
  "admin/catalog-translation.navigation.search.kws": "카탈로그, 번역",
  "admin/catalog-translation.product.header": "제품 번역",
  "admin/catalog-translation.category.header": "카테고리 번역",
  "admin/catalog-translation.brand.header": "브랜드 번역",
  "admin/catalog-translation.sku.header": "SKU 번역",
  "admin/catalog-translation.specifications.header": "사양 번역",
  "admin/catalog-translation.collections.header": "컬렉션 번역",
  "admin/catalog-translation.collections.search-label": "컬렉션 ID",
  "admin/catalog-translation.collections.block-header": "{selectedLocale}에 대한 컬렉션 번역",
  "admin/catalog-translation.form-fields.name": "이름",
  "admin/catalog-translation.navigation.category": "카테고리",
  "admin/catalog-translation.navigation.product": "제품",
  "admin/catalog-translation.navigation.sku": "SKU",
  "admin/catalog-translation.navigation.brand": "브랜드",
  "admin/catalog-translation.navigation.specifications": "사양",
  "admin/catalog-translation.navigation.collections": "컬렉션",
  "admin/catalog-translation.export.modal.missing-category-id": "카테고리 ID를 선택하세요.",
  "admin/catalog-translation.export.modal.header": "{selectedLocale}에 대한 {entry} 데이터 내보내기",
  "admin/catalog-translation.export.modal.header-product": "제품",
  "admin/catalog-translation.export.modal.header-sku": "SKU",
  "admin/catalog-translation.export.modal.export-tab": "내보내기",
  "admin/catalog-translation.export.modal.cancelation": "취소",
  "admin/catalog-translation.export.modal.confirmation-products": "제품 내보내기",
  "admin/catalog-translation.export.modal.confirmation-skus": "SKU 내보내기",
  "admin/catalog-translation.export.modal.search-header": "카테고리 선택",
  "admin/catalog-translation.export.modal.category-search-placeholder": "카테고리 이름 또는 ID를 입력하세요.",
  "admin/catalog-translation.export.modal.error-exporting": "내보내는 중에 오류가 발생했습니다. 몇 분 후에 다시 시도하세요.",
  "admin/catalog-translation.export.modal.see-files-tab": "파일 보기",
  "admin/catalog-translation.export.modal.long-process.warning": "번역하는 데 시간이 걸릴 수 있습니다. 페이지를 종료하고 나중에 확인할 수 있습니다.",
  "admin/catalog-translation.export.modal.table-header.catId": "카테고리 ID",
  "admin/catalog-translation.export.modal.table-header.locale": "로케일",
  "admin/catalog-translation.export.modal.table-header.requested.by": "요청자",
  "admin/catalog-translation.export.modal.table-header.requested.at": "요청 시간",
  "admin/catalog-translation.export.modal.table-header.download": "다운로드",
  "admin/catalog-translation.export.modal.download-list.error": "파일 생성 중 오류",
  "admin/catalog-translation.export.modal.download-list.download-btn": "다운로드",
  "admin/catalog-translation.import.modal.cancelation": "취소",
  "admin/catalog-translation.import.modal.confirmation": "번역 전송",
  "admin/catalog-translation.import.modal.header.collections": "{selectedLocale}에 대한 컬렉션 번역 가져오기",
  "admin/catalog-translation.import.modal.import-tab": "불러오기",
  "admin/catalog-translation.import.modal.request-tab": "요청",
  "admin/catalog-translation.import.modal.download-button": "xlsx 모델 다운로드",
  "admin/catalog-translation.import.modal.dropzone": "여기에 XLS를 놓거나 ",
  "admin/catalog-translation.import.modal.dropzone-choose-file": "파일을 선택하세요",
  "admin/catalog-translation.import.modal.total-entries": "총 항목",
  "admin/catalog-translation.import.modal.total-warnings": "경고",
  "admin/catalog-translation.import.modal.total-errors": "오류. 항목은 무시됩니다",
  "admin/catalog-translation.import.modal.error-uploading": "번역을 업로드하는 중에 오류가 발생했습니다.",
  "admin/catalog-translation.import.modal.table-header.locale": "로케일",
  "admin/catalog-translation.import.modal.table-header.translated-by": "번역자:",
  "admin/catalog-translation.import.modal.table-header.created-at": "생성일",
  "admin/catalog-translation.import.modal.table-header.progress": "진행",
  "admin/catalog-translation.import.modal.table-header.total-translated": "% 번역됨",
  "admin/catalog-translation.import.modal.status-list.error": "오류",
  "admin/catalog-translation.import.warning-and-error": "/",
  "admin/catalog-translation.import.table-header.line": "라인",
  "admin/catalog-translation.import.table-header.missing-fields": "누락된 필드",
  "admin/catalog-translation.export.modal.title-brand": "브랜드 데이터 내보내기",
  "admin/catalog-translation.export.modal.confirmation-brands": "브랜드 내보내기",
  "admin/catalog-translation.export.modal.export-active-brands": "활성 브랜드만 내보내기",
  "admin/catalog-translation.import.modal.entry-name-header": "{selectedLocale}에 대한 {entryName} 번역 가져오기",
  "admin/catalog-translation.export.modal.specification-header": "{selectedLocale}에 대한 사양 번역 내보내기",
  "admin/catalog-translation.export.modal.download-button": "xlsx 모델 다운로드",
  "admin/catalog-translation.export.modal.dropzone": "여기에 XLS를 놓거나 ",
  "admin/catalog-translation.export.modal.dropzone-choose-file": "파일을 선택하세요",
  "admin/catalog-translation.export.modal.total-entries": "총 항목",
  "admin/catalog-translation.export.modal.total-warnings": "경고",
  "admin/catalog-translation.export.modal.total-errors": "오류. 항목은 무시됩니다",
  "admin/catalog-translation.export.modal.error-uploading": "번역을 업로드하는 중에 오류가 발생했습니다.",
  "admin/catalog-translation.export.modal.confirmation": "번역 내보내기"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/messages/th.json from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/catalog-translation.action-buttons.save": "บันทึก",
  "admin/catalog-translation.action-buttons.cancel": "ยกเลิก",
  "admin/catalog-translation.action-buttons.edit": "แก้ไข",
  "admin/catalog-translation.entry-type.collection": "คอลเล็กชัน",
  "admin/catalog-translation.errors.not-found-header": "ไม่พบ {entry}",
  "admin/catalog-translation.errors.not-found-message": "ไม่พบรหัส{entry} {entryId}",
  "admin/catalog-translation.errors.error-header": "มีข้อผิดพลาดการเรียกข้อมูล {entry}",
  "admin/catalog-translation.errors.error-message": "มีข้อผิดพลาดในการเรียกข้อมูล ${entry} ที่คุณค้นหา โปรดลองอีกครั้ง",
  "admin/catalog-translation.navigation.label": "การแปลแคตาล็อก",
  "admin/catalog-translation.navigation.search.kws": "แคตาล็อก, การแปล",
  "admin/catalog-translation.product.header": "การแปลผลิตภัณฑ์",
  "admin/catalog-translation.category.header": "การแปลหมวดหมู่",
  "admin/catalog-translation.brand.header": "การแปลแบรนด์",
  "admin/catalog-translation.sku.header": "การแปล SKU",
  "admin/catalog-translation.specifications.header": "การแปลข้อมูลจำเพาะ",
  "admin/catalog-translation.collections.header": "การแปลคอลเล็กชัน",
  "admin/catalog-translation.collections.search-label": "รหัสคอลเล็กชัน",
  "admin/catalog-translation.collections.block-header": "การแปลคอลเล็กชันเป็นภาษา {selectedLocale}",
  "admin/catalog-translation.form-fields.name": "ชื่อ",
  "admin/catalog-translation.navigation.category": "หมวดหมู่",
  "admin/catalog-translation.navigation.product": "ผลิตภัณฑ์",
  "admin/catalog-translation.navigation.sku": "SKU",
  "admin/catalog-translation.navigation.brand": "แบรนด์",
  "admin/catalog-translation.navigation.specifications": "ข้อมูลจำเพาะ",
  "admin/catalog-translation.navigation.collections": "คอลเล็กชัน",
  "admin/catalog-translation.export.modal.missing-category-id": "โปรดเลือกรหัสหมวดหมู่",
  "admin/catalog-translation.export.modal.header": "ส่งออกข้อมูล {entry} สำหรับภาษา {selectedLocale}",
  "admin/catalog-translation.export.modal.header-product": "ผลิตภัณฑ์",
  "admin/catalog-translation.export.modal.header-sku": "SKU",
  "admin/catalog-translation.export.modal.export-tab": "ส่งออก",
  "admin/catalog-translation.export.modal.cancelation": "ยกเลิก",
  "admin/catalog-translation.export.modal.confirmation-products": "ส่งออกผลิตภัณฑ์",
  "admin/catalog-translation.export.modal.confirmation-skus": "ส่งออก SKU",
  "admin/catalog-translation.export.modal.search-header": "เลือกหมวดหมู่",
  "admin/catalog-translation.export.modal.category-search-placeholder": "กรอกชื่อหรือรหัสหมวดหมู่",
  "admin/catalog-translation.export.modal.error-exporting": "มีข้อผิดพลาดในการส่งออก โปรดลองอีกครั้งในอีกสองสามนาที",
  "admin/catalog-translation.export.modal.see-files-tab": "ดูไฟล์",
  "admin/catalog-translation.export.modal.long-process.warning": "ขั้นตอนแปลอาจใช้เวลาสักพัก คุณสามารถออกจากหน้านี้และตรวจสอบดูในภายหลัง",
  "admin/catalog-translation.export.modal.table-header.catId": "รหัสหมวดหมู่",
  "admin/catalog-translation.export.modal.table-header.locale": "ระบบภาษา",
  "admin/catalog-translation.export.modal.table-header.requested.by": "ขอโดย",
  "admin/catalog-translation.export.modal.table-header.requested.at": "ขอเมื่อ",
  "admin/catalog-translation.export.modal.table-header.download": "ดาวน์โหลด",
  "admin/catalog-translation.export.modal.download-list.error": "มีข้อผิดพลาดการสร้างไฟล์",
  "admin/catalog-translation.export.modal.download-list.download-btn": "ดาวน์โหลด",
  "admin/catalog-translation.import.modal.cancelation": "ยกเลิก",
  "admin/catalog-translation.import.modal.confirmation": "ส่งคำแปล",
  "admin/catalog-translation.import.modal.header.collections": "นำเข้าคำแปลคอลเล็กชันสำหรับภาษา {selectedLocale}",
  "admin/catalog-translation.import.modal.import-tab": "นำเข้า",
  "admin/catalog-translation.import.modal.request-tab": "คำขอ",
  "admin/catalog-translation.import.modal.download-button": "ดาสวน์โหลดแบบจำลอง xlsx",
  "admin/catalog-translation.import.modal.dropzone": "วาง XLSX ของคุณที่นี่หรือ ",
  "admin/catalog-translation.import.modal.dropzone-choose-file": "เลือกไฟล์",
  "admin/catalog-translation.import.modal.total-entries": "ยอดรวมรายการ",
  "admin/catalog-translation.import.modal.total-warnings": "คำเตือน",
  "admin/catalog-translation.import.modal.total-errors": "มีข้อผิดพลาด รายการเหล่านี้จะถูกทิ้ง",
  "admin/catalog-translation.import.modal.error-uploading": "มีข้อผิดพลาดเมื่ออัปโหลดการแปล",
  "admin/catalog-translation.import.modal.table-header.locale": "ระบบภาษา",
  "admin/catalog-translation.import.modal.table-header.translated-by": "แปลโดย",
  "admin/catalog-translation.import.modal.table-header.created-at": "สร้างที่",
  "admin/catalog-translation.import.modal.table-header.progress": "ความคืบหน้า",
  "admin/catalog-translation.import.modal.table-header.total-translated": "% ที่แปลแล้ว",
  "admin/catalog-translation.import.modal.status-list.error": "ข้อผิดพลาด",
  "admin/catalog-translation.import.warning-and-error": "ของ",
  "admin/catalog-translation.import.table-header.line": "บรรทัด",
  "admin/catalog-translation.import.table-header.missing-fields": "ฟิลด์ที่ขาดหาย",
  "admin/catalog-translation.export.modal.title-brand": "ส่งออกแบรนด์สำหรับ",
  "admin/catalog-translation.export.modal.confirmation-brands": "ส่งออกแบรนด์",
  "admin/catalog-translation.export.modal.export-active-brands": "ส่งออกเฉพาะแบรนด์ที่ใช้งานอยู่",
  "admin/catalog-translation.import.modal.entry-name-header": "นำเข้าคำแปล {entryName} สำหรับภาษา {selectedLocale}",
  "admin/catalog-translation.export.modal.specification-header": "ส่งออกคำแปลข้อมูลจำเพาะสำหรับภาษา {selectedLocale}",
  "admin/catalog-translation.export.modal.download-button": "ดาวน์โหลดแบบจำลอง xlsx",
  "admin/catalog-translation.export.modal.dropzone": "วาง XLSX ของคุณที่นี่หรือ ",
  "admin/catalog-translation.export.modal.dropzone-choose-file": "เลือกไฟล์",
  "admin/catalog-translation.export.modal.total-entries": "ยอดรวมรายการ",
  "admin/catalog-translation.export.modal.total-warnings": "คำเตือน",
  "admin/catalog-translation.export.modal.total-errors": "มีข้อผิดพลาด รายการจะถูกทิ้ง",
  "admin/catalog-translation.export.modal.error-uploading": "ผิดพลาดเมื่ออัปโหลดการแปล",
  "admin/catalog-translation.export.modal.confirmation": "ส่งออกคำแปล"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/messages/es.json from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin/catalog-translation.action-buttons.save": "Guardar",
  "admin/catalog-translation.action-buttons.cancel": "Cancelar",
  "admin/catalog-translation.action-buttons.edit": "Editar",
  "admin/catalog-translation.entry-type.collection": "Colección",
  "admin/catalog-translation.errors.not-found-header": "No se encontró {entry}",
  "admin/catalog-translation.errors.not-found-message": "No se encontró {entry} de ID {entryId}",
  "admin/catalog-translation.errors.error-header": "Error al obtener la información de {entry}",
  "admin/catalog-translation.errors.error-message": "Se produjo un error al obtener la información de ${entry} que buscaste. Por favor, inténtalo de nuevo.",
  "admin/catalog-translation.navigation.label": "Traducción de catálogos",
  "admin/catalog-translation.navigation.search.kws": "catálogo, traducción",
  "admin/catalog-translation.product.header": "Traducción de productos",
  "admin/catalog-translation.category.header": "Traducción de categorías",
  "admin/catalog-translation.brand.header": "Traducción de marcas",
  "admin/catalog-translation.sku.header": "Traducción de SKU",
  "admin/catalog-translation.specifications.header": "Traducción de especificaciones",
  "admin/catalog-translation.collections.header": "Traducción de colecciones",
  "admin/catalog-translation.collections.search-label": "ID de la colección",
  "admin/catalog-translation.collections.block-header": "Colección traducida al {selectedLocale}",
  "admin/catalog-translation.form-fields.name": "Nombre",
  "admin/catalog-translation.navigation.category": "Categoría",
  "admin/catalog-translation.navigation.product": "Producto",
  "admin/catalog-translation.navigation.sku": "SKU",
  "admin/catalog-translation.navigation.brand": "Marcas",
  "admin/catalog-translation.navigation.specifications": "Especificaciones",
  "admin/catalog-translation.navigation.collections": "Colecciones",
  "admin/catalog-translation.export.modal.missing-category-id": "Por favor, selecciona un ID de categoría",
  "admin/catalog-translation.export.modal.header": "Exportar datos de {entry} a {selectedLocale}",
  "admin/catalog-translation.export.modal.header-product": "producto",
  "admin/catalog-translation.export.modal.header-sku": "SKU",
  "admin/catalog-translation.export.modal.export-tab": "Exportar",
  "admin/catalog-translation.export.modal.cancelation": "Cancelar",
  "admin/catalog-translation.export.modal.confirmation-products": "Exportar productos",
  "admin/catalog-translation.export.modal.confirmation-skus": "Exportar SKUs",
  "admin/catalog-translation.export.modal.search-header": "Selecciona una categoría",
  "admin/catalog-translation.export.modal.category-search-placeholder": "Ingresa el nombre o ID de categoría",
  "admin/catalog-translation.export.modal.error-exporting": "Se produjo un error al exportar. Por favor, inténtalo de nuevo en unos minutos.",
  "admin/catalog-translation.export.modal.see-files-tab": "Ver archivos",
  "admin/catalog-translation.export.modal.long-process.warning": "El proceso de traducción puede tardar. Puedes salir de la página y comprobarla más tarde.",
  "admin/catalog-translation.export.modal.table-header.catId": "ID de categoría",
  "admin/catalog-translation.export.modal.table-header.locale": "Región",
  "admin/catalog-translation.export.modal.table-header.requested.by": "Solicitado por",
  "admin/catalog-translation.export.modal.table-header.requested.at": "Solicitado el",
  "admin/catalog-translation.export.modal.table-header.download": "Descargar",
  "admin/catalog-translation.export.modal.download-list.error": "Error al crear el archivo",
  "admin/catalog-translation.export.modal.download-list.download-btn": "Descargar",
  "admin/catalog-translation.import.modal.cancelation": "Cancelar",
  "admin/catalog-translation.import.modal.confirmation": "Enviar traducciones",
  "admin/catalog-translation.import.modal.header.collections": "Importar traducciones de colecciones de {selectedLocale}",
  "admin/catalog-translation.import.modal.import-tab": "Importar",
  "admin/catalog-translation.import.modal.request-tab": "Solicitudes",
  "admin/catalog-translation.import.modal.download-button": "Descargar plantilla xlsx",
  "admin/catalog-translation.import.modal.dropzone": "Suelta aquí tu XLSX o ",
  "admin/catalog-translation.import.modal.dropzone-choose-file": "selecciona un archivo",
  "admin/catalog-translation.import.modal.total-entries": "nº total de entradas",
  "admin/catalog-translation.import.modal.total-warnings": "alertas",
  "admin/catalog-translation.import.modal.total-errors": "errores. Las entradas serán ignoradas.",
  "admin/catalog-translation.import.modal.error-uploading": "Error al cargar las traducciones",
  "admin/catalog-translation.import.modal.table-header.locale": "Región",
  "admin/catalog-translation.import.modal.table-header.translated-by": "Traducido por",
  "admin/catalog-translation.import.modal.table-header.created-at": "Creado el",
  "admin/catalog-translation.import.modal.table-header.progress": "Progreso",
  "admin/catalog-translation.import.modal.table-header.total-translated": "% traducido",
  "admin/catalog-translation.import.modal.status-list.error": "Error",
  "admin/catalog-translation.import.warning-and-error": "de",
  "admin/catalog-translation.import.table-header.line": "Línea",
  "admin/catalog-translation.import.table-header.missing-fields": "Campos vacíos",
  "admin/catalog-translation.export.modal.title-brand": "Exportar datos de marca para",
  "admin/catalog-translation.export.modal.confirmation-brands": "Exportar marcas",
  "admin/catalog-translation.export.modal.export-active-brands": "Exportar solo marcas activas",
  "admin/catalog-translation.import.modal.entry-name-header": "Importar traducciones de {entryName} de {selectedLocale}",
  "admin/catalog-translation.export.modal.specification-header": "Exportar las traducciones de especificaciones de {selectedLocale}",
  "admin/catalog-translation.export.modal.download-button": "Descargar modelo XLSX",
  "admin/catalog-translation.export.modal.dropzone": "Suelta aquí tu XLSX o ",
  "admin/catalog-translation.export.modal.dropzone-choose-file": "selecciona un archivo",
  "admin/catalog-translation.export.modal.total-entries": "nº total de entradas",
  "admin/catalog-translation.export.modal.total-warnings": "alertas",
  "admin/catalog-translation.export.modal.total-errors": "errores. Las entradas serán ignoradas.",
  "admin/catalog-translation.export.modal.error-uploading": "Error al cargar las traducciones",
  "admin/catalog-translation.export.modal.confirmation": "Exportar traducciones"
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/admin/navigation.json from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "section": "products",
  "titleId": "admin/catalog-translation.navigation.label",
  "searchKeyWordsHelpers": "admin/catalog-translation.navigation.search.kws",
  "subSectionItems": [
    {
      "labelId": "admin/catalog-translation.navigation.category",
      "path": "/admin/catalog-translation/category"
    },
    {
      "labelId": "admin/catalog-translation.navigation.product",
      "path": "/admin/catalog-translation/product"
    },
    {
      "labelId": "admin/catalog-translation.navigation.sku",
      "path": "/admin/catalog-translation/sku"
    },
    {
      "labelId": "admin/catalog-translation.navigation.brand",
      "path": "/admin/catalog-translation/brand"
    },
    {
      "labelId": "admin/catalog-translation.navigation.specifications",
      "path": "/admin/catalog-translation/specifications"
    }
  ]
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/admin/routes.json from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "admin.app.catalog-translation-category": {
    "component": "CategoryTranslation",
    "path": "/admin/app/catalog-translation/category"
  },
  "admin.app.catalog-translation-product": {
    "component": "ProductTranslation",
    "path": "/admin/app/catalog-translation/product"
  },
  "admin.app.catalog-translation-sku": {
    "component": "SKUTranslation",
    "path": "/admin/app/catalog-translation/sku"
  },
  "admin.app.catalog-translation-brand": {
    "component": "BrandTranslation",
    "path": "/admin/app/catalog-translation/brand"
  },
  "admin.app.catalog-translation-specifications": {
    "component": "SpecificationsTranslation",
    "path": "/admin/app/catalog-translation/specifications"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/docs/README.md from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```markdown
# Admin Catalog Translation

##  ⚠ Maintenance ⚠
*Starting June 1st 2023, this application will no longer be maintained by VTEX.*

---

The catalog translation app provides a way to read and update SEO relevant information for all bindings associated to an account for the following catalog entries:

| Entry Type | Available Translation option |
| ---------|-------------|
| Category | Single entries, bulk export and bulk import |
| Product | Single entries, bulk export and bulk import |
| SKU | Single entries and bulk export |
| Brand | Single entries, bulk export and bulk import |
| Specification | Single entries, bulk export and bulk import |
| Collection | Single entries, bulk export and bulk import |

![](https://user-images.githubusercontent.com/38737958/153192217-45e37812-9a6d-42de-ba5e-f01bd0f62c82.gif)

---
## Description

`This app is aimed towards stores with a one account multiple binding architecture.`

This app uses the `catalog translation` queries on `vtex.catalog-graphql` instead of `messages app`, this is because we are targeting the SEO relevant information for Google.

Every entry type has its own page to handle translations.They automatically display the list of all languages (default and supported ones) for all `storefront` bindings configured for the store.
From this binding list, the first one is always the `X-Vtex-Tenant` or the default language. For this option the details cannot be translated. To modify these values, the changes should be made inside your store's catalog.
For all the others, it's possible to edit the content.

For some entries, there are options for bulk import and export (please refer back to the table above). Selecting a language different than the `X-Vtex-Tenant`, a button called `export` allows user to download the translations for that binding. Another button called `import` allow bulk upload for the entries in the selected language.

---
## Usage

Using [VTEX IO Toobelt](https://vtex.io/docs/recipes/development/vtex-io-cli-installation-and-command-reference/#command-reference) log into the VTEX account you are working on and install the latest version:

```
$ vtex install vtex.admin-catalog-translation
```

---

## Knowing issues

- It's not possible to add content for a specific field if there is no content for that same field in the `X-Vtex-Tenant` (default) binding.
- If different fields have the same value (e.g. product name and product description are equals) for the main language, translating one of them will also translate the other one. This is due to the behavior of VTEX message center.

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/graphql/schema.graphql from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```graphql
enum DownloadBucket {
  product_translation
  sku_translation
  field_translation
}

enum UploadBucket {
  brand_translation
  product_translation
  sku_translation
  collection_transl
  field_translation
  category_transl
}

type Query {
  """
  Returns a list with the translations for all categories. The translation happens during the request.
  """
  categoryTranslations(locale: String!, active: Boolean): [Category]
  """
  Returns a list with the translations for all collections. The translation happens during the request.
  """
  collectionTranslations(
    locale: String!
    active: Boolean
    xVtexTenant: String!
  ): [Collection]
  """
  Returns a list with all the categories names.
  """
  getCategoriesName: [CategoryName]
  """
  Starts the async process to get product translations given a category id and locale.
  Returns an object with the request information.
  """
  productTranslations(
    locale: String!
    categoryId: String!
  ): ProductTranslationRequest
  """
  Returns a list of IDs for requests made to download product translations. The id can be used to check the request status and download the translations.
  """
  productTranslationRequests: [String]
  """
  Returns a list of product translations related to the given requestId that were saved on VBASE.
  """
  downloadProductTranslation(requestId: String!): [Product]
  """
  Starts the async process to get the SKUs translations given a category id and locale.
  Returns an object with the request information.
  """
  skuTranslations(locale: String!, categoryId: String!): SKUTranslationRequest
  """
  Returns a list of IDs for requests made to download SKU translations. The id can be used to check the request status and download the translations.
  """
  skuTranslationRequests: [String]
  """
  Returns a list of SKU translations related to the given requestId that were saved on VBASE.
  """
  downloadSKUTranslation(requestId: String!): [SKU]
  """
  Returns a list of IDs for requests made to upload product translations.
  """
  productTranslationsUploadRequests: [String]
  """
  Returns a list of IDs for requests made to upload sku translations.
  """
  skuTranslationsUploadRequests: [String]
  """
  Returns a list of IDs for requests made to upload collection translations.
  """
  collectionTranslationsUploadRequests: [String]
  """
  Returns the upload request status for a given requestId - generated when a file is sent to be uploaded.
  """
  translationUploadRequestInfo(
    requestId: String!
    bucket: UploadBucket!
  ): UploadRequest
  """
  Returns a list with the translations for all brands. The translation happens during the request.
  """
  brandTranslations(locale: String!, active: Boolean): [Brand]
  """
  Returns a list of IDs for requests made to upload brand translations.
  """
  brandTranslationsUploadRequests: [String]
  """
  Returns a list of IDs for requests made to upload category translations.
  """
  categoryTranslationsUploadRequests: [String]
  """
  Starts the async process to translate specification field given a locale.
  Returns an object with the request information.
  """
  fieldTranslations(fields: Upload!, locale: String!): TranslationRequest
  """
  Returns a list of IDs for requests made to download specification field translations. The id can be used to check the request status and download the translations.
  """
  fieldTranslationsRequests: [String]
  """
  Returns a list of IDs for requests made to upload specification field translations.
  """
  fieldTranslationsUploadRequests: [String]
  """
  Returns a list of specification field translations related to the given requestId that were saved on VBASE.
  """
  downloadFieldTranslations(requestId: String!): [Field]
  """
  Returns status of a request made to download translations. It works for downloads that are managed async - Product, SKU and specification fields.
  """
  translationRequestInfo(
    requestId: String!
    bucket: DownloadBucket!
  ): TranslationRequest
}

type Mutation {
  uploadProductTranslations(products: Upload!, locale: String!): String
  uploadSkuTranslations(skus: Upload!, locale: String!): String
  uploadCollectionTranslations(
    collections: Upload!
    locale: String!
    xVtexTenant: String!
  ): String
  uploadBrandTranslations(brands: Upload!, locale: String!): String
  uploadFieldTranslationsImport(fields: Upload!, locale: String!): String
  uploadCategoryTranslations(categories: Upload!, locale: String!): String
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/service.json from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "memory": 1024,
  "ttl": 60,
  "timeout": 60,
  "minReplicas": 2,
  "maxReplicas": 10,
  "workers": 2
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/package.json from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "vtex.admin-catalog-translation",
  "version": "2.4.0",
  "main": "index.ts",
  "license": "UNLICENSED",
  "dependencies": {
    "@vtex/api": "6.47.0",
    "JSONStream": "^1.3.5"
  },
  "devDependencies": {
    "@vtex/tsconfig": "^0.5.6",
    "typescript": "3.9.7",
    "vtex.admin-catalog-translation": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.admin-catalog-translation@1.3.0/public/@types/vtex.admin-catalog-translation",
    "vtex.catalog-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.catalog-graphql@1.97.0/public/@types/vtex.catalog-graphql",
    "vtex.messages": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.messages@1.65.0/public/@types/vtex.messages",
    "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.145.2/public/@types/vtex.styleguide",
    "vtex.tenant-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.tenant-graphql@0.1.2/public/@types/vtex.tenant-graphql"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/index.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  ParamsContext,
  RecorderState,
  Service,
  ServiceContext,
} from '@vtex/api'

import { Clients } from './clients'
import { resolvers, queries, mutations } from './resolvers'

declare global {
  type Context = ServiceContext<Clients, State>

  interface State extends RecorderState {
    locale: string
  }
}

export default new Service<Clients, State, ParamsContext>({
  clients: {
    implementation: Clients,
    options: {
      default: {
        retries: 2,
        timeout: 2 * 60000,
      },
    },
  },
  graphql: {
    resolvers: {
      ...resolvers,
      Query: {
        ...queries,
      },
      Mutation: {
        ...mutations,
      },
    },
  },
})

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/react/CategoryTranslation.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC } from 'react'
import { FormattedMessage } from 'react-intl'

import CategoryTranslation from './components/CategoryTranslation/CategoryTranslation'
import CatalogTranslationWrapper from './components/CatalogTranslationWrapper'

const CategoryTranslationWrapped: FC = () => {
  return (
    <CatalogTranslationWrapper
      titleComponent={
        <FormattedMessage id="admin/catalog-translation.category.header" />
      }
      hasExport
      hasImport
    >
      <CategoryTranslation />
    </CatalogTranslationWrapper>
  )
}

export default CategoryTranslationWrapped

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/react/CollectionsTranslation.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC } from 'react'
import { FormattedMessage } from 'react-intl'

import CatalogTranslationWrapper from './components/CatalogTranslationWrapper'
import CollectionsTranslation from './components/CollectionsTranslation/CollectionTranslation'

const CollectionsTranslationWrapped: FC = () => {
  return (
    <CatalogTranslationWrapper
      titleComponent={
        <FormattedMessage id="admin/catalog-translation.collections.header" />
      }
      hasExport
      hasImport
    >
      <CollectionsTranslation />
    </CatalogTranslationWrapper>
  )
}

export default CollectionsTranslationWrapped

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/react/SKUTranslation.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC } from 'react'
import { FormattedMessage } from 'react-intl'

import CatalogTranslationWrapper from './components/CatalogTranslationWrapper'
import SKUTranslation from './components/SKUTranslation/SKUTranslation'

const SKUTranslationWrapped: FC = () => {
  return (
    <CatalogTranslationWrapper
      titleComponent={<FormattedMessage id="admin/catalog-translation.sku.header" />}
      hasExport
      hasImport
    >
      <SKUTranslation />
    </CatalogTranslationWrapper>
  )
}
export default SKUTranslationWrapped

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/react/SpecificationsTranslation.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC, useState } from 'react'
import { FormattedMessage } from 'react-intl'
import { Tabs, Tab, Layout, PageHeader } from 'vtex.styleguide'

import CatalogTranslationWrapper from './components/CatalogTranslationWrapper'
import SpecificationsTranslation from './components/SpecificationsTranslation/SpecificationsTranslation'
import SpecificationsTranslationValues from './components/SpecificationsTranslation/SpecificationsTranslationValues'

const SpecificationsTranslationWrapped: FC = () => {
  const [currentTab, setCurrentTab] = useState<number>(1)
  return (
    <Layout className="pd10">
      <Tabs>
        <Tab
          label="Specifications"
          active={currentTab === 1}
          onClick={() => setCurrentTab(1)}>
            <CatalogTranslationWrapper
              titleComponent={
                <FormattedMessage id="admin/catalog-translation.specifications.header" />
              }
              hasExport
              hasImport
            >
                  <SpecificationsTranslation />
            </CatalogTranslationWrapper>
        </Tab>
        <Tab
          label="Specification Values"
          active={currentTab === 2}
          onClick={() => setCurrentTab(2)}>
            <CatalogTranslationWrapper
              titleComponent={
                <FormattedMessage id="admin/catalog-translation.specifications.header" />
              }
              hasExport={false}
              hasImport={false}
            >
              <SpecificationsTranslationValues />
            </CatalogTranslationWrapper>
        </Tab>
      </Tabs>
    </Layout>
  )
}

export default SpecificationsTranslationWrapped

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/react/package.json from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "vtex.admin-example",
  "version": "2.4.0",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "devDependencies": {
    "@types/graphql": "^14.5.0",
    "@types/node": "^11.12.1",
    "@types/react": "^16.8.10",
    "@types/react-intl": "^3.0.0",
    "apollo-client": "^2.4.7",
    "graphql": "^14.0.2",
    "prop-types": "^15.7.2",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-intl": "^5.20.4",
    "typescript": "3.9.7",
    "vtex.catalog-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.catalog-graphql@1.97.0/public/@types/vtex.catalog-graphql",
    "vtex.messages": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.messages@1.65.0/public/@types/vtex.messages",
    "vtex.admin-catalog-translation": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.admin-catalog-translation@1.3.0/public/@types/vtex.admin-catalog-translation",
    "vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.render-runtime@8.132.3/public/@types/vtex.render-runtime",
    "vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.styleguide@9.145.2/public/@types/vtex.styleguide",
    "vtex.tenant-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/vtex.tenant-graphql@0.1.2/public/@types/vtex.tenant-graphql"
  },
  "dependencies": {
    "@types/faker": "^4.1.5",
    "faker": "^4.1.0",
    "react-apollo": "^3.1.5",
    "xlsx": "^0.16.9"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/react/ProductTranslation.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC } from 'react'
import { FormattedMessage } from 'react-intl'

import CatalogTranslationWrapper from './components/CatalogTranslationWrapper'
import ProductTranslation from './components/ProductTranslation/ProductTranslation'

const ProductTranslationWrapped: FC = () => {
  return (
    <CatalogTranslationWrapper
      titleComponent={
        <FormattedMessage id="admin/catalog-translation.product.header" />
      }
      hasExport
      hasImport
    >
      <ProductTranslation />
    </CatalogTranslationWrapper>
  )
}

export default ProductTranslationWrapped

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/react/BrandTranslation.tsx from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```tsx
import React, { FC } from 'react'
import { FormattedMessage } from 'react-intl'

import CatalogTranslationWrapper from './components/CatalogTranslationWrapper'
import BrandTranslation from './components/BrandTranslation/BrandTranslation'

const BrandTranslationWrapper: FC = () => {
  return (
    <CatalogTranslationWrapper
      titleComponent={
        <FormattedMessage id="admin/catalog-translation.brand.header" />
      }
      hasExport
      hasImport
    >
      <BrandTranslation />
    </CatalogTranslationWrapper>
  )
}
export default BrandTranslationWrapper

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/public/metadata/messages/es-AR.json from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "Traducción del catálogo",
  "headline": "La aplicación de traducción de catálogos ofrece una forma de leer y actualizar la información relevante para el SEO.",
  "overview": "La aplicación se puede utilizar para todos los enlaces asociados a una cuenta para las siguientes entradas del catálogo: Categoría, Producto, SKU, Marca, Especificación y Colección\\\\n Esta aplicación está dirigida a las tiendas con una arquitectura de vinculación múltiple de una cuenta.\\\\n Cada tipo de entrada tiene su propia página para manejar las traducciones.Muestran automáticamente la lista de todos los idiomas (por defecto y soportados) para todas las vinculaciones del escaparate configuradas para la tienda.",
  "features": [
    "Mejorar SEO",
    "Traducción de Categoría, Producto, SKU, Marca, Especificación y Colección"
  ],
  "websiteURL": "https://vtex.com/",
  "support": {
    "url": "https://support.vtex.com/hc/requests"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/public/metadata/messages/pt-BR.json from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "Tradução do catálogo",
  "headline": "A aplicação de tradução do catálogo oferece uma maneira de ler e atualizar informações relevantes para SEO.",
  "overview": "O aplicativo pode ser usado para todos os links associados a uma conta para as seguintes entradas do catálogo: Categoria, Produto, SKU, Marca, Especificação e Coleção\\\\n Esta aplicação é destinada a lojas com uma arquitetura de múltiplos links de uma conta.\\\\n Cada tipo de entrada tem sua própria página para lidar com traduções. Eles exibem automaticamente a lista de todos os idiomas (padrão e suportado) para todos os links de loja configurados para a loja.",
  "features": [
    "Melhorando SEO",
    "Tradução de Categoria, Produto, SKU, Marca, Especificação e Coleção"
  ],
  "websiteURL": "https://vtex.com/",
  "support": {
    "url": "https://support.vtex.com/hc/requests"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/public/metadata/messages/en-US.json from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```json
{
  "name": "Catalog translation",
  "headline": "The catalog translation app provides a way to read and update SEO relevant information.",
  "overview": "The app can be use for for all bindings associated to an account for the following catalog entries: Category, Product, SKU, Brand, Specification and Collection\\\\n This app is aimed towards stores with a one account multiple binding architecture.\\\\n Every entry type has its own page to handle translations.They automatically display the list of all languages (default and supported ones) for all storefront bindings configured for the store.",
  "features": [
    "Improve SEO",
    "Translation of Category, Product, SKU, Brand, Specification and Collection"
  ],
  "websiteURL": "https://vtex.com/",
  "support": {
    "url": "https://support.vtex.com/hc/requests"
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/graphql/types/SKU.graphql from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```graphql
type SKUTranslationRequest {
  requestId: String!
  requestedBy: String!
  categoryId: String!
  error: Boolean
  createdAt: String!
  locale: String!
  completedAt: String
  estimatedTime: Int
}

type SKU {
  id: String!
  name: String
  locale: String
}

input SkuTranslationUpload {
  id: String!
  name: String
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/graphql/types/Brand.graphql from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```graphql
type Brand {
  id: Int
  name: String
  text: String
  siteTitle: String
  active: Boolean
  locale: String
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/graphql/types/Category.graphql from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```graphql
type Category {
  id: String
  name: String
  title: String
  description: String
  linkId: String
  locale: String
}

type CategoryName {
  id: String
  name: String
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/graphql/types/Product.graphql from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```graphql
type Product {
  id: String!
  name: String
  description: String
  shortDescription: String
  title: String
  locale: String
  linkId: String
}

type ProductTranslationRequest {
  requestId: String!
  requestedBy: String!
  categoryId: String!
  error: Boolean
  createdAt: String!
  locale: String!
  completedAt: String
  estimatedTime: Int
}

input ProductTranslationUpload {
  id: String!
  name: String
  description: String
  shortDescription: String
  title: String
}

type UploadRequest {
  requestId: String!
  translatedBy: String!
  createdAt: String!
  estimatedTime: Int!
  error: Boolean
  progress: Int
  locale: String!
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/graphql/types/Translation.graphql from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```graphql
type TranslationRequest {
  requestId: String!
  requestedBy: String!
  categoryId: String
  error: Boolean
  createdAt: String!
  locale: String!
  completedAt: String
  estimatedTime: Int
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/graphql/types/Collection.graphql from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```graphql
type Collection {
  id: String
  name: String
  locale: String
}

type CollectionName {
  id: String
  name: String
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/clients/catalogGQL.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { AppGraphQLClient, InstanceOptions, IOContext } from '@vtex/api'

import { statusToError } from '../utils'
import {
  CATEGORIES_QUERY,
  GET_CATEGORY_TRANSLATION_QUERY,
  TRANSLATE_CATEGORY,
  GET_PRODUCT_TRANSLATION_QUERY,
  GET_SKU_TRANSLATION_QUERY,
  TRANSLATE_PRODUCT,
  TRANSLATE_SKU,
  BRAND_QUERY,
  GET_BRAND_TRANSLATION_QUERY,
  TRANSLATE_BRAND,
  GET_FIELD_TRANSLATION_QUERY,
  TRANSLATE_FIELD,
  COLLECTIONS_QUERY,
  GET_COLLECTION_TRANSLATION_QUERY,
} from './utils/catalogGQLQueries'

const CATALOG_GRAPHQL_APP = 'vtex.catalog-graphql@1.x'

export class CatalogGQL extends AppGraphQLClient {
  constructor(ctx: IOContext, options?: InstanceOptions) {
    super(CATALOG_GRAPHQL_APP, ctx, {
      ...options,
      headers: {
        ...options?.headers,
        VtexIdclientAutCookie: ctx.authToken ?? ctx.adminUserAuthToken ?? null,
      },
    })
  }

  public getCategories = async (active = true) => {
    try {
      const response = await this.getCategoriesPerPage({ active, page: 1 })
      const {
        items,
        paging: { pages },
      } = (response.data as CategoryResponse).categories
      const collectItems = items
      const responsePromises = []

      for (let i = 2; i <= pages; i++) {
        const promise = this.getCategoriesPerPage({ active, page: i })
        responsePromises.push(promise)
      }

      const resolvedPromises = await Promise.all(responsePromises)

      const flattenResponse = resolvedPromises.reduce((acc, curr) => {
        return [...acc, ...(curr.data as CategoryResponse).categories.items]
      }, collectItems)

      return flattenResponse
    } catch (error) {
      return statusToError(error)
    }
  }

  private getCategoriesPerPage = ({
    active = true,
    page,
  }: {
    active: boolean
    page: number
  }) =>
    this.graphql.query<CategoryResponse, { active: boolean; page: number }>({
      query: CATEGORIES_QUERY,
      variables: {
        active,
        page,
      },
    })

  public getCategoryTranslation = (id: string, locale: string) => {
    return this.graphql.query<CategoryTranslationResponse, { id: string }>(
      {
        query: GET_CATEGORY_TRANSLATION_QUERY,
        variables: {
          id,
        },
      },
      {
        headers: {
          'x-vtex-locale': `${locale}`,
        },
      }
    )
  }

  public translateCategory = <T>(params: TranslateEntry<T>) => {
    const { entry: category, locale } = params
    return this.graphql.query({
      query: TRANSLATE_CATEGORY,
      variables: {
        category,
        locale,
      },
    })
  }

  public getProductTranslation = <T>(params: TranslateEntry<T>) => {
    const { entry: id, locale } = params
    return this.graphql.query<
      ProductTranslationResponse,
      { identifier: { value: T; field: 'id' } }
    >(
      {
        query: GET_PRODUCT_TRANSLATION_QUERY,
        variables: {
          identifier: {
            field: 'id',
            value: id,
          },
        },
      },
      {
        headers: {
          'x-vtex-locale': `${locale}`,
        },
      }
    )
  }

  public getSKUTranslation = (id: string, locale: string) =>
    this.graphql.query<
      SKUTranslationsResponse,
      { identifier: { value: string; field: 'id' } }
    >(
      {
        query: GET_SKU_TRANSLATION_QUERY,
        variables: {
          identifier: {
            field: 'id',
            value: id,
          },
        },
      },
      {
        headers: {
          'x-vtex-locale': `${locale}`,
        },
      }
    )

  public translateProduct = <T>(params: TranslateEntry<T>) => {
    const { entry: product, locale } = params

    return this.graphql.query({
      query: TRANSLATE_PRODUCT,
      variables: {
        product,
        locale,
      },
    })
  }

  public translateSku = <T>(params: TranslateEntry<T>) => {
    const { entry: sku, locale } = params

    return this.graphql.query({
      query: TRANSLATE_SKU,
      variables: {
        sku,
        locale,
      },
    })
  }

  private getBrandsPerPage = ({ page }: { page: number }) =>
    this.graphql.query<BrandResponse, { page: number }>({
      query: BRAND_QUERY,
      variables: {
        page,
      },
    })

  private filterAndBuildItemsTranslation = (
    items: Brand[],
    active: boolean
  ) => {
    const filterItems = []
    if (items?.length > 0) {
      for (const a in items) {
        if (!active || active === items[a]?.active) {
          filterItems.push({
            data: {
              brand: items[a],
            },
          })
        }
      }
    }
    return filterItems
  }

  public getBrands = async (active = true) => {
    try {
      const response = await this.getBrandsPerPage({ page: 1 })
      const {
        items,
        paging: { pages },
      } = (response.data as BrandResponse).brands
      const responsePromises = []

      for (let i = 2; i <= pages; i++) {
        const promise = this.getBrandsPerPage({ page: i })
        responsePromises.push(promise)
      }

      const resolvedPromises = await Promise.all(responsePromises)

      let translations = [
        ...this.filterAndBuildItemsTranslation(items ?? [], active),
      ]
      for (const i in resolvedPromises) {
        const { data } = resolvedPromises[i]
        translations = [
          ...translations,
          ...this.filterAndBuildItemsTranslation(
            data?.brands?.items ?? [],
            active
          ),
        ]
      }

      const flattenResponse = translations.reduce((acc: Brand[], curr) => {
        return [...acc, curr.data.brand]
      }, [] as Brand[])

      return flattenResponse
    } catch (error) {
      return statusToError(error)
    }
  }

  public getBrandTranslation = (id: string, locale: string) => {
    return this.graphql.query<BrandTranslationResponse, { id: string }>(
      {
        query: GET_BRAND_TRANSLATION_QUERY,
        variables: {
          id,
        },
      },
      {
        headers: {
          'x-vtex-locale': `${locale}`,
        },
      }
    )
  }

  public translateBrand = <T>(params: TranslateEntry<T>) => {
    const { entry: brand, locale } = params
    return this.graphql.query({
      query: TRANSLATE_BRAND,
      variables: {
        brand,
        locale,
      },
    })
  }

  public getFields = async (fields: FieldTranslationInput[]) => {
    try {
      return [`fields${fields.length}`]
    } catch (error) {
      return statusToError(error)
    }
  }

  public getFieldTranslation = <T>(params: TranslateEntry<T>) => {
    const { entry: id, locale } = params
    return this.graphql.query<FieldTranslationResponse, { id: T }>(
      {
        query: GET_FIELD_TRANSLATION_QUERY,
        variables: {
          id,
        },
      },
      {
        headers: {
          'x-vtex-locale': `${locale}`,
        },
      }
    )
  }

  public translateField = <T>(params: TranslateEntry<T>) => {
    const { entry: field, locale } = params
    return this.graphql.query({
      query: TRANSLATE_FIELD,
      variables: {
        field,
        locale,
      },
    })
  }

  public getCollections = async () => {
    try {
      const response = await this.getCollectionsPerPage({ page: 1 })
      const {
        items,
        paging: { pages },
      } = (response.data as CollectionResponse).collections
      const collectItems = items
      const responsePromises = []

      for (let i = 2; i <= pages; i++) {
        const promise = this.getCollectionsPerPage({ page: i })
        responsePromises.push(promise)
      }

      const resolvedPromises = await Promise.all(responsePromises)

      const flattenResponse = resolvedPromises.reduce((acc, curr) => {
        return [...acc, ...(curr.data as CollectionResponse).collections.items]
      }, collectItems)

      return flattenResponse
    } catch (error) {
      console.error(error)
      return statusToError(error)
    }
  }

  private getCollectionsPerPage = ({ page }: { page: number }) =>
    this.graphql.query<CollectionResponse, { page: number }>({
      query: COLLECTIONS_QUERY,
      variables: {
        page,
      },
    })

  public getCollectionTranslation = (id: string, locale: string) => {
    return this.graphql.query<CollectionTranslationResponse, { id: string }>(
      {
        query: GET_COLLECTION_TRANSLATION_QUERY,
        variables: {
          id,
        },
      },
      {
        headers: {
          'x-vtex-locale': `${locale}`,
        },
      }
    )
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/clients/index.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { IOClients, MessagesGraphQL } from '@vtex/api'

import { CatalogGQL } from './catalogGQL'
import { Catalog } from './catalog'

export class Clients extends IOClients {
  public get catalogGQL() {
    return this.getOrSet('catalogGQL', CatalogGQL)
  }

  public get catalog() {
    return this.getOrSet('catalog', Catalog)
  }

  public get messages() {
    return this.getOrSet('messages', MessagesGraphQL)
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/clients/catalog.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  InstanceOptions,
  IOContext,
  JanusClient,
  RequestConfig,
} from '@vtex/api'

import {
  statusToError,
  interations,
  getInterationPairs,
  extractProductId,
  MAX_PRODUCTS_PER_CATEGORY,
  extractSkuId,
} from '../utils'

export class Catalog extends JanusClient {
  constructor(ctx: IOContext, opts?: InstanceOptions) {
    super(ctx, {
      ...opts,
      headers: {
        ...opts?.headers,
        ...(ctx.adminUserAuthToken
          ? { VtexIdclientAutCookie: ctx.adminUserAuthToken }
          : null),
      },
    })
  }

  public getAllProducts = async (categoryId: string) => {
    const { range, ...products } = await this.getProductIdsByCategory(
      categoryId,
      1,
      MAX_PRODUCTS_PER_CATEGORY
    )
    const { total } = range
    const remainingInterations = interations(total)
    const productPerCategorypromises = []

    // /GetProductAndSkuIds returns max 50 responses. We loop over the remaining interations to get all products
    for (let i = 1; i <= remainingInterations; i++) {
      const [from, to] = getInterationPairs(i)
      const productPerIdPromise = this.getProductIdsByCategory(
        categoryId,
        from,
        to
      )
      productPerCategorypromises.push(productPerIdPromise)
    }

    const productPerCategoryCollection = await Promise.all(
      productPerCategorypromises
    )

    const finalProducts = []

    // we plug together the first response and all the others. Then we extract only the product ids from responses
    for (const product of [products, ...productPerCategoryCollection]) {
      const productIds = extractProductId(product.data)
      finalProducts.push(...productIds)
    }
    return finalProducts
  }

  public getAllSKUs = async (categoryId: string) => {
    const { range, ...products } = await this.getProductIdsByCategory(
      categoryId,
      1,
      MAX_PRODUCTS_PER_CATEGORY
    )
    const { total } = range
    const remainingInterations = interations(total)
    const productPerCategorypromises = []

    // /GetProductAndSkuIds returns max 50 responses. We loop over the remaining interations to get all products
    for (let i = 1; i <= remainingInterations; i++) {
      const [from, to] = getInterationPairs(i)
      const productPerIdPromise = this.getProductIdsByCategory(
        categoryId,
        from,
        to
      )
      productPerCategorypromises.push(productPerIdPromise)
    }

    const productPerCategoryCollection = await Promise.all(
      productPerCategorypromises
    )

    const finalSKUs = []

    // we plug together the first response and all the others. Then we extract only the product ids from responses
    for (const product of [products, ...productPerCategoryCollection]) {
      const skuIds = extractSkuId(product.data)
      finalSKUs.push(...skuIds)
    }
    return finalSKUs
  }

  private getProductIdsByCategory = (
    categoryId: string,
    _from: number,
    _to: number
  ) => {
    return this.get<GetProductAndSkuIds>(this.routes.getProductAndSkuIds(), {
      params: { categoryId, _from, _to },
    })
  }

  protected get = <T>(url: string, config: RequestConfig = {}) => {
    try {
      return this.http.get<T>(url, config)
    } catch (e) {
      return statusToError(e)
    }
  }

  private get routes() {
    const basePath = '/api/catalog_system'

    return {
      getProductAndSkuIds: () => `${basePath}/pvt/products/GetProductAndSkuIds`,
    }
  }
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/typings/brand.d.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface Brand {
  id: string
  name: string
  text: string
  siteTitle: string
  active: boolean
}

interface BrandResponse {
  brands: {
    items: Brand[]
    paging: {
      pages: number
    }
  }
}

interface BrandTranslationResponse {
  brand: Brand
}

interface BrandTranslationInput {
  id: string
  name?: string
  text?: string
  siteTitle?: string
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/typings/field.d.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface Field {
  fieldId: string
  name?: string
}

interface FieldTranslationResponse {
  field: Field
}

interface FieldTranslationInput {
  fieldId: string
  name?: string
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/typings/category.d.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface CategoryResponse {
  categories: {
    items: Array<{ id: string; name: string }>
    paging: {
      pages: number
    }
  }
}

interface CategoryTranslationResponse {
  category: {
    id: string
    name: string
    title: string
    description: string
    linkId: string
  }
}

interface ResolvedPromise<Response> {
  data: Response
}

interface CategoryTranslationInput {
  id: string
  name?: string
  title?: string
  description?: string
  locale?: string
  linkId?: string
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/typings/translation.d.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface TranslationRequest<T> {
  requestId: string
  translations?: Array<GraphQLResponse<T>> | null
  requestedBy: string
  categoryId?: string
  error?: boolean
  createdAt: Date
  locale: string
  completedAt?: Date
  estimatedTime: number
}
interface TranslateEntry<T> {
  entry: T
  locale: string
}

interface ParamsTranslationsToVBase extends EntryParams {
  bucket: UploadBucket
  requestId: string
  locale: string
}

interface InterfaceTranslationsEntriesToVBase<T> {
  entries: T[]
  params: ParamsTranslationsToVBase
  getEntryTranslation: (
    params: EntryTranslationParams
  ) => Promise<GraphQLResponse<Serializable>>
}

interface UploadEntriesAsync<T> {
  entries: T[]
  requestId: string
  locale: string
  bucket: UploadBucket
  translateEntry: <T>(
    params: TranslateEntry<T>
  ) => Promise<GraphQLResponse<Serializable>>
}

interface UploadTranslations<T> {
  entries: UploadFile<ReadStream>
  locale: string
  bucket: UploadBucket
  path: string
  translateEntry: <T>(
    params: TranslateEntry<T>
  ) => Promise<GraphQLResponse<Serializable>>
}
interface EntryTranslations<T> extends UploadEntriesAsync<T> {
  path: string
  categoryId?: string
}

type UploadBucket =
  | 'brand_translation'
  | 'product_translation'
  | 'collection_transl'
  | 'field_translation'
  | 'category_transl'
  | 'sku_translation'

type DownloadBucket =
  | 'product_translation'
  | 'field_translation'
  | 'sku_translation'

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/typings/SKU.d.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface SKUTranslationsResponse {
  sku: {
    id: string
    name: string
  }
}

interface SKUTranslationRequest {
  requestId: string
  translations?: Array<GraphQLResponse<SKUTranslationsResponse>> | null
  requestedBy: string
  categoryId: string
  error?: boolean
  createdAt: Date
  locale: string
  completedAt?: Date
  estimatedTime: number
}

interface SkuTranslationInput {
  id: string
  name?: string
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/typings/collection.d.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface CollectionResponse {
  collections: {
    items: Array<{
      id: string
      name: string
      status: string
    }>
    paging: {
      pages: number
    }
  }
}

interface CollectionTranslationResponse {
  collection: {
    id: string
    name: string
    status: string
  }
}

interface ResolvedPromise<Response> {
  data: Response
}

interface CollectionTranslationInput {
  id: string
  name?: string
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/typings/product.d.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
interface GetProductAndSkuIds {
  data: {
    [productId: string]: number[]
  }
  range: {
    total: number
    from: number
    to: number
  }
}

interface ProductTranslationResponse {
  product: {
    id: string
    name: string
    description: string
    shortDescription: string
    title: string
    linkId: string
  }
}

interface ProductTranslationInput {
  id: string
  name?: string
  description?: string
  shortDescription?: string
  title?: string
  linkId?: string
}

interface UploadRequest {
  requestId: string
  translatedBy: string
  createdAt: Date
  estimatedTime: number
  locale: string
  error?: boolean
  progress?: number
}

type UploadFile<T> = Promise<{
  filename: string
  mimetype: string
  enconding: string
  createReadStream: () => T
}>

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/utils/index.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ReadStream } from 'fs'

import {
  AuthenticationError,
  ForbiddenError,
  UserInputError,
  VBase,
} from '@vtex/api'
import type { AxiosError } from 'axios'
import JSONStream from 'JSONStream'

import { CatalogGQL } from '../clients/catalogGQL'

const ONE_MINUTE = 60 * 1000
export const CALLS_PER_MINUTE = 1600
export const PRODUCT_BUCKET = 'product_translation'
export const PRODUCT_TRANSLATION_UPLOAD = 'product-upload'
export const COLLECTION_BUCKET = 'collection_transl'
export const COLLECTION_TRANSLATION_UPLOAD = 'collection-upload'
export const SKU_BUCKET = 'sku_translation'
export const SKU_TRANSLATION_UPLOAD = 'sku-upload'
export const ALL_TRANSLATIONS_FILES = 'all-translations'
export const ALL_SKU_TRANSLATIONS_FILES = 'all-sku-translations'
export const BRAND_BUCKET = 'brand_translation'
export const BRAND_TRANSLATION_UPLOAD = 'brand-upload'
export const FIELD_BUCKET = 'field_translation'
export const FIELD_TRANSLATION_EXPORT_UPLOAD = 'field-export-upload'
export const FIELD_TRANSLATION_IMPORT_UPLOAD = 'field-import-upload'
export const CATEGORY_BUCKET = 'category_transl'
export const CATEGORY_TRANSLATION_UPLOAD = 'category-upload'

export const statusToError = (e: AxiosError) => {
  if (!e.response) {
    throw e
  }

  const {
    response: { status },
  } = e

  switch (status) {
    case 401: {
      throw new AuthenticationError(e)
    }

    case 403: {
      throw new ForbiddenError(e)
    }

    case 400: {
      throw new UserInputError(e)
    }

    default:
      throw new TypeError(e.message)
  }
}

export const MAX_PRODUCTS_PER_CATEGORY = 50

export const interations = (total: number) => {
  return (
    Math.floor(total / MAX_PRODUCTS_PER_CATEGORY) -
    (total % MAX_PRODUCTS_PER_CATEGORY ? 0 : 1)
  )
}

export const getInterationPairs = (currentStep: number): number[] => [
  MAX_PRODUCTS_PER_CATEGORY * currentStep + 1,
  MAX_PRODUCTS_PER_CATEGORY * currentStep + 50,
]

export const extractProductId = (productResponse: Record<string, number[]>) => {
  return Object.keys(productResponse)
}

export const extractSkuId = (productResponse: Record<string, number[]>) => {
  return Object.values(productResponse).flat()
}

export const pacer = (callsPerMinute: number) =>
  new Promise((resolve) => {
    setTimeout(() => {
      resolve('done')
    }, ONE_MINUTE / callsPerMinute)
  })

export const calculateExportProcessTime = (
  size: number,
  callsPerMinute: number
): number => Math.ceil(size * (ONE_MINUTE / callsPerMinute))

export const calculateBreakpoints = (size: number): number[] => {
  return [
    Math.ceil(size * 0.25),
    Math.ceil(size * 0.5),
    Math.ceil(size * 0.75),
  ].filter((num, idx, self) => self.indexOf(num) === idx)
}

export const parseStreamToJSON = <T>(stream: ReadStream): Promise<T[]> => {
  const promise = new Promise<T[]>((resolve) => {
    const finalArray: T[] = []
    stream.pipe(
      JSONStream.parse('*').on('data', (data: T) => {
        finalArray.push(data)
      })
    )
    stream.on('end', () => {
      stream.destroy()
      resolve(finalArray)
    })
  })

  return promise
}

export const uploadEntriesAsync = async <T>(
  { entries, requestId, locale, bucket, translateEntry }: UploadEntriesAsync<T>,
  { vbase }: { vbase: VBase }
) => {
  const translationRequest = await vbase.getJSON<UploadRequest>(
    bucket,
    requestId,
    true
  )

  try {
    const totalEntries = entries.length

    const breakPointsProgress = calculateBreakpoints(totalEntries)

    let promiseController = []
    for (let i = 0; i < totalEntries; i++) {
      const params: TranslateEntry<T> = {
        entry: entries[i],
        locale,
      }
      promiseController.push(translateEntry<T>(params))
      // eslint-disable-next-line no-await-in-loop
      await pacer(CALLS_PER_MINUTE)
      if (breakPointsProgress.includes(i)) {
        // eslint-disable-next-line no-await-in-loop
        await Promise.all(promiseController)

        // eslint-disable-next-line no-await-in-loop
        await vbase.saveJSON<UploadRequest>(bucket, requestId, {
          ...translationRequest,
          progress: Math.ceil((i / totalEntries) * 100),
        })
        promiseController = []
      }
    }

    await Promise.all(promiseController)
    await vbase.saveJSON<UploadRequest>(bucket, requestId, {
      ...translationRequest,
      progress: 100,
    })
  } catch (error) {
    const translationRequestUpdated = await vbase.getJSON<UploadRequest>(
      bucket,
      requestId,
      true
    )
    await vbase.saveJSON<UploadRequest>(bucket, requestId, {
      ...translationRequestUpdated,
      error: true,
    })
  }
}

const saveTranslationsEntriesToVBase = async <T, X>(
  {
    entries,
    params,
    getEntryTranslation,
  }: InterfaceTranslationsEntriesToVBase<T>,
  { vbase }: { catalogGQLClient: CatalogGQL; vbase: VBase }
): Promise<void> => {
  const { requestId, bucket, locale } = params
  const translationRequest = await vbase.getJSON<TranslationRequest<X>>(
    bucket,
    requestId,
    true
  )
  const entryTranslationPromises = []
  try {
    for (const entry of entries) {
      const currentParams: TranslateEntry<T> = {
        locale,
        entry,
      }
      const translationPromise = getEntryTranslation(currentParams)
      entryTranslationPromises.push(translationPromise)
      // eslint-disable-next-line no-await-in-loop
      await pacer(CALLS_PER_MINUTE)
    }

    const translations = await Promise.all(entryTranslationPromises)

    const updateTranslation = {
      ...translationRequest,
      translations,
      completedAt: new Date(),
    }

    await vbase.saveJSON<TranslationRequest<X>>(
      bucket,
      requestId,
      updateTranslation
    )
  } catch {
    const addError = {
      ...translationRequest,
      error: true,
    }
    await vbase.saveJSON<TranslationRequest<X>>(bucket, requestId, addError)
  }
}

export const uploadTranslations = async <T>(
  { entries, locale, bucket, path, translateEntry }: UploadTranslations<T>,
  ctx: Context
) => {
  const {
    clients: { licenseManager, vbase },
    vtex: { adminUserAuthToken, requestId },
  } = ctx

  const { createReadStream } = await entries

  const stream = createReadStream()

  const streamParsed = await parseStreamToJSON<T>(stream)

  const {
    profile: { email },
  } = await licenseManager.getTopbarData(adminUserAuthToken as string)

  const allTranslationsMade = await vbase.getJSON<string[]>(bucket, path, true)

  const updateRequests = allTranslationsMade
    ? [requestId, ...allTranslationsMade]
    : [requestId]

  await vbase.saveJSON<string[]>(bucket, path, updateRequests)

  const requestInfo = {
    requestId,
    locale,
    translatedBy: email,
    createdAt: new Date(),
    estimatedTime: calculateExportProcessTime(
      streamParsed.length,
      CALLS_PER_MINUTE
    ),
  }

  await vbase.saveJSON<UploadRequest>(bucket, requestId, requestInfo)

  uploadEntriesAsync<T>(
    {
      entries: streamParsed,
      requestId,
      locale,
      bucket,
      translateEntry,
    },
    { vbase }
  )

  return requestId
}

export const entryTranslations = async <T, X>(
  {
    entries,
    locale,
    bucket,
    path,
    categoryId,
    requestId,
    translateEntry,
  }: EntryTranslations<T>,
  ctx: Context
) => {
  const {
    clients: { catalogGQL, vbase, licenseManager },
    vtex: { adminUserAuthToken },
  } = ctx

  const {
    profile: { email },
  } = await licenseManager.getTopbarData(adminUserAuthToken as string)

  const allTranslationRequest = await vbase.getJSON<string[]>(
    bucket,
    path,
    true
  )

  const updateRequests = allTranslationRequest
    ? [requestId, ...allTranslationRequest]
    : [requestId]

  await vbase.saveJSON<string[]>(bucket, path, updateRequests)

  const requestInfo: TranslationRequest<X> = {
    requestId,
    requestedBy: email,
    locale,
    createdAt: new Date(),
    estimatedTime: calculateExportProcessTime(entries.length, CALLS_PER_MINUTE),
    ...(categoryId ? { categoryId } : ''),
  }

  await vbase.saveJSON<TranslationRequest<X>>(bucket, requestId, requestInfo)

  const params: ParamsTranslationsToVBase = {
    locale,
    requestId,
    bucket,
  }

  saveTranslationsEntriesToVBase<T, X>(
    {
      entries,
      params,
      getEntryTranslation: translateEntry,
    },
    {
      catalogGQLClient: catalogGQL,
      vbase,
    }
  )

  return requestInfo
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/resolvers/index.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  Category,
  queries as categoryQueries,
  mutations as categoryMutations,
} from './category'
import {
  Collection,
  queries as collectionQueries,
  mutations as collectionMutations,
} from './collection'
import {
  Product,
  queries as productQueries,
  mutations as productMutations,
} from './product'
import { SKU, queries as skuQueries, mutations as skuMutations } from './sku'
import {
  Brand,
  queries as brandQueries,
  mutations as brandMutations,
} from './brand'
import {
  Field,
  queries as fieldsQueries,
  mutations as fieldMutations,
} from './field'
import { queries as translationUploadRequestInfo } from './translation'

export const queries = {
  ...categoryQueries,
  ...productQueries,
  ...skuQueries,
  ...brandQueries,
  ...translationUploadRequestInfo,
  ...collectionQueries,
  ...fieldsQueries,
}

export const resolvers = {
  Category,
  Product,
  SKU,
  Brand,
  Collection,
  Field,
}

export const mutations = {
  ...productMutations,
  ...skuMutations,
  ...brandMutations,
  ...categoryMutations,
  ...collectionMutations,
  ...fieldMutations,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/clients/utils/catalogGQLQueries.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
export const CATEGORIES_QUERY = `
  query GetCategories ($active: Boolean, $page: Int!) {
    categories(term:"*", page: $page, pageSize: 50, active: $active) {
      items {
        id
        name
      }
      paging {
        pages
      }
    }
  }
`

export const GET_CATEGORY_TRANSLATION_QUERY = `
  query getTranslation($id:ID!) {
    category(id: $id) {
      id
      name
      title
      description
      linkId
    }
  }
`
export const TRANSLATE_CATEGORY = `mutation translateCategory($category:CategoryInputTranslation!, $locale: Locale!) {
  translateCategory(category: $category, locale: $locale)
}`

export const GET_PRODUCT_TRANSLATION_QUERY = `
  query getProductTranslation($identifier: ProductUniqueIdentifier) {
    product(identifier: $identifier) {
      id
      name
      description
      shortDescription
      title
      linkId
    }
  }
`

export const GET_SKU_TRANSLATION_QUERY = `
  query getSKUTranslation($identifier: SKUUniqueIdentifier) {
    sku(identifier: $identifier) {
      id
      name
    }
  }
`

export const TRANSLATE_PRODUCT = `mutation translateProduct($product:ProductInputTranslation!, $locale: Locale!) {
  translateProduct(product: $product, locale: $locale)
}`

export const TRANSLATE_SKU = `mutation translateSKU($sku:SKUInputTranslation!, $locale: Locale!) {
  translateSKU(sku: $sku, locale: $locale)
}`

export const BRAND_QUERY = `
  query GetBrands ($page: Int!) {
    brands(term:"*", page: $page, pageSize: 50) {
      items {
        id
        name
        text
        siteTitle
        active
      }
      paging {
        pages
      }
    }
  }
`

export const GET_BRAND_TRANSLATION_QUERY = `
  query getTranslation($id:ID!) {
    brand(id: $id) {
      id
      name
      text
      siteTitle
      active
    }
  }
`

export const TRANSLATE_BRAND = `mutation translateBrand($brand:BrandInputTranslation!, $locale: Locale!) {
  translateBrand(brand: $brand, locale: $locale)
}`

export const COLLECTIONS_QUERY = `
  query GetCollections ($page: Int!) {
    collections(searchKey:"", page: $page, pageSize: 50) {
      items {
        id
        name
        status
      }
      paging {
        pages
      }
    }
  }
`

export const GET_COLLECTION_TRANSLATION_QUERY = `
  query getTranslation($id:ID!) {
    collection(id: $id) {
      id
      name
      status
    }
  }
`
export const GET_FIELD_TRANSLATION_QUERY = `query field($id:ID!) {
  field(id: $id){
    fieldId
    name
  }
}`

export const TRANSLATE_FIELD = `mutation translateField($field: FieldInputTranslation!, $locale: Locale!) {
  translateField(field: $field, locale: $locale)
}`

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/resolvers/collection/index.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  mutations as uploadMutations,
  queries as uploadQueries,
} from './upload'

export const Collection = {
  locale: (_root: unknown, _args: unknown, ctx: Context) => {
    return ctx.state.locale
  },
}

const collectionTranslations = async (
  _root: unknown,
  args: { locale: string; active?: boolean; xVtexTenant: string },
  ctx: Context
) => {
  const {
    clients: { catalogGQL, messagesGraphQL },
  } = ctx

  const { active, locale, xVtexTenant } = args

  ctx.state.locale = locale

  const collections = await catalogGQL.getCollections()

  const originalTranslation = collections.reduce<Record<string, string>>(
    (map, collection) => {
      return {
        [collection.id]: collection.name,
        ...map,
      }
    },
    {}
  )

  const messages = collections
    .filter(({ status }) => (active ? status === 'active' : true))
    .map(({ id, name }) => ({
      content: name,
      context: id,
    }))

  const userTranslations = await messagesGraphQL.userTranslations({
    from: xVtexTenant,
    messages,
  })

  const transformMessages = userTranslations.map(
    ({ context, translations }) => {
      const translatedName = translations.find(({ lang }) => lang === locale)
        ?.translation
      // If there is no translation, the value is set to be the original one, since
      // this is the normal behavior for the store when autmoatic translation is off.
      return {
        id: context,
        name: translatedName ?? originalTranslation[context as string],
      }
    }
  )

  return transformMessages
}

export const mutations = {
  ...uploadMutations,
}

export const queries = {
  collectionTranslations,
  ...uploadQueries,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/resolvers/collection/upload.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
/* eslint-disable no-await-in-loop */
import { ReadStream } from 'fs'

import { VBase, MessagesGraphQL, MessageSaveInput } from '@vtex/api'
import JSONStream from 'JSONStream'

import { CatalogGQL } from '../../clients/catalogGQL'
import {
  pacer,
  COLLECTION_BUCKET,
  calculateExportProcessTime,
  COLLECTION_TRANSLATION_UPLOAD,
  CALLS_PER_MINUTE,
  calculateBreakpoints,
} from '../../utils'

const translateMessagesCollection = async (
  {
    collections,
    requestId,
    locale,
    xVtexTenant,
  }: {
    collections: CollectionTranslationInput[]
    requestId: string
    locale: string
    xVtexTenant: string
  },
  {
    messages,
    catalogGQL,
    vbase,
  }: { messages: MessagesGraphQL; catalogGQL: CatalogGQL; vbase: VBase }
) => {
  const translationRequest = await vbase.getJSON<UploadRequest>(
    COLLECTION_BUCKET,
    requestId,
    true
  )

  try {
    const messagesToTranslate: MessageSaveInput[] = []
    const totalEntries = collections.length
    const breakPointsProgress = calculateBreakpoints(totalEntries)

    for (let i = 0; i < totalEntries; i++) {
      const { data } = await catalogGQL.getCollectionTranslation(
        collections[i].id,
        xVtexTenant
      )

      messagesToTranslate.push({
        srcLang: xVtexTenant,
        srcMessage: data?.collection.name ?? '',
        context: collections[i].id,
        targetMessage: collections[i].name ?? '',
      })

      await pacer(CALLS_PER_MINUTE)
      if (breakPointsProgress.includes(i)) {
        await vbase.saveJSON<UploadRequest>(COLLECTION_BUCKET, requestId, {
          ...translationRequest,
          progress: Math.ceil((i / totalEntries) * 100),
        })
      }
    }

    const saveMessagedPromise = messages.saveV2({
      to: locale,
      messages: messagesToTranslate,
    })

    const updateProgressPromise = vbase.saveJSON<UploadRequest>(
      COLLECTION_BUCKET,
      requestId,
      {
        ...translationRequest,
        progress: 100,
      }
    )

    await Promise.all([saveMessagedPromise, updateProgressPromise])
  } catch (error) {
    const translationRequestUpdated = await vbase.getJSON<UploadRequest>(
      COLLECTION_BUCKET,
      requestId,
      true
    )
    await vbase.saveJSON<UploadRequest>(COLLECTION_BUCKET, requestId, {
      ...translationRequestUpdated,
      error: true,
    })
  }
}

const parseStreamToJSON = <T>(stream: ReadStream): Promise<T[]> => {
  const promise = new Promise<T[]>((resolve) => {
    const finalArray: T[] = []
    stream.pipe(
      JSONStream.parse('*').on('data', (data: T) => {
        finalArray.push(data)
      })
    )
    stream.on('end', () => {
      stream.destroy()
      resolve(finalArray)
    })
  })

  return promise
}

const uploadCollectionTranslations = async (
  _root: unknown,
  {
    collections,
    locale,
    xVtexTenant,
  }: {
    collections: UploadFile<ReadStream>
    locale: string
    xVtexTenant: string
  },
  ctx: Context
) => {
  const {
    clients: { catalogGQL, messages, licenseManager, vbase },
    vtex: { adminUserAuthToken, requestId },
  } = ctx

  const { createReadStream } = await collections

  const collectionStream = createReadStream()

  const collectionsParsed = await parseStreamToJSON<CollectionTranslationInput>(
    collectionStream
  )

  const {
    profile: { email },
  } = await licenseManager.getTopbarData(adminUserAuthToken as string)

  const allTranslationsMade = await vbase.getJSON<string[]>(
    COLLECTION_BUCKET,
    COLLECTION_TRANSLATION_UPLOAD,
    true
  )

  const updateRequests = allTranslationsMade
    ? [requestId, ...allTranslationsMade]
    : [requestId]

  await vbase.saveJSON<string[]>(
    COLLECTION_BUCKET,
    COLLECTION_TRANSLATION_UPLOAD,
    updateRequests
  )

  const requestInfo = {
    requestId,
    locale,
    translatedBy: email,
    createdAt: new Date(),
    estimatedTime: calculateExportProcessTime(
      collectionsParsed.length,
      CALLS_PER_MINUTE
    ),
  }

  await vbase.saveJSON<UploadRequest>(COLLECTION_BUCKET, requestId, requestInfo)

  translateMessagesCollection(
    { collections: collectionsParsed, requestId, locale, xVtexTenant },
    { messages, catalogGQL, vbase }
  )

  return requestId
}

const collectionTranslationsUploadRequests = async (
  _root: unknown,
  _args: unknown,
  ctx: Context
) =>
  ctx.clients.vbase.getJSON<string[]>(
    COLLECTION_BUCKET,
    COLLECTION_TRANSLATION_UPLOAD,
    true
  )

export const mutations = { uploadCollectionTranslations }

export const queries = {
  collectionTranslationsUploadRequests,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/resolvers/category/index.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  mutations as uploadMutations,
  queries as uploadQueries,
} from './upload'

export const Category = {
  locale: (
    _root: ResolvedPromise<CategoryTranslationResponse>,
    _args: unknown,
    ctx: Context
  ) => {
    return ctx.state.locale
  },
  name: (root: ResolvedPromise<CategoryTranslationResponse>) =>
    root.data.category.name,
  title: (root: ResolvedPromise<CategoryTranslationResponse>) =>
    root.data.category.title,
  description: (root: ResolvedPromise<CategoryTranslationResponse>) =>
    root.data.category.description,
  id: (root: ResolvedPromise<CategoryTranslationResponse>) =>
    root.data.category.id,
  linkId: (root: ResolvedPromise<CategoryTranslationResponse>) =>
    root.data.category.linkId,
}

const categoryTranslations = async (
  _root: unknown,
  args: { locale: string; active?: boolean },
  ctx: Context
) => {
  const {
    clients: { catalogGQL },
  } = ctx

  const { active, locale } = args

  ctx.state.locale = locale

  const ids = await catalogGQL.getCategories(active)

  const translationsP = []

  for (const { id } of ids) {
    const promise = catalogGQL.getCategoryTranslation(id, locale)
    translationsP.push(promise)
  }

  const translations = await Promise.all(translationsP)

  return translations
}

const getCategoriesName = async (
  _root: unknown,
  _args: unknown,
  ctx: Context
) => {
  const {
    clients: { catalogGQL },
  } = ctx

  return catalogGQL.getCategories(false)
}

export const queries = {
  categoryTranslations,
  getCategoriesName,
  ...uploadQueries,
}

export const mutations = {
  ...uploadMutations,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/resolvers/category/upload.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ReadStream } from 'fs'

import {
  CATEGORY_BUCKET,
  calculateExportProcessTime,
  CATEGORY_TRANSLATION_UPLOAD,
  CALLS_PER_MINUTE,
  parseStreamToJSON,
  uploadEntriesAsync,
} from '../../utils'

const uploadCategoryTranslations = async (
  _root: unknown,
  {
    categories,
    locale,
  }: { categories: UploadFile<ReadStream>; locale: string },
  ctx: Context
) => {
  const {
    clients: { catalogGQL, licenseManager, vbase },
    vtex: { adminUserAuthToken, requestId },
  } = ctx

  const { createReadStream } = await categories

  const categoryStream = createReadStream()

  const categoriesParsed = await parseStreamToJSON<CategoryTranslationInput>(
    categoryStream
  )

  const {
    profile: { email },
  } = await licenseManager.getTopbarData(adminUserAuthToken as string)

  const allTranslationsMade = await vbase.getJSON<string[]>(
    CATEGORY_BUCKET,
    CATEGORY_TRANSLATION_UPLOAD,
    true
  )

  const updateRequests = allTranslationsMade
    ? [requestId, ...allTranslationsMade]
    : [requestId]

  await vbase.saveJSON<string[]>(
    CATEGORY_BUCKET,
    CATEGORY_TRANSLATION_UPLOAD,
    updateRequests
  )

  const requestInfo = {
    requestId,
    locale,
    translatedBy: email,
    createdAt: new Date(),
    estimatedTime: calculateExportProcessTime(
      categoriesParsed.length,
      CALLS_PER_MINUTE
    ),
  }

  await vbase.saveJSON<UploadRequest>(CATEGORY_BUCKET, requestId, requestInfo)

  uploadEntriesAsync<CategoryTranslationInput>(
    {
      entries: categoriesParsed,
      requestId,
      locale,
      bucket: CATEGORY_BUCKET,
      translateEntry: catalogGQL?.translateCategory,
    },
    { vbase }
  )

  return requestId
}

const categoryTranslationsUploadRequests = async (
  _root: unknown,
  _args: unknown,
  ctx: Context
) =>
  ctx.clients.vbase.getJSON<string[]>(
    CATEGORY_BUCKET,
    CATEGORY_TRANSLATION_UPLOAD,
    true
  )

export const mutations = {
  uploadCategoryTranslations,
}

export const queries = {
  categoryTranslationsUploadRequests,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/resolvers/translation/index.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
const translationUploadRequestInfo = (
  _root: unknown,
  args: { requestId: string; bucket: UploadBucket },
  ctx: Context
) => ctx.clients.vbase.getJSON<UploadRequest>(args?.bucket, args.requestId)

const translationRequestInfo = (
  _root: unknown,
  args: { requestId: string; bucket: DownloadBucket },
  ctx: Context
) => ctx.clients.vbase.getJSON(args?.bucket, args.requestId)

export const queries = {
  translationUploadRequestInfo,
  translationRequestInfo,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/resolvers/brand/index.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import {
  mutations as uploadMutations,
  queries as uploadQueries,
} from './upload'

export const Brand = {
  locale: (
    _root: ResolvedPromise<BrandTranslationResponse>,
    _args: unknown,
    ctx: Context
  ) => {
    return ctx.state.locale
  },
  id: (root: ResolvedPromise<BrandTranslationResponse>) => root.data.brand.id,
  name: (root: ResolvedPromise<BrandTranslationResponse>) =>
    root.data.brand.name,
  text: (root: ResolvedPromise<BrandTranslationResponse>) =>
    root.data.brand.text,
  siteTitle: (root: ResolvedPromise<BrandTranslationResponse>) =>
    root.data.brand.siteTitle,
  active: (root: ResolvedPromise<BrandTranslationResponse>) =>
    root.data.brand.active,
}

const brandTranslations = async (
  _root: unknown,
  args: { locale: string; active?: boolean },
  ctx: Context
) => {
  const {
    clients: { catalogGQL },
  } = ctx

  const { active, locale } = args

  ctx.state.locale = locale

  const brands = await catalogGQL.getBrands(active)
  const translationsP = []

  for (const { id } of brands) {
    const promise = catalogGQL.getBrandTranslation(id, locale)
    translationsP.push(promise)
  }

  const translations = await Promise.all(translationsP)

  return translations
}

export const queries = {
  brandTranslations,
  ...uploadQueries,
}

export const mutations = {
  ...uploadMutations,
}

```


[System] You are an expert VTEX IO developer. You specialize in building VTEX Store Framework apps, React components, GraphQL APIs, and Node.js services for e-commerce platforms.

[User] Show me the implementation of admin-catalog-translations/node/resolvers/brand/upload.ts from the admin-catalog-translations VTEX app.

[Assistant]
Here's the implementation:

```typescript
import { ReadStream } from 'fs'

import {
  BRAND_BUCKET,
  BRAND_TRANSLATION_UPLOAD,
  uploadTranslations,
} from '../../utils'

const uploadBrandTranslations = async (
  _root: unknown,
  { brands, locale }: { brands: UploadFile<ReadStream>; locale: string },
  ctx: Context
) => {
  const params: UploadTranslations<BrandTranslationInput> = {
    entries: brands,
    locale,
    bucket: BRAND_BUCKET,
    path: BRAND_TRANSLATION_UPLOAD,
    translateEntry: ctx?.clients?.catalogGQL?.translateBrand,
  }
  return uploadTranslations(params, ctx)
}

const brandTranslationsUploadRequests = async (
  _root: unknown,
  _args: unknown,
  ctx: Context
) =>
  ctx.clients.vbase.getJSON<string[]>(
    BRAND_BUCKET,
    BRAND_TRANSLATION_UPLOAD,
    true
  )

export const mutations = { uploadBrandTranslations }

export const queries = {
  brandTranslationsUploadRequests,
}

```