@goenhance/strapi-plugins-translate 1.1.6 → 1.1.9

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.
@@ -1097,8 +1097,169 @@ const controllers$1 = ({ strapi: strapi2 }) => ({
1097
1097
  }
1098
1098
  }
1099
1099
  });
1100
+ const apiController = ({ strapi: strapi2 }) => ({
1101
+ // Get all i18n-enabled content types
1102
+ async getContentTypes(ctx) {
1103
+ try {
1104
+ const contentTypes2 = strapi2.contentTypes;
1105
+ const i18nContentTypes = [];
1106
+ Object.entries(contentTypes2).forEach(([uid, contentType]) => {
1107
+ const isI18nEnabled = contentType.pluginOptions?.i18n?.localized === true;
1108
+ const isApiContentType = uid.startsWith("api::");
1109
+ if (isI18nEnabled && isApiContentType) {
1110
+ i18nContentTypes.push({
1111
+ uid,
1112
+ displayName: contentType.info?.displayName || uid,
1113
+ apiID: contentType.info?.singularName || uid.split(".").pop() || uid,
1114
+ kind: contentType.kind || "collectionType"
1115
+ });
1116
+ }
1117
+ });
1118
+ ctx.body = {
1119
+ data: i18nContentTypes,
1120
+ meta: { ok: true }
1121
+ };
1122
+ } catch (error) {
1123
+ console.error("Error getting content types:", error);
1124
+ ctx.status = 500;
1125
+ ctx.body = {
1126
+ data: [],
1127
+ meta: { ok: false, message: "Failed to get content types" }
1128
+ };
1129
+ }
1130
+ },
1131
+ // Get all available locales
1132
+ async getLocales(ctx) {
1133
+ try {
1134
+ const locales = await strapi2.plugin("i18n").service("locales").find();
1135
+ ctx.body = {
1136
+ data: locales,
1137
+ meta: { ok: true }
1138
+ };
1139
+ } catch (error) {
1140
+ console.error("Error getting locales:", error);
1141
+ ctx.status = 500;
1142
+ ctx.body = {
1143
+ data: [],
1144
+ meta: { ok: false, message: "Failed to get locales" }
1145
+ };
1146
+ }
1147
+ },
1148
+ // Unpublish a document (optionally for a specific locale)
1149
+ async unpublish(ctx) {
1150
+ const { uid, documentId, locale } = ctx.request.body;
1151
+ if (!uid || !documentId) {
1152
+ ctx.status = 400;
1153
+ ctx.body = {
1154
+ data: null,
1155
+ meta: { ok: false, message: "Missing required fields: uid, documentId" }
1156
+ };
1157
+ return;
1158
+ }
1159
+ if (!strapi2.contentTypes[uid]) {
1160
+ ctx.status = 404;
1161
+ ctx.body = {
1162
+ data: null,
1163
+ meta: { ok: false, message: `Content type "${uid}" not found` }
1164
+ };
1165
+ return;
1166
+ }
1167
+ try {
1168
+ const result = await strapi2.documents(uid).unpublish({
1169
+ documentId,
1170
+ ...locale ? { locale } : {}
1171
+ });
1172
+ ctx.body = {
1173
+ data: result,
1174
+ meta: { ok: true, message: "Document unpublished successfully" }
1175
+ };
1176
+ } catch (error) {
1177
+ console.error("Error unpublishing document:", error);
1178
+ ctx.status = 500;
1179
+ ctx.body = {
1180
+ data: null,
1181
+ meta: {
1182
+ ok: false,
1183
+ message: error instanceof Error ? error.message : "Failed to unpublish document"
1184
+ }
1185
+ };
1186
+ }
1187
+ },
1188
+ // Trigger batch translation (async - returns immediately)
1189
+ async batchTranslate(ctx) {
1190
+ const { contentType, entries, sourceLocale, targetLocales, publish = false } = ctx.request.body;
1191
+ if (!contentType || !entries || entries.length === 0 || !sourceLocale || !targetLocales || targetLocales.length === 0) {
1192
+ ctx.status = 400;
1193
+ ctx.body = {
1194
+ data: null,
1195
+ meta: { ok: false, message: "Invalid request parameters" }
1196
+ };
1197
+ return;
1198
+ }
1199
+ const contentTypeSchema = strapi2.contentTypes[contentType];
1200
+ if (!contentTypeSchema) {
1201
+ ctx.status = 404;
1202
+ ctx.body = {
1203
+ data: null,
1204
+ meta: { ok: false, message: "Content type not found" }
1205
+ };
1206
+ return;
1207
+ }
1208
+ const taskId = `${contentType}-${Date.now()}`;
1209
+ ctx.body = {
1210
+ data: {
1211
+ taskId,
1212
+ message: "Translation task started in background"
1213
+ },
1214
+ meta: {
1215
+ ok: true,
1216
+ message: "Translation task queued. You will receive a Feishu notification upon completion."
1217
+ }
1218
+ };
1219
+ setImmediate(async () => {
1220
+ try {
1221
+ const contentTypeDisplayName = contentTypeSchema.info?.displayName || contentType;
1222
+ const mainField = contentTypeSchema.info?.mainField || Object.keys(contentTypeSchema.attributes).find(
1223
+ (key) => contentTypeSchema.attributes[key].type === "string" && ["title", "name", "label"].includes(key.toLowerCase())
1224
+ ) || "id";
1225
+ const getDisplayValue = (value) => {
1226
+ if (value === null || value === void 0) return "";
1227
+ if (typeof value === "string" || typeof value === "number") return String(value);
1228
+ if (typeof value === "object") {
1229
+ return value.title || value.name || value.label || value.id || JSON.stringify(value);
1230
+ }
1231
+ return String(value);
1232
+ };
1233
+ const entryInfoPromises = entries.map(async (entryId) => {
1234
+ try {
1235
+ const entry = await strapi2.documents(contentType).findOne({
1236
+ documentId: entryId,
1237
+ locale: sourceLocale,
1238
+ populate: []
1239
+ });
1240
+ if (!entry) return { documentId: entryId, title: entryId };
1241
+ const title = getDisplayValue(entry[mainField]) || getDisplayValue(entry.id) || entryId;
1242
+ return { documentId: entryId, title };
1243
+ } catch (error) {
1244
+ return { documentId: entryId, title: entryId };
1245
+ }
1246
+ });
1247
+ const entryInfoList = await Promise.all(entryInfoPromises);
1248
+ await sendTaskStartNotification(contentTypeDisplayName, sourceLocale, targetLocales, entryInfoList);
1249
+ } catch (error) {
1250
+ console.error("Failed to send task start notification:", error);
1251
+ }
1252
+ });
1253
+ setImmediate(() => {
1254
+ executeBatchTranslation(strapi2, contentType, entries, sourceLocale, targetLocales, publish).catch((error) => {
1255
+ console.error("Background translation error:", error);
1256
+ });
1257
+ });
1258
+ }
1259
+ });
1100
1260
  const controllers = {
1101
- admin: controllers$1
1261
+ admin: controllers$1,
1262
+ api: apiController
1102
1263
  };
1103
1264
  const middlewares = {};
1104
1265
  const policies = {};
@@ -1160,10 +1321,60 @@ const adminRoutes = [
1160
1321
  }
1161
1322
  }
1162
1323
  ];
1324
+ const contentApiRoutes = [
1325
+ {
1326
+ method: "POST",
1327
+ path: "/unpublish",
1328
+ handler: "api.unpublish",
1329
+ config: {
1330
+ policies: [],
1331
+ auth: {
1332
+ scope: []
1333
+ }
1334
+ }
1335
+ },
1336
+ {
1337
+ method: "POST",
1338
+ path: "/batch-translate",
1339
+ handler: "api.batchTranslate",
1340
+ config: {
1341
+ policies: [],
1342
+ auth: {
1343
+ scope: []
1344
+ }
1345
+ }
1346
+ },
1347
+ {
1348
+ method: "GET",
1349
+ path: "/content-types",
1350
+ handler: "api.getContentTypes",
1351
+ config: {
1352
+ policies: [],
1353
+ auth: {
1354
+ scope: []
1355
+ }
1356
+ }
1357
+ },
1358
+ {
1359
+ method: "GET",
1360
+ path: "/locales",
1361
+ handler: "api.getLocales",
1362
+ config: {
1363
+ policies: [],
1364
+ auth: {
1365
+ scope: []
1366
+ }
1367
+ }
1368
+ }
1369
+ ];
1163
1370
  const routes = {
1164
1371
  admin: {
1165
1372
  type: "admin",
1166
1373
  routes: adminRoutes
1374
+ },
1375
+ "content-api": {
1376
+ type: "content-api",
1377
+ routes: contentApiRoutes
1167
1378
  }
1168
1379
  };
1169
1380
  const cleanJSONString = (content) => {
@@ -1096,8 +1096,169 @@ const controllers$1 = ({ strapi: strapi2 }) => ({
1096
1096
  }
1097
1097
  }
1098
1098
  });
1099
+ const apiController = ({ strapi: strapi2 }) => ({
1100
+ // Get all i18n-enabled content types
1101
+ async getContentTypes(ctx) {
1102
+ try {
1103
+ const contentTypes2 = strapi2.contentTypes;
1104
+ const i18nContentTypes = [];
1105
+ Object.entries(contentTypes2).forEach(([uid, contentType]) => {
1106
+ const isI18nEnabled = contentType.pluginOptions?.i18n?.localized === true;
1107
+ const isApiContentType = uid.startsWith("api::");
1108
+ if (isI18nEnabled && isApiContentType) {
1109
+ i18nContentTypes.push({
1110
+ uid,
1111
+ displayName: contentType.info?.displayName || uid,
1112
+ apiID: contentType.info?.singularName || uid.split(".").pop() || uid,
1113
+ kind: contentType.kind || "collectionType"
1114
+ });
1115
+ }
1116
+ });
1117
+ ctx.body = {
1118
+ data: i18nContentTypes,
1119
+ meta: { ok: true }
1120
+ };
1121
+ } catch (error) {
1122
+ console.error("Error getting content types:", error);
1123
+ ctx.status = 500;
1124
+ ctx.body = {
1125
+ data: [],
1126
+ meta: { ok: false, message: "Failed to get content types" }
1127
+ };
1128
+ }
1129
+ },
1130
+ // Get all available locales
1131
+ async getLocales(ctx) {
1132
+ try {
1133
+ const locales = await strapi2.plugin("i18n").service("locales").find();
1134
+ ctx.body = {
1135
+ data: locales,
1136
+ meta: { ok: true }
1137
+ };
1138
+ } catch (error) {
1139
+ console.error("Error getting locales:", error);
1140
+ ctx.status = 500;
1141
+ ctx.body = {
1142
+ data: [],
1143
+ meta: { ok: false, message: "Failed to get locales" }
1144
+ };
1145
+ }
1146
+ },
1147
+ // Unpublish a document (optionally for a specific locale)
1148
+ async unpublish(ctx) {
1149
+ const { uid, documentId, locale } = ctx.request.body;
1150
+ if (!uid || !documentId) {
1151
+ ctx.status = 400;
1152
+ ctx.body = {
1153
+ data: null,
1154
+ meta: { ok: false, message: "Missing required fields: uid, documentId" }
1155
+ };
1156
+ return;
1157
+ }
1158
+ if (!strapi2.contentTypes[uid]) {
1159
+ ctx.status = 404;
1160
+ ctx.body = {
1161
+ data: null,
1162
+ meta: { ok: false, message: `Content type "${uid}" not found` }
1163
+ };
1164
+ return;
1165
+ }
1166
+ try {
1167
+ const result = await strapi2.documents(uid).unpublish({
1168
+ documentId,
1169
+ ...locale ? { locale } : {}
1170
+ });
1171
+ ctx.body = {
1172
+ data: result,
1173
+ meta: { ok: true, message: "Document unpublished successfully" }
1174
+ };
1175
+ } catch (error) {
1176
+ console.error("Error unpublishing document:", error);
1177
+ ctx.status = 500;
1178
+ ctx.body = {
1179
+ data: null,
1180
+ meta: {
1181
+ ok: false,
1182
+ message: error instanceof Error ? error.message : "Failed to unpublish document"
1183
+ }
1184
+ };
1185
+ }
1186
+ },
1187
+ // Trigger batch translation (async - returns immediately)
1188
+ async batchTranslate(ctx) {
1189
+ const { contentType, entries, sourceLocale, targetLocales, publish = false } = ctx.request.body;
1190
+ if (!contentType || !entries || entries.length === 0 || !sourceLocale || !targetLocales || targetLocales.length === 0) {
1191
+ ctx.status = 400;
1192
+ ctx.body = {
1193
+ data: null,
1194
+ meta: { ok: false, message: "Invalid request parameters" }
1195
+ };
1196
+ return;
1197
+ }
1198
+ const contentTypeSchema = strapi2.contentTypes[contentType];
1199
+ if (!contentTypeSchema) {
1200
+ ctx.status = 404;
1201
+ ctx.body = {
1202
+ data: null,
1203
+ meta: { ok: false, message: "Content type not found" }
1204
+ };
1205
+ return;
1206
+ }
1207
+ const taskId = `${contentType}-${Date.now()}`;
1208
+ ctx.body = {
1209
+ data: {
1210
+ taskId,
1211
+ message: "Translation task started in background"
1212
+ },
1213
+ meta: {
1214
+ ok: true,
1215
+ message: "Translation task queued. You will receive a Feishu notification upon completion."
1216
+ }
1217
+ };
1218
+ setImmediate(async () => {
1219
+ try {
1220
+ const contentTypeDisplayName = contentTypeSchema.info?.displayName || contentType;
1221
+ const mainField = contentTypeSchema.info?.mainField || Object.keys(contentTypeSchema.attributes).find(
1222
+ (key) => contentTypeSchema.attributes[key].type === "string" && ["title", "name", "label"].includes(key.toLowerCase())
1223
+ ) || "id";
1224
+ const getDisplayValue = (value) => {
1225
+ if (value === null || value === void 0) return "";
1226
+ if (typeof value === "string" || typeof value === "number") return String(value);
1227
+ if (typeof value === "object") {
1228
+ return value.title || value.name || value.label || value.id || JSON.stringify(value);
1229
+ }
1230
+ return String(value);
1231
+ };
1232
+ const entryInfoPromises = entries.map(async (entryId) => {
1233
+ try {
1234
+ const entry = await strapi2.documents(contentType).findOne({
1235
+ documentId: entryId,
1236
+ locale: sourceLocale,
1237
+ populate: []
1238
+ });
1239
+ if (!entry) return { documentId: entryId, title: entryId };
1240
+ const title = getDisplayValue(entry[mainField]) || getDisplayValue(entry.id) || entryId;
1241
+ return { documentId: entryId, title };
1242
+ } catch (error) {
1243
+ return { documentId: entryId, title: entryId };
1244
+ }
1245
+ });
1246
+ const entryInfoList = await Promise.all(entryInfoPromises);
1247
+ await sendTaskStartNotification(contentTypeDisplayName, sourceLocale, targetLocales, entryInfoList);
1248
+ } catch (error) {
1249
+ console.error("Failed to send task start notification:", error);
1250
+ }
1251
+ });
1252
+ setImmediate(() => {
1253
+ executeBatchTranslation(strapi2, contentType, entries, sourceLocale, targetLocales, publish).catch((error) => {
1254
+ console.error("Background translation error:", error);
1255
+ });
1256
+ });
1257
+ }
1258
+ });
1099
1259
  const controllers = {
1100
- admin: controllers$1
1260
+ admin: controllers$1,
1261
+ api: apiController
1101
1262
  };
1102
1263
  const middlewares = {};
1103
1264
  const policies = {};
@@ -1159,10 +1320,60 @@ const adminRoutes = [
1159
1320
  }
1160
1321
  }
1161
1322
  ];
1323
+ const contentApiRoutes = [
1324
+ {
1325
+ method: "POST",
1326
+ path: "/unpublish",
1327
+ handler: "api.unpublish",
1328
+ config: {
1329
+ policies: [],
1330
+ auth: {
1331
+ scope: []
1332
+ }
1333
+ }
1334
+ },
1335
+ {
1336
+ method: "POST",
1337
+ path: "/batch-translate",
1338
+ handler: "api.batchTranslate",
1339
+ config: {
1340
+ policies: [],
1341
+ auth: {
1342
+ scope: []
1343
+ }
1344
+ }
1345
+ },
1346
+ {
1347
+ method: "GET",
1348
+ path: "/content-types",
1349
+ handler: "api.getContentTypes",
1350
+ config: {
1351
+ policies: [],
1352
+ auth: {
1353
+ scope: []
1354
+ }
1355
+ }
1356
+ },
1357
+ {
1358
+ method: "GET",
1359
+ path: "/locales",
1360
+ handler: "api.getLocales",
1361
+ config: {
1362
+ policies: [],
1363
+ auth: {
1364
+ scope: []
1365
+ }
1366
+ }
1367
+ }
1368
+ ];
1162
1369
  const routes = {
1163
1370
  admin: {
1164
1371
  type: "admin",
1165
1372
  routes: adminRoutes
1373
+ },
1374
+ "content-api": {
1375
+ type: "content-api",
1376
+ routes: contentApiRoutes
1166
1377
  }
1167
1378
  };
1168
1379
  const cleanJSONString = (content) => {
@@ -0,0 +1,20 @@
1
+ import { BatchTranslateRequestBody, RequestContext, StrapiContext } from 'src/types';
2
+ declare const apiController: ({ strapi }: StrapiContext) => {
3
+ getContentTypes(ctx: RequestContext): Promise<void>;
4
+ getLocales(ctx: RequestContext): Promise<void>;
5
+ unpublish(ctx: RequestContext & {
6
+ request: {
7
+ body: {
8
+ uid: string;
9
+ documentId: string;
10
+ locale?: string;
11
+ };
12
+ };
13
+ }): Promise<void>;
14
+ batchTranslate(ctx: RequestContext & {
15
+ request: {
16
+ body: BatchTranslateRequestBody;
17
+ };
18
+ }): Promise<void>;
19
+ };
20
+ export default apiController;
@@ -54,5 +54,43 @@ declare const _default: {
54
54
  };
55
55
  }): Promise<void>;
56
56
  };
57
+ api: ({ strapi }: import("../types").StrapiContext) => {
58
+ getContentTypes(ctx: import("../types").RequestContext): Promise<void>;
59
+ getLocales(ctx: import("../types").RequestContext): Promise<void>;
60
+ unpublish(ctx: Omit<import("koa").Context, "body" | "query" | "request"> & {
61
+ body: object;
62
+ query: object;
63
+ params: object;
64
+ request: Omit<import("koa").Request, "body"> & {
65
+ body: object;
66
+ };
67
+ state: {
68
+ user?: import("../types").AdminUser;
69
+ };
70
+ } & {
71
+ request: {
72
+ body: {
73
+ uid: string;
74
+ documentId: string;
75
+ locale?: string;
76
+ };
77
+ };
78
+ }): Promise<void>;
79
+ batchTranslate(ctx: Omit<import("koa").Context, "body" | "query" | "request"> & {
80
+ body: object;
81
+ query: object;
82
+ params: object;
83
+ request: Omit<import("koa").Request, "body"> & {
84
+ body: object;
85
+ };
86
+ state: {
87
+ user?: import("../types").AdminUser;
88
+ };
89
+ } & {
90
+ request: {
91
+ body: import("../types").BatchTranslateRequestBody;
92
+ };
93
+ }): Promise<void>;
94
+ };
57
95
  };
58
96
  export default _default;
@@ -0,0 +1,12 @@
1
+ declare const _default: {
2
+ method: string;
3
+ path: string;
4
+ handler: string;
5
+ config: {
6
+ policies: any[];
7
+ auth: {
8
+ scope: any[];
9
+ };
10
+ };
11
+ }[];
12
+ export default _default;
@@ -10,5 +10,19 @@ declare const routes: {
10
10
  };
11
11
  }[];
12
12
  };
13
+ 'content-api': {
14
+ type: string;
15
+ routes: {
16
+ method: string;
17
+ path: string;
18
+ handler: string;
19
+ config: {
20
+ policies: any[];
21
+ auth: {
22
+ scope: any[];
23
+ };
24
+ };
25
+ }[];
26
+ };
13
27
  };
14
28
  export default routes;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.1.6",
2
+ "version": "1.1.9",
3
3
  "keywords": [
4
4
  "strapi",
5
5
  "plugin",