sanity-plugin-transifex 4.0.3 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  ## Installation
3
2
 
4
3
  ```sh
@@ -7,7 +6,6 @@ npm install sanity-plugin-transifex
7
6
 
8
7
  ![2023-02-15 10 06 06](https://user-images.githubusercontent.com/3969996/219130146-8d34c5a6-f11c-4647-826d-e3c07eaf7144.gif)
9
8
 
10
-
11
9
  # Sanity + Transifex = 🌍
12
10
 
13
11
  This plugin provides an in-studio integration with [Transifex](https://transifex.com). It allows your editors to send any document to Transifex with the click of a button, monitor ongoing translations, and import partial or complete translations back into the studio.
@@ -31,7 +29,7 @@ _Recent updates for v4:_ We've added support for the new document internationali
31
29
  npm install sanity-plugin-transifex
32
30
  ```
33
31
 
34
- 2. Ensure the plugin has access to your Transifex secrets. You'll want to create a document that includes your project name, organization name, and a token with appropriate access.
32
+ 2. Ensure the plugin has access to your Transifex secrets. You'll want to create a document that includes your project name, organization name, and a token with appropriate access.
35
33
 
36
34
  [Please refer to the Transifex documentation on creating a token if you don't have one already.](https://docs.transifex.com/account/authentication)
37
35
 
@@ -53,30 +51,30 @@ client.createOrReplace({
53
51
  // Replace these with your values
54
52
  organization: 'YOUR_TRANSIFEX_ORG_HERE',
55
53
  project: 'YOUR_TRANSIFEX_PROJECT_HERE',
56
- token: 'YOUR_TRANSIFEX_TOKEN_HERE'
54
+ token: 'YOUR_TRANSIFEX_TOKEN_HERE',
57
55
  })
58
56
  ```
59
57
 
60
- On the command line, run the file:
58
+ On the command line, run the file:
61
59
 
62
60
  ```sh
63
61
  npx sanity exec populateTransifexSecrets.js --with-user-token
64
62
  ```
65
-
63
+
66
64
  Verify that the document was created using the Vision Tool in the Studio and query `*[_id == 'transifex.secrets']`. Note: If you have multiple datasets, you'll have to do this across all of them.
67
65
 
68
- If the document was found in your dataset(s), delete `populateTransifexSecrets.js`.
66
+ If the document was found in your dataset(s), delete `populateTransifexSecrets.js`.
69
67
 
70
68
  If you have concerns about this being exposed to authenticated users of your studio, you can control access to this path with [role-based access control](https://www.sanity.io/docs/access-control).
71
69
 
72
- 4. Get the Transifex tab on your desired document type, using whatever pattern you like. You'll use the [desk structure](https://www.sanity.io/docs/structure-builder-introduction) for this. The options for translation will be nested under this desired document type's views. Here's an example:
70
+ 3. Get the Transifex tab on your desired document type, using whatever pattern you like. You'll use the [desk structure](https://www.sanity.io/docs/structure-builder-introduction) for this. The options for translation will be nested under this desired document type's views. Here's an example:
73
71
 
74
72
  ```javascript
75
73
  import {DefaultDocumentNodeResolver} from 'sanity/desk'
76
74
  //...your other desk structure imports...
77
75
  import {TranslationsTab, defaultDocumentLevelConfig} from 'sanity-plugin-transifex'
78
76
  //if you are using field-level translations, you can import the field-level config instead:
79
- //import {TranslationsTab, defaultFieldLevelConfig} from 'sanity-plugin-studio-smartling'
77
+ //import {TranslationsTab, defaultFieldLevelConfig} from 'sanity-plugin-transifex'
80
78
  //if you're not sure which, please look at the document-level and field-level sections below
81
79
 
82
80
  export const defaultDocumentNode: DefaultDocumentNodeResolver = (S, {schemaType}) => {
@@ -140,7 +138,7 @@ There are several reasons to override these functions. More general cases are of
140
138
 
141
139
  ## Migrating to Sanity Studio v3
142
140
 
143
- You should not have to do anything to migrate to Sanity Studio v3. If you are using the default configs, you should be able to upgrade without any changes. If you are using custom serialization, you may need to update how `BaseDocumentSerializer` receives your schema.
141
+ You should not have to do anything to migrate to Sanity Studio v3. If you are using the default configs, you should be able to upgrade without any changes. If you are using custom serialization, you may need to update how `BaseDocumentSerializer` receives your schema.
144
142
 
145
143
  These are outlined in the serializer README [here](https://github.com/sanity-io/sanity-naive-html-serializer#v2-to-v3-changes).
146
144
 
@@ -160,7 +158,6 @@ on how to run this plugin with hotreload in the studio.
160
158
 
161
159
  ### Release new version
162
160
 
163
- Run ["CI & Release" workflow](https://github.com/sanity-io/sanity-plugin-transifex/actions/workflows/main.yml).
164
- Make sure to select the main branch and check "Release new version".
161
+ Releases are handled from the [`sanity-io/plugins`](https://github.com/sanity-io/plugins) monorepo using Changesets.
165
162
 
166
- Semantic release will only release on configured branches, so it is safe to run release on any branch.
163
+ Add a changeset in your PR, then follow the monorepo release workflow to publish from `main`.
package/dist/index.d.ts CHANGED
@@ -1,57 +1,13 @@
1
- import {Adapter} from 'sanity-translations-tab'
2
- import {BaseDocumentDeserializer} from 'sanity-translations-tab'
3
- import {BaseDocumentMerger} from 'sanity-translations-tab'
4
- import {BaseDocumentSerializer} from 'sanity-translations-tab'
5
- import {customSerializers} from 'sanity-translations-tab'
6
- import {defaultStopTypes} from 'sanity-translations-tab'
7
- import {documentLevelPatch} from 'sanity-translations-tab'
8
- import {fieldLevelPatch} from 'sanity-translations-tab'
9
- import {findLatestDraft} from 'sanity-translations-tab'
10
- import {legacyDocumentLevelPatch} from 'sanity-translations-tab'
11
- import {TranslationFunctionContext} from 'sanity-translations-tab'
12
- import {TranslationsTab} from 'sanity-translations-tab'
13
-
14
- export {BaseDocumentDeserializer}
15
-
16
- export {BaseDocumentMerger}
17
-
18
- export {BaseDocumentSerializer}
19
-
20
- declare interface ConfigOptions {
21
- adapter: Adapter
22
- secretsNamespace: string | null
23
- exportForTranslation: (
24
- id: string,
25
- context: TranslationFunctionContext,
26
- ) => Promise<Record<string, any>>
27
- importTranslation: (
28
- id: string,
29
- localeId: string,
30
- doc: string,
31
- context: TranslationFunctionContext,
32
- ) => Promise<void>
1
+ import { Adapter, BaseDocumentDeserializer, BaseDocumentMerger, BaseDocumentSerializer, TranslationFunctionContext, TranslationsTab, customSerializers, defaultStopTypes, documentLevelPatch, fieldLevelPatch, findLatestDraft, legacyDocumentLevelPatch } from "sanity-translations-tab";
2
+ declare const TransifexAdapter: Adapter;
3
+ interface ConfigOptions {
4
+ adapter: Adapter;
5
+ secretsNamespace: string | null;
6
+ exportForTranslation: (id: string, context: TranslationFunctionContext) => Promise<Record<string, any>>;
7
+ importTranslation: (id: string, localeId: string, doc: string, context: TranslationFunctionContext) => Promise<void>;
33
8
  }
34
-
35
- export {customSerializers}
36
-
37
- export declare const defaultDocumentLevelConfig: ConfigOptions
38
-
39
- export declare const defaultFieldLevelConfig: ConfigOptions
40
-
41
- export {defaultStopTypes}
42
-
43
- export {documentLevelPatch}
44
-
45
- export {fieldLevelPatch}
46
-
47
- export {findLatestDraft}
48
-
49
- export declare const legacyDocumentLevelConfig: ConfigOptions
50
-
51
- export {legacyDocumentLevelPatch}
52
-
53
- export declare const TransifexAdapter: Adapter
54
-
55
- export {TranslationsTab}
56
-
57
- export {}
9
+ declare const defaultDocumentLevelConfig: ConfigOptions;
10
+ declare const legacyDocumentLevelConfig: ConfigOptions;
11
+ declare const defaultFieldLevelConfig: ConfigOptions;
12
+ export { BaseDocumentDeserializer, BaseDocumentMerger, BaseDocumentSerializer, TransifexAdapter, TranslationsTab, customSerializers, defaultDocumentLevelConfig, defaultFieldLevelConfig, defaultStopTypes, documentLevelPatch, fieldLevelPatch, findLatestDraft, legacyDocumentLevelConfig, legacyDocumentLevelPatch };
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/transifexAdapter/index.ts","../src/index.ts"],"mappings":";cAOa,gBAAA,EAAkB,OAAA;AAAA,UCerB,aAAA;EACR,OAAA,EAAS,OAAA;EACT,gBAAA;EACA,oBAAA,GACE,EAAA,UACA,OAAA,EAAS,0BAAA,KACN,OAAA,CAAQ,MAAA;EACb,iBAAA,GACE,EAAA,UACA,QAAA,UACA,GAAA,UACA,OAAA,EAAS,0BAAA,KACN,OAAA;AAAA;AAAA,cAED,0BAAA,EAA4B,aAAA;AAAA,cAM5B,yBAAA,EAA2B,aAAA;AAAA,cAM3B,uBAAA,EAAyB,aAAA"}
package/dist/index.js CHANGED
@@ -1,20 +1,23 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: !0 });
3
- var sanityTranslationsTab = require("sanity-translations-tab");
1
+ import { baseDocumentLevelConfig, baseFieldLevelConfig, legacyDocumentLevelConfig as legacyDocumentLevelConfig$1 } from "sanity-translations-tab";
2
+ import { BaseDocumentDeserializer, BaseDocumentMerger, BaseDocumentSerializer, TranslationsTab, customSerializers, defaultStopTypes, documentLevelPatch, fieldLevelPatch, findLatestDraft, legacyDocumentLevelPatch } from "sanity-translations-tab";
4
3
  const baseTransifexUrl = "https://rest.api.transifex.com", getHeaders = (secrets) => ({
5
4
  Authorization: `Bearer ${secrets?.token}`,
6
5
  "Content-Type": "application/vnd.api+json"
7
6
  }), projOrgSlug = (secrets) => `o:${secrets?.organization}:p:${secrets?.project}`, getLocales = async (secrets) => {
8
7
  let locales = [];
9
- return secrets && (locales = await fetch(`${baseTransifexUrl}/projects/${projOrgSlug(secrets)}/languages`, {
10
- headers: getHeaders(secrets)
11
- }).then((res) => res.json()).then(
12
- (res) => res.data.map((lang) => ({
8
+ if (secrets) {
9
+ const response = await fetch(`${baseTransifexUrl}/projects/${projOrgSlug(secrets)}/languages`, {
10
+ headers: getHeaders(secrets)
11
+ });
12
+ if (!response.ok)
13
+ throw Error(`Failed to retrieve locales from Transifex. Status: ${response.status}`);
14
+ locales = await response.json().then((res) => res.data.map((lang) => ({
13
15
  enabled: !0,
14
16
  description: lang.attributes.name,
15
17
  localeId: lang.attributes.code
16
- }))
17
- )), locales;
18
+ })));
19
+ }
20
+ return locales;
18
21
  }, getTranslationTask = async (documentId, secrets) => {
19
22
  if (!documentId || !secrets)
20
23
  return {
@@ -22,74 +25,29 @@ const baseTransifexUrl = "https://rest.api.transifex.com", getHeaders = (secrets
22
25
  documentId,
23
26
  locales: []
24
27
  };
25
- const projectFilter = `filter[project]=${projOrgSlug(secrets)}`, resourceFilter = `filter[resource]=${projOrgSlug(secrets)}:r:${documentId}`, task = await fetch(
26
- `${baseTransifexUrl}/resource_language_stats?${projectFilter}&${resourceFilter}`,
27
- { headers: getHeaders(secrets) }
28
- ).then((res) => {
28
+ const projectFilter = `filter[project]=${projOrgSlug(secrets)}`, resourceFilter = `filter[resource]=${projOrgSlug(secrets)}:r:${documentId}`, task = await fetch(`${baseTransifexUrl}/resource_language_stats?${projectFilter}&${resourceFilter}`, {
29
+ headers: getHeaders(secrets)
30
+ }).then((res) => {
29
31
  if (res.ok)
30
32
  return res.json();
31
33
  if (res.status === 404)
32
- return { data: [] };
34
+ return {
35
+ data: []
36
+ };
33
37
  throw Error(`Failed to retrieve tasks from Transifex. Status: ${res.status}`);
34
38
  }).then((res) => ({
35
39
  taskId: `${projOrgSlug(secrets)}:r:${documentId}`,
36
40
  documentId,
37
- locales: res.data.map((locale) => ({
38
- localeId: locale.relationships.language.data.id.split(":")[1],
39
- progress: Math.floor(
40
- 100 * (locale.attributes.reviewed_strings / parseFloat(locale.attributes.total_strings))
41
- )
42
- }))
43
- })), localeIds = (await getLocales(secrets)).map((l) => l.localeId), validLocales = task.locales.filter(
44
- (locale) => localeIds.find((id) => id === locale.localeId)
45
- );
41
+ locales: res.data.map((locale) => {
42
+ const reviewedStrings = Number(locale.attributes.reviewed_strings), totalStrings = Number(locale.attributes.total_strings);
43
+ return {
44
+ localeId: locale.relationships.language.data.id.split(":")[1],
45
+ progress: Number.isFinite(reviewedStrings) && Number.isFinite(totalStrings) && totalStrings > 0 ? Math.floor(100 * reviewedStrings / totalStrings) : 0
46
+ };
47
+ })
48
+ })), localeIds = (await getLocales(secrets)).map((l) => l.localeId), validLocales = task.locales.filter((locale) => localeIds.find((id) => id === locale.localeId));
46
49
  return task.locales = validLocales, task;
47
- }, pollForFileDownloadLocation = async (resourceDownloadUrl, translationDownloadId, headers) => {
48
- const response = await fetch(`${resourceDownloadUrl}/${translationDownloadId}`, {
49
- headers
50
- });
51
- return response.status === 500 ? (console.info(
52
- `Transifex plugin message: Received 500 for translation download ID ${translationDownloadId}. Trying to reconnect...`
53
- ), await new Promise((resolve) => setTimeout(resolve, 3e3)), pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)) : response.redirected ? (console.info(
54
- `Transifex plugin message: Received redirect for translation download ID ${translationDownloadId}. Following redirect now for file download.`
55
- ), response.url) : response.status === 200 ? (console.info(
56
- `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId}. Location is still pending, trying again.`
57
- ), await new Promise((resolve) => setTimeout(resolve, 3e3)), pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)) : (console.error(
58
- `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId} but received error code ${response.status}. Waiting and trying again.`
59
- ), await new Promise((resolve) => setTimeout(resolve, 3e3)), pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers));
60
- }, handleFileDownload = (url) => fetch(url).then((res) => res.text()), getTranslation = async (taskId, localeId, secrets) => {
61
- const resourceDownloadBody = {
62
- data: {
63
- attributes: {
64
- content_encoding: "text"
65
- },
66
- relationships: {
67
- language: {
68
- data: {
69
- id: `l:${localeId}`,
70
- type: "languages"
71
- }
72
- },
73
- resource: {
74
- data: {
75
- id: taskId,
76
- type: "resources"
77
- }
78
- }
79
- },
80
- type: "resource_translations_async_downloads"
81
- }
82
- }, resourceDownloadUrl = `${baseTransifexUrl}/resource_translations_async_downloads`, translationDownloadId = await fetch(resourceDownloadUrl, {
83
- headers: getHeaders(secrets),
84
- method: "POST",
85
- body: JSON.stringify(resourceDownloadBody)
86
- }).then((res) => res.json()).then((res) => res.data.id), headers = getHeaders(secrets), location = await pollForFileDownloadLocation(
87
- resourceDownloadUrl,
88
- translationDownloadId,
89
- headers
90
- );
91
- return handleFileDownload(location);
92
- }, createResource = (doc, documentId, secrets) => {
50
+ }, createResource = async (doc, documentId, secrets) => {
93
51
  const resourceCreateBody = {
94
52
  data: {
95
53
  attributes: {
@@ -113,17 +71,31 @@ const baseTransifexUrl = "https://rest.api.transifex.com", getHeaders = (secrets
113
71
  },
114
72
  type: "resources"
115
73
  }
116
- };
117
- return fetch(`${baseTransifexUrl}/resources`, {
74
+ }, response = await fetch(`${baseTransifexUrl}/resources`, {
118
75
  headers: getHeaders(secrets),
119
76
  method: "POST",
120
77
  body: JSON.stringify(resourceCreateBody)
121
- }).then((res) => res.json()).then((res) => res.data.id);
122
- }, createTask = async (documentId, document, localeIds, secrets) => {
123
- let resourceId = await fetch(
124
- `${baseTransifexUrl}/resources/${projOrgSlug(secrets)}:r:${documentId}`,
125
- { headers: getHeaders(secrets) }
126
- ).then((res) => res.json()).then((res) => res.data ? res.data.id : null);
78
+ });
79
+ if (!response.ok)
80
+ throw Error(`Failed to create Transifex resource. Status: ${response.status}`);
81
+ const resourceId = (await response.json())?.data?.id;
82
+ if (!resourceId)
83
+ throw Error("Failed to create Transifex resource. Missing resource id in response.");
84
+ return resourceId;
85
+ }, createTask = async (documentId, document, _localeIds, secrets) => {
86
+ if (!documentId || !secrets)
87
+ throw Error("Missing documentId or Transifex secrets.");
88
+ const resourceResponse = await fetch(`${baseTransifexUrl}/resources/${projOrgSlug(secrets)}:r:${documentId}`, {
89
+ headers: getHeaders(secrets)
90
+ });
91
+ let resourceId = null;
92
+ if (resourceResponse.status === 404)
93
+ resourceId = null;
94
+ else if (resourceResponse.ok) {
95
+ const resource = await resourceResponse.json();
96
+ resourceId = resource.data ? resource.data.id : null;
97
+ } else
98
+ throw Error(`Failed to retrieve Transifex resource. Status: ${resourceResponse.status}`);
127
99
  resourceId || (resourceId = await createResource(document, documentId, secrets));
128
100
  const resourceUploadUrl = `${baseTransifexUrl}/resource_strings_async_uploads`, resourceUploadBody = {
129
101
  data: {
@@ -141,92 +113,97 @@ const baseTransifexUrl = "https://rest.api.transifex.com", getHeaders = (secrets
141
113
  },
142
114
  type: "resource_strings_async_uploads"
143
115
  }
144
- };
145
- return fetch(resourceUploadUrl, {
116
+ }, uploadResponse = await fetch(resourceUploadUrl, {
146
117
  method: "POST",
147
118
  body: JSON.stringify(resourceUploadBody),
148
119
  headers: getHeaders(secrets)
149
- }).then(() => getTranslationTask(documentId, secrets));
120
+ });
121
+ if (!uploadResponse.ok)
122
+ throw Error(`Failed to upload resource strings to Transifex. Status: ${uploadResponse.status}`);
123
+ return getTranslationTask(documentId, secrets);
124
+ }, pollForFileDownloadLocation = async (resourceDownloadUrl, translationDownloadId, headers, retryCount = 0, maxRetries = 20) => {
125
+ const response = await fetch(`${resourceDownloadUrl}/${translationDownloadId}`, {
126
+ headers
127
+ });
128
+ if (retryCount >= maxRetries)
129
+ throw Error(`Failed to retrieve download location for translation download ID ${translationDownloadId} after ${maxRetries} retries.`);
130
+ if (response.status === 500)
131
+ return console.info(`Transifex plugin message: Received 500 for translation download ID ${translationDownloadId}. Trying to reconnect...`), await new Promise((resolve) => setTimeout(resolve, 3e3)), pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers, retryCount + 1, maxRetries);
132
+ if (response.redirected)
133
+ return console.info(`Transifex plugin message: Received redirect for translation download ID ${translationDownloadId}. Following redirect now for file download.`), response.url;
134
+ if (response.status === 200)
135
+ return console.info(`Transifex plugin message: Requested download location for translation download ID ${translationDownloadId}. Location is still pending, trying again.`), await new Promise((resolve) => setTimeout(resolve, 3e3)), pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers, retryCount + 1, maxRetries);
136
+ if (response.status === 401 || response.status === 403)
137
+ throw Error(`Failed to retrieve download location for translation download ID ${translationDownloadId}. Status: ${response.status}`);
138
+ return console.error(`Transifex plugin message: Requested download location for translation download ID ${translationDownloadId} but received error code ${response.status}. Waiting and trying again.`), await new Promise((resolve) => setTimeout(resolve, 3e3)), pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers, retryCount + 1, maxRetries);
139
+ }, handleFileDownload = (url) => fetch(url).then((res) => res.text()), getTranslation = async (taskId, localeId, secrets) => {
140
+ if (!secrets)
141
+ throw Error("Missing Transifex secrets.");
142
+ const resourceDownloadBody = {
143
+ data: {
144
+ attributes: {
145
+ content_encoding: "text"
146
+ },
147
+ relationships: {
148
+ language: {
149
+ data: {
150
+ id: `l:${localeId}`,
151
+ type: "languages"
152
+ }
153
+ },
154
+ resource: {
155
+ data: {
156
+ id: taskId,
157
+ type: "resources"
158
+ }
159
+ }
160
+ },
161
+ type: "resource_translations_async_downloads"
162
+ }
163
+ }, resourceDownloadUrl = `${baseTransifexUrl}/resource_translations_async_downloads`, downloadResponse = await fetch(resourceDownloadUrl, {
164
+ headers: getHeaders(secrets),
165
+ method: "POST",
166
+ body: JSON.stringify(resourceDownloadBody)
167
+ });
168
+ if (!downloadResponse.ok)
169
+ throw Error(`Failed to create translation download request in Transifex. Status: ${downloadResponse.status}`);
170
+ const translationDownloadId = (await downloadResponse.json())?.data?.id;
171
+ if (!translationDownloadId)
172
+ throw Error("Failed to create translation download request in Transifex. Missing download id.");
173
+ const headers = getHeaders(secrets), location = await pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers);
174
+ return handleFileDownload(location);
150
175
  }, TransifexAdapter = {
151
176
  getLocales,
152
177
  getTranslationTask,
153
178
  createTask,
154
179
  getTranslation
155
180
  }, defaultDocumentLevelConfig = {
156
- ...sanityTranslationsTab.baseDocumentLevelConfig,
181
+ ...baseDocumentLevelConfig,
157
182
  adapter: TransifexAdapter,
158
183
  secretsNamespace: "transifex"
159
184
  }, legacyDocumentLevelConfig = {
160
- ...sanityTranslationsTab.legacyDocumentLevelConfig,
185
+ ...legacyDocumentLevelConfig$1,
161
186
  adapter: TransifexAdapter,
162
187
  secretsNamespace: "transifex"
163
188
  }, defaultFieldLevelConfig = {
164
- ...sanityTranslationsTab.baseFieldLevelConfig,
189
+ ...baseFieldLevelConfig,
165
190
  adapter: TransifexAdapter,
166
191
  secretsNamespace: "transifex"
167
192
  };
168
- Object.defineProperty(exports, "BaseDocumentDeserializer", {
169
- enumerable: !0,
170
- get: function() {
171
- return sanityTranslationsTab.BaseDocumentDeserializer;
172
- }
173
- });
174
- Object.defineProperty(exports, "BaseDocumentMerger", {
175
- enumerable: !0,
176
- get: function() {
177
- return sanityTranslationsTab.BaseDocumentMerger;
178
- }
179
- });
180
- Object.defineProperty(exports, "BaseDocumentSerializer", {
181
- enumerable: !0,
182
- get: function() {
183
- return sanityTranslationsTab.BaseDocumentSerializer;
184
- }
185
- });
186
- Object.defineProperty(exports, "TranslationsTab", {
187
- enumerable: !0,
188
- get: function() {
189
- return sanityTranslationsTab.TranslationsTab;
190
- }
191
- });
192
- Object.defineProperty(exports, "customSerializers", {
193
- enumerable: !0,
194
- get: function() {
195
- return sanityTranslationsTab.customSerializers;
196
- }
197
- });
198
- Object.defineProperty(exports, "defaultStopTypes", {
199
- enumerable: !0,
200
- get: function() {
201
- return sanityTranslationsTab.defaultStopTypes;
202
- }
203
- });
204
- Object.defineProperty(exports, "documentLevelPatch", {
205
- enumerable: !0,
206
- get: function() {
207
- return sanityTranslationsTab.documentLevelPatch;
208
- }
209
- });
210
- Object.defineProperty(exports, "fieldLevelPatch", {
211
- enumerable: !0,
212
- get: function() {
213
- return sanityTranslationsTab.fieldLevelPatch;
214
- }
215
- });
216
- Object.defineProperty(exports, "findLatestDraft", {
217
- enumerable: !0,
218
- get: function() {
219
- return sanityTranslationsTab.findLatestDraft;
220
- }
221
- });
222
- Object.defineProperty(exports, "legacyDocumentLevelPatch", {
223
- enumerable: !0,
224
- get: function() {
225
- return sanityTranslationsTab.legacyDocumentLevelPatch;
226
- }
227
- });
228
- exports.TransifexAdapter = TransifexAdapter;
229
- exports.defaultDocumentLevelConfig = defaultDocumentLevelConfig;
230
- exports.defaultFieldLevelConfig = defaultFieldLevelConfig;
231
- exports.legacyDocumentLevelConfig = legacyDocumentLevelConfig;
193
+ export {
194
+ BaseDocumentDeserializer,
195
+ BaseDocumentMerger,
196
+ BaseDocumentSerializer,
197
+ TransifexAdapter,
198
+ TranslationsTab,
199
+ customSerializers,
200
+ defaultDocumentLevelConfig,
201
+ defaultFieldLevelConfig,
202
+ defaultStopTypes,
203
+ documentLevelPatch,
204
+ fieldLevelPatch,
205
+ findLatestDraft,
206
+ legacyDocumentLevelConfig,
207
+ legacyDocumentLevelPatch
208
+ };
232
209
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/transifexAdapter/helpers.ts","../src/transifexAdapter/getLocales.ts","../src/transifexAdapter/getTranslationTask.ts","../src/transifexAdapter/getTranslation.ts","../src/transifexAdapter/createTask.ts","../src/transifexAdapter/index.ts","../src/index.ts"],"sourcesContent":["import {Secrets} from 'sanity-translations-tab'\n\nexport const baseTransifexUrl = 'https://rest.api.transifex.com'\n\nexport const getHeaders = (secrets: Secrets | null): Record<string, string> => ({\n Authorization: `Bearer ${secrets?.token}`,\n 'Content-Type': 'application/vnd.api+json',\n})\n\nexport const projOrgSlug = (secrets: Secrets | null): string =>\n `o:${secrets?.organization}:p:${secrets?.project}`\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nexport const getLocales: Adapter['getLocales'] = async (secrets: Secrets | null) => {\n let locales = []\n if (secrets) {\n locales = await fetch(`${baseTransifexUrl}/projects/${projOrgSlug(secrets)}/languages`, {\n headers: getHeaders(secrets),\n })\n .then((res) => res.json())\n .then((res) =>\n res.data.map((lang: Record<string, any>) => ({\n enabled: true,\n description: lang.attributes.name,\n localeId: lang.attributes.code,\n }))\n )\n }\n return locales\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\nimport {getLocales} from './getLocales'\n\nexport const getTranslationTask: Adapter['getTranslationTask'] = async (\n documentId: string,\n secrets: Secrets | null\n) => {\n if (!documentId || !secrets) {\n return {\n taskId: documentId,\n documentId: documentId,\n locales: [],\n }\n }\n const projectFilter = `filter[project]=${projOrgSlug(secrets)}`\n const resourceFilter = `filter[resource]=${projOrgSlug(secrets)}:r:${documentId}`\n const task = await fetch(\n `${baseTransifexUrl}/resource_language_stats?${projectFilter}&${resourceFilter}`,\n {headers: getHeaders(secrets)}\n )\n .then((res) => {\n if (res.ok) {\n return res.json()\n }\n //normal -- just means that this task doesn't exist yet.\n else if (res.status === 404) {\n return {data: []}\n }\n throw Error(`Failed to retrieve tasks from Transifex. Status: ${res.status}`)\n })\n .then((res) => ({\n taskId: `${projOrgSlug(secrets)}:r:${documentId}`,\n documentId: documentId,\n locales: res.data.map((locale: Record<string, any>) => ({\n localeId: locale.relationships.language.data.id.split(':')[1],\n progress: Math.floor(\n 100 * (locale.attributes.reviewed_strings / parseFloat(locale.attributes.total_strings))\n ),\n })),\n }))\n\n const locales = await getLocales(secrets)\n const localeIds = locales.map((l: Record<string, any>) => l.localeId)\n const validLocales = task.locales.filter((locale: Record<string, any>) =>\n localeIds.find((id: string) => id === locale.localeId)\n )\n task.locales = validLocales\n\n return task\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, getHeaders} from './helpers'\n\nconst pollForFileDownloadLocation = async (\n resourceDownloadUrl: string,\n translationDownloadId: string,\n headers: Record<string, any>\n): Promise<string> => {\n const response = await fetch(`${resourceDownloadUrl}/${translationDownloadId}`, {\n headers: headers,\n })\n\n if (response.status === 500) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Received 500 for translation download ID ${translationDownloadId}. Trying to reconnect...`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n } else if (response.redirected) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Received redirect for translation download ID ${translationDownloadId}. Following redirect now for file download.`\n )\n return response.url\n } else if (response.status === 200) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId}. Location is still pending, trying again.`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n }\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.error(\n `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId} but received error code ${response.status}. Waiting and trying again.`\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(resourceDownloadUrl, translationDownloadId, headers)\n}\n\nconst handleFileDownload = (url: string) => {\n return fetch(url).then((res) => res.text())\n}\n\nexport const getTranslation: Adapter['getTranslation'] = async (\n taskId: string,\n localeId: string,\n secrets: Secrets | null\n) => {\n const resourceDownloadBody = {\n data: {\n attributes: {\n content_encoding: 'text',\n },\n relationships: {\n language: {\n data: {\n id: `l:${localeId}`,\n type: 'languages',\n },\n },\n resource: {\n data: {\n id: taskId,\n type: 'resources',\n },\n },\n },\n type: 'resource_translations_async_downloads',\n },\n }\n\n const resourceDownloadUrl = `${baseTransifexUrl}/resource_translations_async_downloads`\n const translationDownloadId = await fetch(resourceDownloadUrl, {\n headers: getHeaders(secrets),\n method: 'POST',\n body: JSON.stringify(resourceDownloadBody),\n })\n .then((res) => res.json())\n .then((res) => res.data.id)\n\n const headers = getHeaders(secrets)\n const location = await pollForFileDownloadLocation(\n resourceDownloadUrl,\n translationDownloadId,\n headers\n )\n return handleFileDownload(location)\n}\n","import {Adapter, Secrets} from 'sanity-translations-tab'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\nimport {getTranslationTask} from './getTranslationTask'\n\nconst createResource = (doc: Record<string, any>, documentId: string, secrets: Secrets | null) => {\n const resourceCreateBody = {\n data: {\n attributes: {\n accept_translations: true,\n name: doc.name,\n slug: documentId,\n },\n relationships: {\n i18n_format: {\n data: {\n id: 'HTML_FRAGMENT',\n type: 'i18n_formats',\n },\n },\n project: {\n data: {\n id: projOrgSlug(secrets),\n type: 'projects',\n },\n },\n },\n type: 'resources',\n },\n }\n\n return fetch(`${baseTransifexUrl}/resources`, {\n headers: getHeaders(secrets),\n method: 'POST',\n body: JSON.stringify(resourceCreateBody),\n })\n .then((res) => res.json())\n .then((res) => res.data.id)\n}\n\n//@ts-ignore until we resolve the TranslationTask return type\nexport const createTask: Adapter['createTask'] = async (\n documentId: string,\n document: Record<string, any>,\n localeIds: string[],\n secrets: Secrets | null\n) => {\n let resourceId = await fetch(\n `${baseTransifexUrl}/resources/${projOrgSlug(secrets)}:r:${documentId}`,\n {headers: getHeaders(secrets)}\n )\n .then((res) => res.json())\n .then((res) => (res.data ? res.data.id : null))\n\n if (!resourceId) {\n resourceId = await createResource(document, documentId, secrets)\n }\n\n const resourceUploadUrl = `${baseTransifexUrl}/resource_strings_async_uploads`\n const resourceUploadBody = {\n data: {\n attributes: {\n content: document.content,\n content_encoding: 'text',\n },\n relationships: {\n resource: {\n data: {\n id: resourceId,\n type: 'resources',\n },\n },\n },\n type: 'resource_strings_async_uploads',\n },\n }\n\n return fetch(resourceUploadUrl, {\n method: 'POST',\n body: JSON.stringify(resourceUploadBody),\n headers: getHeaders(secrets),\n }).then(() => getTranslationTask(documentId, secrets))\n}\n","import {Adapter} from 'sanity-translations-tab'\n\nimport {getLocales} from './getLocales'\nimport {getTranslationTask} from './getTranslationTask'\nimport {getTranslation} from './getTranslation'\nimport {createTask} from './createTask'\n\nexport const TransifexAdapter: Adapter = {\n getLocales,\n getTranslationTask,\n createTask,\n getTranslation,\n}\n","import {\n baseDocumentLevelConfig,\n legacyDocumentLevelConfig as baseLegacyDocumentLevelConfig,\n baseFieldLevelConfig,\n Adapter,\n TranslationFunctionContext,\n} from 'sanity-translations-tab'\nimport {TransifexAdapter} from './transifexAdapter'\n\nexport {\n findLatestDraft,\n BaseDocumentDeserializer,\n BaseDocumentSerializer,\n BaseDocumentMerger,\n defaultStopTypes,\n customSerializers,\n legacyDocumentLevelPatch,\n documentLevelPatch,\n fieldLevelPatch,\n TranslationsTab,\n} from 'sanity-translations-tab'\n\ninterface ConfigOptions {\n adapter: Adapter\n secretsNamespace: string | null\n exportForTranslation: (\n id: string,\n context: TranslationFunctionContext\n ) => Promise<Record<string, any>>\n importTranslation: (\n id: string,\n localeId: string,\n doc: string,\n context: TranslationFunctionContext\n ) => Promise<void>\n}\nconst defaultDocumentLevelConfig: ConfigOptions = {\n ...baseDocumentLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nconst legacyDocumentLevelConfig: ConfigOptions = {\n ...baseLegacyDocumentLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nconst defaultFieldLevelConfig: ConfigOptions = {\n ...baseFieldLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nexport {\n TransifexAdapter,\n defaultDocumentLevelConfig,\n defaultFieldLevelConfig,\n legacyDocumentLevelConfig,\n}\n"],"names":["baseDocumentLevelConfig","baseLegacyDocumentLevelConfig","baseFieldLevelConfig"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/transifexAdapter/helpers.ts","../src/transifexAdapter/getLocales.ts","../src/transifexAdapter/getTranslationTask.ts","../src/transifexAdapter/createTask.ts","../src/transifexAdapter/getTranslation.ts","../src/transifexAdapter/index.ts","../src/index.ts"],"sourcesContent":["import type {Secrets} from 'sanity-translations-tab'\n\nexport const baseTransifexUrl = 'https://rest.api.transifex.com'\n\nexport const getHeaders = (secrets: Secrets | null): Record<string, string> => ({\n 'Authorization': `Bearer ${secrets?.token}`,\n 'Content-Type': 'application/vnd.api+json',\n})\n\nexport const projOrgSlug = (secrets: Secrets | null): string =>\n `o:${secrets?.organization}:p:${secrets?.project}`\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nexport const getLocales: Adapter['getLocales'] = async (secrets: Secrets | null) => {\n let locales = []\n if (secrets) {\n const response = await fetch(`${baseTransifexUrl}/projects/${projOrgSlug(secrets)}/languages`, {\n headers: getHeaders(secrets),\n })\n\n if (!response.ok) {\n throw Error(`Failed to retrieve locales from Transifex. Status: ${response.status}`)\n }\n\n locales = await response.json().then((res) =>\n res.data.map((lang: Record<string, any>) => ({\n enabled: true,\n description: lang['attributes']['name'],\n localeId: lang['attributes']['code'],\n })),\n )\n }\n return locales\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {getLocales} from './getLocales'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nexport const getTranslationTask: Adapter['getTranslationTask'] = async (\n documentId: string,\n secrets: Secrets | null,\n) => {\n if (!documentId || !secrets) {\n return {\n taskId: documentId,\n documentId: documentId,\n locales: [],\n }\n }\n const projectFilter = `filter[project]=${projOrgSlug(secrets)}`\n const resourceFilter = `filter[resource]=${projOrgSlug(secrets)}:r:${documentId}`\n const task = await fetch(\n `${baseTransifexUrl}/resource_language_stats?${projectFilter}&${resourceFilter}`,\n {headers: getHeaders(secrets)},\n )\n .then((res) => {\n if (res.ok) {\n return res.json()\n }\n //normal -- just means that this task doesn't exist yet.\n else if (res.status === 404) {\n return {data: []}\n }\n throw Error(`Failed to retrieve tasks from Transifex. Status: ${res.status}`)\n })\n .then((res) => ({\n taskId: `${projOrgSlug(secrets)}:r:${documentId}`,\n documentId: documentId,\n locales: res.data.map((locale: Record<string, any>) => {\n const reviewedStrings = Number(locale['attributes']['reviewed_strings'])\n const totalStrings = Number(locale['attributes']['total_strings'])\n\n return {\n localeId: locale['relationships']['language']['data']['id'].split(':')[1],\n progress:\n Number.isFinite(reviewedStrings) && Number.isFinite(totalStrings) && totalStrings > 0\n ? Math.floor((100 * reviewedStrings) / totalStrings)\n : 0,\n }\n }),\n }))\n\n const locales = await getLocales(secrets)\n const localeIds = locales.map((l: Record<string, any>) => l['localeId'])\n const validLocales = task.locales.filter((locale: Record<string, any>) =>\n localeIds.find((id: string) => id === locale['localeId']),\n )\n task.locales = validLocales\n\n return task\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {getTranslationTask} from './getTranslationTask'\nimport {baseTransifexUrl, projOrgSlug, getHeaders} from './helpers'\n\nconst createResource = async (\n doc: Record<string, any>,\n documentId: string,\n secrets: Secrets | null,\n) => {\n const resourceCreateBody = {\n data: {\n attributes: {\n accept_translations: true,\n name: doc['name'],\n slug: documentId,\n },\n relationships: {\n i18n_format: {\n data: {\n id: 'HTML_FRAGMENT',\n type: 'i18n_formats',\n },\n },\n project: {\n data: {\n id: projOrgSlug(secrets),\n type: 'projects',\n },\n },\n },\n type: 'resources',\n },\n }\n\n const response = await fetch(`${baseTransifexUrl}/resources`, {\n headers: getHeaders(secrets),\n method: 'POST',\n body: JSON.stringify(resourceCreateBody),\n })\n\n if (!response.ok) {\n throw Error(`Failed to create Transifex resource. Status: ${response.status}`)\n }\n\n const res = await response.json()\n const resourceId = res?.data?.id\n\n if (!resourceId) {\n throw Error('Failed to create Transifex resource. Missing resource id in response.')\n }\n\n return resourceId\n}\n\nexport const createTask: Adapter['createTask'] = async (\n documentId: string,\n document: Record<string, any>,\n _localeIds: string[],\n secrets: Secrets | null,\n) => {\n if (!documentId || !secrets) {\n throw Error('Missing documentId or Transifex secrets.')\n }\n\n const resourceResponse = await fetch(\n `${baseTransifexUrl}/resources/${projOrgSlug(secrets)}:r:${documentId}`,\n {headers: getHeaders(secrets)},\n )\n let resourceId: string | null = null\n\n if (resourceResponse.status === 404) {\n resourceId = null\n } else if (!resourceResponse.ok) {\n throw Error(`Failed to retrieve Transifex resource. Status: ${resourceResponse.status}`)\n } else {\n const resource = await resourceResponse.json()\n resourceId = resource.data ? resource.data.id : null\n }\n\n if (!resourceId) {\n resourceId = await createResource(document, documentId, secrets)\n }\n\n const resourceUploadUrl = `${baseTransifexUrl}/resource_strings_async_uploads`\n const resourceUploadBody = {\n data: {\n attributes: {\n content: document['content'],\n content_encoding: 'text',\n },\n relationships: {\n resource: {\n data: {\n id: resourceId,\n type: 'resources',\n },\n },\n },\n type: 'resource_strings_async_uploads',\n },\n }\n\n const uploadResponse = await fetch(resourceUploadUrl, {\n method: 'POST',\n body: JSON.stringify(resourceUploadBody),\n headers: getHeaders(secrets),\n })\n\n if (!uploadResponse.ok) {\n throw Error(`Failed to upload resource strings to Transifex. Status: ${uploadResponse.status}`)\n }\n\n return getTranslationTask(documentId, secrets)\n}\n","import type {Adapter, Secrets} from 'sanity-translations-tab'\n\nimport {baseTransifexUrl, getHeaders} from './helpers'\n\nconst pollForFileDownloadLocation = async (\n resourceDownloadUrl: string,\n translationDownloadId: string,\n headers: Record<string, any>,\n retryCount = 0,\n maxRetries = 20,\n): Promise<string> => {\n const response = await fetch(`${resourceDownloadUrl}/${translationDownloadId}`, {\n headers: headers,\n })\n\n if (retryCount >= maxRetries) {\n throw Error(\n `Failed to retrieve download location for translation download ID ${translationDownloadId} after ${maxRetries} retries.`,\n )\n }\n\n if (response.status === 500) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Received 500 for translation download ID ${translationDownloadId}. Trying to reconnect...`,\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(\n resourceDownloadUrl,\n translationDownloadId,\n headers,\n retryCount + 1,\n maxRetries,\n )\n } else if (response.redirected) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Received redirect for translation download ID ${translationDownloadId}. Following redirect now for file download.`,\n )\n return response.url\n } else if (response.status === 200) {\n //eslint-disable-next-line no-console -- this is for developer feedback/debugging\n console.info(\n `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId}. Location is still pending, trying again.`,\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(\n resourceDownloadUrl,\n translationDownloadId,\n headers,\n retryCount + 1,\n maxRetries,\n )\n } else if (response.status === 401 || response.status === 403) {\n throw Error(\n `Failed to retrieve download location for translation download ID ${translationDownloadId}. Status: ${response.status}`,\n )\n }\n console.error(\n `Transifex plugin message: Requested download location for translation download ID ${translationDownloadId} but received error code ${response.status}. Waiting and trying again.`,\n )\n await new Promise((resolve) => setTimeout(resolve, 3000))\n return pollForFileDownloadLocation(\n resourceDownloadUrl,\n translationDownloadId,\n headers,\n retryCount + 1,\n maxRetries,\n )\n}\n\nconst handleFileDownload = (url: string) => {\n return fetch(url).then((res) => res.text())\n}\n\nexport const getTranslation: Adapter['getTranslation'] = async (\n taskId: string,\n localeId: string,\n secrets: Secrets | null,\n) => {\n if (!secrets) {\n throw Error('Missing Transifex secrets.')\n }\n\n const resourceDownloadBody = {\n data: {\n attributes: {\n content_encoding: 'text',\n },\n relationships: {\n language: {\n data: {\n id: `l:${localeId}`,\n type: 'languages',\n },\n },\n resource: {\n data: {\n id: taskId,\n type: 'resources',\n },\n },\n },\n type: 'resource_translations_async_downloads',\n },\n }\n\n const resourceDownloadUrl = `${baseTransifexUrl}/resource_translations_async_downloads`\n const downloadResponse = await fetch(resourceDownloadUrl, {\n headers: getHeaders(secrets),\n method: 'POST',\n body: JSON.stringify(resourceDownloadBody),\n })\n\n if (!downloadResponse.ok) {\n throw Error(\n `Failed to create translation download request in Transifex. Status: ${downloadResponse.status}`,\n )\n }\n\n const download = await downloadResponse.json()\n const translationDownloadId = download?.data?.id\n\n if (!translationDownloadId) {\n throw Error('Failed to create translation download request in Transifex. Missing download id.')\n }\n\n const headers = getHeaders(secrets)\n const location = await pollForFileDownloadLocation(\n resourceDownloadUrl,\n translationDownloadId,\n headers,\n )\n return handleFileDownload(location)\n}\n","import type {Adapter} from 'sanity-translations-tab'\n\nimport {createTask} from './createTask'\nimport {getLocales} from './getLocales'\nimport {getTranslation} from './getTranslation'\nimport {getTranslationTask} from './getTranslationTask'\n\nexport const TransifexAdapter: Adapter = {\n getLocales,\n getTranslationTask,\n createTask,\n getTranslation,\n}\n","import {\n baseDocumentLevelConfig,\n legacyDocumentLevelConfig as baseLegacyDocumentLevelConfig,\n baseFieldLevelConfig,\n} from 'sanity-translations-tab'\nimport type {Adapter, TranslationFunctionContext} from 'sanity-translations-tab'\n\nimport {TransifexAdapter} from './transifexAdapter'\n\nexport {\n findLatestDraft,\n BaseDocumentDeserializer,\n BaseDocumentSerializer,\n BaseDocumentMerger,\n defaultStopTypes,\n customSerializers,\n legacyDocumentLevelPatch,\n documentLevelPatch,\n fieldLevelPatch,\n TranslationsTab,\n} from 'sanity-translations-tab'\n\ninterface ConfigOptions {\n adapter: Adapter\n secretsNamespace: string | null\n exportForTranslation: (\n id: string,\n context: TranslationFunctionContext,\n ) => Promise<Record<string, any>>\n importTranslation: (\n id: string,\n localeId: string,\n doc: string,\n context: TranslationFunctionContext,\n ) => Promise<void>\n}\nconst defaultDocumentLevelConfig: ConfigOptions = {\n ...baseDocumentLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nconst legacyDocumentLevelConfig: ConfigOptions = {\n ...baseLegacyDocumentLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nconst defaultFieldLevelConfig: ConfigOptions = {\n ...baseFieldLevelConfig,\n adapter: TransifexAdapter,\n secretsNamespace: 'transifex',\n}\n\nexport {\n TransifexAdapter,\n defaultDocumentLevelConfig,\n defaultFieldLevelConfig,\n legacyDocumentLevelConfig,\n}\n"],"names":["baseTransifexUrl","getHeaders","secrets","token","projOrgSlug","organization","project","getLocales","locales","response","fetch","headers","ok","Error","status","json","then","res","data","map","lang","enabled","description","localeId","getTranslationTask","documentId","taskId","projectFilter","resourceFilter","task","locale","reviewedStrings","Number","totalStrings","split","progress","isFinite","Math","floor","localeIds","l","validLocales","filter","find","id","createResource","doc","resourceCreateBody","attributes","accept_translations","name","slug","relationships","i18n_format","type","method","body","JSON","stringify","resourceId","createTask","document","_localeIds","resourceResponse","resource","resourceUploadUrl","resourceUploadBody","content","content_encoding","uploadResponse","pollForFileDownloadLocation","resourceDownloadUrl","translationDownloadId","retryCount","maxRetries","console","info","Promise","resolve","setTimeout","redirected","url","error","handleFileDownload","text","getTranslation","resourceDownloadBody","language","downloadResponse","location","TransifexAdapter","defaultDocumentLevelConfig","baseDocumentLevelConfig","adapter","secretsNamespace","legacyDocumentLevelConfig","baseLegacyDocumentLevelConfig","defaultFieldLevelConfig","baseFieldLevelConfig"],"mappings":"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"}