@goenhance/strapi-plugins-translate 1.1.6 → 1.1.8

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,129 @@ 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
+ // Trigger batch translation (async - returns immediately)
1149
+ async batchTranslate(ctx) {
1150
+ const { contentType, entries, sourceLocale, targetLocales, publish = false } = ctx.request.body;
1151
+ if (!contentType || !entries || entries.length === 0 || !sourceLocale || !targetLocales || targetLocales.length === 0) {
1152
+ ctx.status = 400;
1153
+ ctx.body = {
1154
+ data: null,
1155
+ meta: { ok: false, message: "Invalid request parameters" }
1156
+ };
1157
+ return;
1158
+ }
1159
+ const contentTypeSchema = strapi2.contentTypes[contentType];
1160
+ if (!contentTypeSchema) {
1161
+ ctx.status = 404;
1162
+ ctx.body = {
1163
+ data: null,
1164
+ meta: { ok: false, message: "Content type not found" }
1165
+ };
1166
+ return;
1167
+ }
1168
+ const taskId = `${contentType}-${Date.now()}`;
1169
+ ctx.body = {
1170
+ data: {
1171
+ taskId,
1172
+ message: "Translation task started in background"
1173
+ },
1174
+ meta: {
1175
+ ok: true,
1176
+ message: "Translation task queued. You will receive a Feishu notification upon completion."
1177
+ }
1178
+ };
1179
+ setImmediate(async () => {
1180
+ try {
1181
+ const contentTypeDisplayName = contentTypeSchema.info?.displayName || contentType;
1182
+ const mainField = contentTypeSchema.info?.mainField || Object.keys(contentTypeSchema.attributes).find(
1183
+ (key) => contentTypeSchema.attributes[key].type === "string" && ["title", "name", "label"].includes(key.toLowerCase())
1184
+ ) || "id";
1185
+ const getDisplayValue = (value) => {
1186
+ if (value === null || value === void 0) return "";
1187
+ if (typeof value === "string" || typeof value === "number") return String(value);
1188
+ if (typeof value === "object") {
1189
+ return value.title || value.name || value.label || value.id || JSON.stringify(value);
1190
+ }
1191
+ return String(value);
1192
+ };
1193
+ const entryInfoPromises = entries.map(async (entryId) => {
1194
+ try {
1195
+ const entry = await strapi2.documents(contentType).findOne({
1196
+ documentId: entryId,
1197
+ locale: sourceLocale,
1198
+ populate: []
1199
+ });
1200
+ if (!entry) return { documentId: entryId, title: entryId };
1201
+ const title = getDisplayValue(entry[mainField]) || getDisplayValue(entry.id) || entryId;
1202
+ return { documentId: entryId, title };
1203
+ } catch (error) {
1204
+ return { documentId: entryId, title: entryId };
1205
+ }
1206
+ });
1207
+ const entryInfoList = await Promise.all(entryInfoPromises);
1208
+ await sendTaskStartNotification(contentTypeDisplayName, sourceLocale, targetLocales, entryInfoList);
1209
+ } catch (error) {
1210
+ console.error("Failed to send task start notification:", error);
1211
+ }
1212
+ });
1213
+ setImmediate(() => {
1214
+ executeBatchTranslation(strapi2, contentType, entries, sourceLocale, targetLocales, publish).catch((error) => {
1215
+ console.error("Background translation error:", error);
1216
+ });
1217
+ });
1218
+ }
1219
+ });
1100
1220
  const controllers = {
1101
- admin: controllers$1
1221
+ admin: controllers$1,
1222
+ api: apiController
1102
1223
  };
1103
1224
  const middlewares = {};
1104
1225
  const policies = {};
@@ -1160,10 +1281,49 @@ const adminRoutes = [
1160
1281
  }
1161
1282
  }
1162
1283
  ];
1284
+ const contentApiRoutes = [
1285
+ {
1286
+ method: "POST",
1287
+ path: "/batch-translate",
1288
+ handler: "api.batchTranslate",
1289
+ config: {
1290
+ policies: [],
1291
+ auth: {
1292
+ scope: []
1293
+ }
1294
+ }
1295
+ },
1296
+ {
1297
+ method: "GET",
1298
+ path: "/content-types",
1299
+ handler: "api.getContentTypes",
1300
+ config: {
1301
+ policies: [],
1302
+ auth: {
1303
+ scope: []
1304
+ }
1305
+ }
1306
+ },
1307
+ {
1308
+ method: "GET",
1309
+ path: "/locales",
1310
+ handler: "api.getLocales",
1311
+ config: {
1312
+ policies: [],
1313
+ auth: {
1314
+ scope: []
1315
+ }
1316
+ }
1317
+ }
1318
+ ];
1163
1319
  const routes = {
1164
1320
  admin: {
1165
1321
  type: "admin",
1166
1322
  routes: adminRoutes
1323
+ },
1324
+ "content-api": {
1325
+ type: "content-api",
1326
+ routes: contentApiRoutes
1167
1327
  }
1168
1328
  };
1169
1329
  const cleanJSONString = (content) => {
@@ -1096,8 +1096,129 @@ 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
+ // Trigger batch translation (async - returns immediately)
1148
+ async batchTranslate(ctx) {
1149
+ const { contentType, entries, sourceLocale, targetLocales, publish = false } = ctx.request.body;
1150
+ if (!contentType || !entries || entries.length === 0 || !sourceLocale || !targetLocales || targetLocales.length === 0) {
1151
+ ctx.status = 400;
1152
+ ctx.body = {
1153
+ data: null,
1154
+ meta: { ok: false, message: "Invalid request parameters" }
1155
+ };
1156
+ return;
1157
+ }
1158
+ const contentTypeSchema = strapi2.contentTypes[contentType];
1159
+ if (!contentTypeSchema) {
1160
+ ctx.status = 404;
1161
+ ctx.body = {
1162
+ data: null,
1163
+ meta: { ok: false, message: "Content type not found" }
1164
+ };
1165
+ return;
1166
+ }
1167
+ const taskId = `${contentType}-${Date.now()}`;
1168
+ ctx.body = {
1169
+ data: {
1170
+ taskId,
1171
+ message: "Translation task started in background"
1172
+ },
1173
+ meta: {
1174
+ ok: true,
1175
+ message: "Translation task queued. You will receive a Feishu notification upon completion."
1176
+ }
1177
+ };
1178
+ setImmediate(async () => {
1179
+ try {
1180
+ const contentTypeDisplayName = contentTypeSchema.info?.displayName || contentType;
1181
+ const mainField = contentTypeSchema.info?.mainField || Object.keys(contentTypeSchema.attributes).find(
1182
+ (key) => contentTypeSchema.attributes[key].type === "string" && ["title", "name", "label"].includes(key.toLowerCase())
1183
+ ) || "id";
1184
+ const getDisplayValue = (value) => {
1185
+ if (value === null || value === void 0) return "";
1186
+ if (typeof value === "string" || typeof value === "number") return String(value);
1187
+ if (typeof value === "object") {
1188
+ return value.title || value.name || value.label || value.id || JSON.stringify(value);
1189
+ }
1190
+ return String(value);
1191
+ };
1192
+ const entryInfoPromises = entries.map(async (entryId) => {
1193
+ try {
1194
+ const entry = await strapi2.documents(contentType).findOne({
1195
+ documentId: entryId,
1196
+ locale: sourceLocale,
1197
+ populate: []
1198
+ });
1199
+ if (!entry) return { documentId: entryId, title: entryId };
1200
+ const title = getDisplayValue(entry[mainField]) || getDisplayValue(entry.id) || entryId;
1201
+ return { documentId: entryId, title };
1202
+ } catch (error) {
1203
+ return { documentId: entryId, title: entryId };
1204
+ }
1205
+ });
1206
+ const entryInfoList = await Promise.all(entryInfoPromises);
1207
+ await sendTaskStartNotification(contentTypeDisplayName, sourceLocale, targetLocales, entryInfoList);
1208
+ } catch (error) {
1209
+ console.error("Failed to send task start notification:", error);
1210
+ }
1211
+ });
1212
+ setImmediate(() => {
1213
+ executeBatchTranslation(strapi2, contentType, entries, sourceLocale, targetLocales, publish).catch((error) => {
1214
+ console.error("Background translation error:", error);
1215
+ });
1216
+ });
1217
+ }
1218
+ });
1099
1219
  const controllers = {
1100
- admin: controllers$1
1220
+ admin: controllers$1,
1221
+ api: apiController
1101
1222
  };
1102
1223
  const middlewares = {};
1103
1224
  const policies = {};
@@ -1159,10 +1280,49 @@ const adminRoutes = [
1159
1280
  }
1160
1281
  }
1161
1282
  ];
1283
+ const contentApiRoutes = [
1284
+ {
1285
+ method: "POST",
1286
+ path: "/batch-translate",
1287
+ handler: "api.batchTranslate",
1288
+ config: {
1289
+ policies: [],
1290
+ auth: {
1291
+ scope: []
1292
+ }
1293
+ }
1294
+ },
1295
+ {
1296
+ method: "GET",
1297
+ path: "/content-types",
1298
+ handler: "api.getContentTypes",
1299
+ config: {
1300
+ policies: [],
1301
+ auth: {
1302
+ scope: []
1303
+ }
1304
+ }
1305
+ },
1306
+ {
1307
+ method: "GET",
1308
+ path: "/locales",
1309
+ handler: "api.getLocales",
1310
+ config: {
1311
+ policies: [],
1312
+ auth: {
1313
+ scope: []
1314
+ }
1315
+ }
1316
+ }
1317
+ ];
1162
1318
  const routes = {
1163
1319
  admin: {
1164
1320
  type: "admin",
1165
1321
  routes: adminRoutes
1322
+ },
1323
+ "content-api": {
1324
+ type: "content-api",
1325
+ routes: contentApiRoutes
1166
1326
  }
1167
1327
  };
1168
1328
  const cleanJSONString = (content) => {
@@ -0,0 +1,11 @@
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
+ batchTranslate(ctx: RequestContext & {
6
+ request: {
7
+ body: BatchTranslateRequestBody;
8
+ };
9
+ }): Promise<void>;
10
+ };
11
+ export default apiController;
@@ -54,5 +54,24 @@ 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
+ batchTranslate(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: import("../types").BatchTranslateRequestBody;
73
+ };
74
+ }): Promise<void>;
75
+ };
57
76
  };
58
77
  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.8",
3
3
  "keywords": [
4
4
  "strapi",
5
5
  "plugin",