@strapi/i18n 0.0.0-experimental.31a5317d54179a9ce6225bf1b6e1f9fb6c372fa9 → 0.0.0-experimental.36632203b17974c18103c138ffbef53a82e448c3

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.
Files changed (45) hide show
  1. package/dist/_chunks/{SettingsPage-BjxjwEOb.mjs → SettingsPage-BHvunuIF.mjs} +4 -4
  2. package/dist/_chunks/SettingsPage-BHvunuIF.mjs.map +1 -0
  3. package/dist/_chunks/{SettingsPage-CfTmCkup.js → SettingsPage-Bcj7380u.js} +4 -4
  4. package/dist/_chunks/SettingsPage-Bcj7380u.js.map +1 -0
  5. package/dist/_chunks/{en-DWpfm8h5.js → en-BKBz3tro.js} +5 -4
  6. package/dist/_chunks/en-BKBz3tro.js.map +1 -0
  7. package/dist/_chunks/{en-2xztdZE1.mjs → en-DlXfy6Gy.mjs} +5 -4
  8. package/dist/_chunks/en-DlXfy6Gy.mjs.map +1 -0
  9. package/dist/_chunks/{index-5XLZwzwx.js → index-BKZbxhpm.js} +81 -30
  10. package/dist/_chunks/index-BKZbxhpm.js.map +1 -0
  11. package/dist/_chunks/{index-D-qx3tz4.mjs → index-DUdrr5PR.mjs} +82 -31
  12. package/dist/_chunks/index-DUdrr5PR.mjs.map +1 -0
  13. package/dist/admin/index.js +1 -1
  14. package/dist/admin/index.mjs +1 -1
  15. package/dist/admin/src/components/CMHeaderActions.d.ts +5 -3
  16. package/dist/admin/src/components/CreateLocale.d.ts +6 -6
  17. package/dist/server/index.js +388 -506
  18. package/dist/server/index.js.map +1 -1
  19. package/dist/server/index.mjs +390 -508
  20. package/dist/server/index.mjs.map +1 -1
  21. package/dist/server/src/bootstrap.d.ts +1 -4
  22. package/dist/server/src/bootstrap.d.ts.map +1 -1
  23. package/dist/server/src/index.d.ts +7 -11
  24. package/dist/server/src/index.d.ts.map +1 -1
  25. package/dist/server/src/register.d.ts.map +1 -1
  26. package/dist/server/src/services/index.d.ts +6 -8
  27. package/dist/server/src/services/index.d.ts.map +1 -1
  28. package/dist/server/src/services/sanitize/index.d.ts +11 -0
  29. package/dist/server/src/services/sanitize/index.d.ts.map +1 -0
  30. package/dist/server/src/utils/index.d.ts +2 -2
  31. package/dist/server/src/utils/index.d.ts.map +1 -1
  32. package/package.json +10 -10
  33. package/dist/_chunks/SettingsPage-BjxjwEOb.mjs.map +0 -1
  34. package/dist/_chunks/SettingsPage-CfTmCkup.js.map +0 -1
  35. package/dist/_chunks/en-2xztdZE1.mjs.map +0 -1
  36. package/dist/_chunks/en-DWpfm8h5.js.map +0 -1
  37. package/dist/_chunks/index-5XLZwzwx.js.map +0 -1
  38. package/dist/_chunks/index-D-qx3tz4.mjs.map +0 -1
  39. package/dist/server/src/migrations/content-type/disable/index.d.ts +0 -3
  40. package/dist/server/src/migrations/content-type/disable/index.d.ts.map +0 -1
  41. package/dist/server/src/migrations/content-type/enable/index.d.ts +0 -3
  42. package/dist/server/src/migrations/content-type/enable/index.d.ts.map +0 -1
  43. package/dist/server/src/services/entity-service-decorator.d.ts +0 -29
  44. package/dist/server/src/services/entity-service-decorator.d.ts.map +0 -1
  45. package/strapi-server.js +0 -3
@@ -65,12 +65,10 @@ const registerModelsHooks = () => {
65
65
  return result;
66
66
  });
67
67
  };
68
- const bootstrap = async ({ strapi: strapi2 }) => {
68
+ const bootstrap = async () => {
69
69
  const { sendDidInitializeEvent: sendDidInitializeEvent2 } = getService("metrics");
70
- const { decorator: decorator2 } = getService("entity-service-decorator");
71
70
  const { initDefaultLocale: initDefaultLocale2 } = getService("locales");
72
71
  const { sectionsBuilder, actions: actions2, engine } = getService("permissions");
73
- strapi2.entityService.decorate(decorator2);
74
72
  await initDefaultLocale2();
75
73
  sectionsBuilder.registerLocalesPropertyHandler();
76
74
  await actions2.registerI18nActions();
@@ -197,6 +195,352 @@ const getI18nLocaleArgPlugin = ({ nexus, typeRegistry }) => {
197
195
  }
198
196
  });
199
197
  };
198
+ const register = ({ strapi: strapi2 }) => {
199
+ extendContentTypes(strapi2);
200
+ addContentManagerLocaleMiddleware(strapi2);
201
+ };
202
+ const addContentManagerLocaleMiddleware = (strapi2) => {
203
+ strapi2.server.router.use("/content-manager/collection-types/:model", (ctx, next) => {
204
+ if (ctx.method === "POST" || ctx.method === "PUT") {
205
+ return validateLocaleCreation(ctx, next);
206
+ }
207
+ return next();
208
+ });
209
+ strapi2.server.router.use("/content-manager/single-types/:model", (ctx, next) => {
210
+ if (ctx.method === "POST" || ctx.method === "PUT") {
211
+ return validateLocaleCreation(ctx, next);
212
+ }
213
+ return next();
214
+ });
215
+ };
216
+ const extendContentTypes = (strapi2) => {
217
+ const { isLocalizedContentType: isLocalizedContentType2 } = getService("content-types");
218
+ Object.values(strapi2.contentTypes).forEach((contentType) => {
219
+ const { attributes: attributes2 } = contentType;
220
+ const isLocalized = isLocalizedContentType2(contentType);
221
+ ___default.default.set(attributes2, "locale", {
222
+ writable: true,
223
+ private: !isLocalized,
224
+ configurable: false,
225
+ visible: false,
226
+ type: "string"
227
+ });
228
+ ___default.default.set(attributes2, "localizations", {
229
+ type: "relation",
230
+ relation: "oneToMany",
231
+ target: contentType.uid,
232
+ writable: false,
233
+ private: !isLocalized,
234
+ configurable: false,
235
+ visible: false,
236
+ unstable_virtual: true,
237
+ joinColumn: {
238
+ name: "document_id",
239
+ referencedColumn: "document_id",
240
+ referencedTable: strapi2.db.metadata.identifiers.getTableName(contentType.collectionName),
241
+ // ensure the population will not include the results we already loaded
242
+ on({ results }) {
243
+ return {
244
+ id: {
245
+ $notIn: results.map((r) => r.id)
246
+ }
247
+ };
248
+ }
249
+ }
250
+ });
251
+ });
252
+ if (strapi2.plugin("graphql")) {
253
+ graphqlProvider({ strapi: strapi2 }).register();
254
+ }
255
+ };
256
+ const info = {
257
+ singularName: "locale",
258
+ pluralName: "locales",
259
+ collectionName: "locales",
260
+ displayName: "Locale",
261
+ description: ""
262
+ };
263
+ const options = {};
264
+ const pluginOptions = {
265
+ "content-manager": {
266
+ visible: false
267
+ },
268
+ "content-type-builder": {
269
+ visible: false
270
+ }
271
+ };
272
+ const attributes = {
273
+ name: {
274
+ type: "string",
275
+ min: 1,
276
+ max: 50,
277
+ configurable: false
278
+ },
279
+ code: {
280
+ type: "string",
281
+ unique: true,
282
+ configurable: false
283
+ }
284
+ };
285
+ const schema = {
286
+ info,
287
+ options,
288
+ pluginOptions,
289
+ attributes
290
+ };
291
+ const locale = {
292
+ schema
293
+ };
294
+ const contentTypes$1 = {
295
+ locale
296
+ };
297
+ const actions = [
298
+ {
299
+ section: "settings",
300
+ category: "Internationalization",
301
+ subCategory: "Locales",
302
+ pluginName: "i18n",
303
+ displayName: "Create",
304
+ uid: "locale.create"
305
+ },
306
+ {
307
+ section: "settings",
308
+ category: "Internationalization",
309
+ subCategory: "Locales",
310
+ pluginName: "i18n",
311
+ displayName: "Read",
312
+ uid: "locale.read",
313
+ aliases: [
314
+ { actionId: "plugin::content-manager.explorer.read", subjects: ["plugin::i18n.locale"] }
315
+ ]
316
+ },
317
+ {
318
+ section: "settings",
319
+ category: "Internationalization",
320
+ subCategory: "Locales",
321
+ pluginName: "i18n",
322
+ displayName: "Update",
323
+ uid: "locale.update"
324
+ },
325
+ {
326
+ section: "settings",
327
+ category: "Internationalization",
328
+ subCategory: "Locales",
329
+ pluginName: "i18n",
330
+ displayName: "Delete",
331
+ uid: "locale.delete"
332
+ }
333
+ ];
334
+ const addLocalesPropertyIfNeeded = ({ value: action }) => {
335
+ const {
336
+ section,
337
+ options: { applyToProperties }
338
+ } = action;
339
+ if (section !== "contentTypes") {
340
+ return;
341
+ }
342
+ if (fp.isArray(applyToProperties) && applyToProperties.includes("locales")) {
343
+ return;
344
+ }
345
+ action.options.applyToProperties = fp.isArray(applyToProperties) ? applyToProperties.concat("locales") : ["locales"];
346
+ };
347
+ const shouldApplyLocalesPropertyToSubject = ({ property, subject }) => {
348
+ if (property === "locales") {
349
+ const model = strapi.getModel(subject);
350
+ return getService("content-types").isLocalizedContentType(model);
351
+ }
352
+ return true;
353
+ };
354
+ const addAllLocalesToPermissions = async (permissions2) => {
355
+ const { actionProvider } = strapi.service("admin::permission");
356
+ const { find: findAllLocales } = getService("locales");
357
+ const allLocales = await findAllLocales();
358
+ const allLocalesCode = allLocales.map(fp.prop("code"));
359
+ return Promise.all(
360
+ permissions2.map(async (permission) => {
361
+ const { action, subject } = permission;
362
+ const appliesToLocalesProperty = await actionProvider.appliesToProperty(
363
+ "locales",
364
+ action,
365
+ subject
366
+ );
367
+ if (!appliesToLocalesProperty) {
368
+ return permission;
369
+ }
370
+ const oldPermissionProperties = fp.getOr({}, "properties", permission);
371
+ return { ...permission, properties: { ...oldPermissionProperties, locales: allLocalesCode } };
372
+ })
373
+ );
374
+ };
375
+ const syncSuperAdminPermissionsWithLocales = async () => {
376
+ const roleService = strapi.service("admin::role");
377
+ const permissionService = strapi.service("admin::permission");
378
+ const superAdminRole = await roleService.getSuperAdmin();
379
+ if (!superAdminRole) {
380
+ return;
381
+ }
382
+ const superAdminPermissions = await permissionService.findMany({
383
+ where: {
384
+ role: {
385
+ id: superAdminRole.id
386
+ }
387
+ }
388
+ });
389
+ const newSuperAdminPermissions = await addAllLocalesToPermissions(superAdminPermissions);
390
+ await roleService.assignPermissions(superAdminRole.id, newSuperAdminPermissions);
391
+ };
392
+ const registerI18nActions = async () => {
393
+ const { actionProvider } = strapi.service("admin::permission");
394
+ await actionProvider.registerMany(actions);
395
+ };
396
+ const registerI18nActionsHooks = () => {
397
+ const { actionProvider } = strapi.service("admin::permission");
398
+ const { hooks } = strapi.service("admin::role");
399
+ actionProvider.hooks.appliesPropertyToSubject.register(shouldApplyLocalesPropertyToSubject);
400
+ hooks.willResetSuperAdminPermissions.register(addAllLocalesToPermissions);
401
+ };
402
+ const updateActionsProperties = () => {
403
+ const { actionProvider } = strapi.service("admin::permission");
404
+ actionProvider.hooks.willRegister.register(addLocalesPropertyIfNeeded);
405
+ actionProvider.values().forEach((action) => addLocalesPropertyIfNeeded({ value: action }));
406
+ };
407
+ const i18nActionsService = {
408
+ actions,
409
+ registerI18nActions,
410
+ registerI18nActionsHooks,
411
+ updateActionsProperties,
412
+ syncSuperAdminPermissionsWithLocales
413
+ };
414
+ const localesPropertyHandler = async ({ action, section }) => {
415
+ const { actionProvider } = strapi.service("admin::permission");
416
+ const locales2 = await getService("locales").find();
417
+ if (fp.isEmpty(locales2)) {
418
+ return;
419
+ }
420
+ for (const subject of section.subjects) {
421
+ const applies = await actionProvider.appliesToProperty("locales", action.actionId, subject.uid);
422
+ const hasLocalesProperty = subject.properties.find(
423
+ (property) => property.value === "locales"
424
+ );
425
+ if (applies && !hasLocalesProperty) {
426
+ subject.properties.push({
427
+ label: "Locales",
428
+ value: "locales",
429
+ children: locales2.map(({ name, code }) => ({ label: name || code, value: code }))
430
+ });
431
+ }
432
+ }
433
+ };
434
+ const registerLocalesPropertyHandler = () => {
435
+ const { sectionsBuilder } = strapi.service("admin::permission");
436
+ sectionsBuilder.addHandler("singleTypes", localesPropertyHandler);
437
+ sectionsBuilder.addHandler("collectionTypes", localesPropertyHandler);
438
+ };
439
+ const sectionsBuilderService = {
440
+ localesPropertyHandler,
441
+ registerLocalesPropertyHandler
442
+ };
443
+ const willRegisterPermission = (context) => {
444
+ const { permission, condition, user } = context;
445
+ const { subject, properties } = permission;
446
+ const isSuperAdmin = strapi.service("admin::role").hasSuperAdminRole(user);
447
+ if (isSuperAdmin) {
448
+ return;
449
+ }
450
+ const { locales: locales2 } = properties || {};
451
+ const { isLocalizedContentType: isLocalizedContentType2 } = getService("content-types");
452
+ if (!subject) {
453
+ return;
454
+ }
455
+ const ct = strapi.contentTypes[subject];
456
+ if (!isLocalizedContentType2(ct)) {
457
+ return;
458
+ }
459
+ if (locales2 === null) {
460
+ return;
461
+ }
462
+ condition.and({
463
+ locale: {
464
+ $in: locales2 || []
465
+ }
466
+ });
467
+ };
468
+ const registerI18nPermissionsHandlers = () => {
469
+ const { engine } = strapi.service("admin::permission");
470
+ engine.hooks["before-register.permission"].register(willRegisterPermission);
471
+ };
472
+ const engineService = {
473
+ willRegisterPermission,
474
+ registerI18nPermissionsHandlers
475
+ };
476
+ const permissions = () => ({
477
+ actions: i18nActionsService,
478
+ sectionsBuilder: sectionsBuilderService,
479
+ engine: engineService
480
+ });
481
+ const sendDidInitializeEvent = async () => {
482
+ const { isLocalizedContentType: isLocalizedContentType2 } = getService("content-types");
483
+ const numberOfContentTypes = fp.reduce(
484
+ (sum, contentType) => isLocalizedContentType2(contentType) ? sum + 1 : sum,
485
+ 0
486
+ )(strapi.contentTypes);
487
+ await strapi.telemetry.send("didInitializeI18n", { groupProperties: { numberOfContentTypes } });
488
+ };
489
+ const sendDidUpdateI18nLocalesEvent = async () => {
490
+ const numberOfLocales = await getService("locales").count();
491
+ await strapi.telemetry.send("didUpdateI18nLocales", {
492
+ groupProperties: { numberOfLocales }
493
+ });
494
+ };
495
+ const metrics = () => ({
496
+ sendDidInitializeEvent,
497
+ sendDidUpdateI18nLocalesEvent
498
+ });
499
+ const syncNonLocalizedAttributes = async (sourceEntry, model) => {
500
+ const { copyNonLocalizedAttributes: copyNonLocalizedAttributes2 } = getService("content-types");
501
+ const nonLocalizedAttributes = copyNonLocalizedAttributes2(model, sourceEntry);
502
+ if (fp.isEmpty(nonLocalizedAttributes)) {
503
+ return;
504
+ }
505
+ const uid = model.uid;
506
+ const documentId = sourceEntry.documentId;
507
+ const locale2 = sourceEntry.locale;
508
+ const status = sourceEntry?.publishedAt ? "published" : "draft";
509
+ const localeEntriesToUpdate = await strapi.db.query(uid).findMany({
510
+ where: {
511
+ documentId,
512
+ publishedAt: status === "published" ? { $ne: null } : null,
513
+ locale: { $ne: locale2 }
514
+ },
515
+ select: ["locale", "id"]
516
+ });
517
+ const entryData = await strapi.documents(uid).omitComponentData(nonLocalizedAttributes);
518
+ await utils.async.map(localeEntriesToUpdate, async (entry) => {
519
+ const transformedData = await strapi.documents.utils.transformData(
520
+ fp.cloneDeep(nonLocalizedAttributes),
521
+ {
522
+ uid,
523
+ status,
524
+ locale: entry.locale,
525
+ allowMissingId: true
526
+ }
527
+ );
528
+ const componentData = await strapi.documents(uid).updateComponents(entry, transformedData);
529
+ await strapi.db.query(uid).update({
530
+ where: {
531
+ documentId,
532
+ publishedAt: status === "published" ? { $ne: null } : null,
533
+ locale: { $eq: entry.locale }
534
+ },
535
+ // The data we send to the update function is the entry data merged with
536
+ // the updated component data
537
+ data: Object.assign(fp.cloneDeep(entryData), componentData)
538
+ });
539
+ });
540
+ };
541
+ const localizations = () => ({
542
+ syncNonLocalizedAttributes
543
+ });
200
544
  const isoLocales = [
201
545
  {
202
546
  code: "af",
@@ -2596,431 +2940,34 @@ const isoLocales = [
2596
2940
  },
2597
2941
  {
2598
2942
  code: "dje-NE",
2599
- name: "Zarma (Niger) (dje-NE)"
2600
- },
2601
- {
2602
- code: "zu",
2603
- name: "Zulu (zu)"
2604
- },
2605
- {
2606
- code: "zu-ZA",
2607
- name: "Zulu (South Africa) (zu-ZA)"
2608
- }
2609
- ];
2610
- const getInitLocale = () => {
2611
- const envLocaleCode = process.env.STRAPI_PLUGIN_I18N_INIT_LOCALE_CODE;
2612
- if (envLocaleCode) {
2613
- const matchingLocale = isoLocales.find(({ code }) => code === envLocaleCode);
2614
- if (!matchingLocale) {
2615
- throw new Error(
2616
- "Unknown locale code provided in the environment variable STRAPI_PLUGIN_I18N_INIT_LOCALE_CODE"
2617
- );
2618
- }
2619
- return { ...matchingLocale };
2620
- }
2621
- return {
2622
- code: "en",
2623
- name: "English (en)"
2624
- };
2625
- };
2626
- const DEFAULT_LOCALE = getInitLocale();
2627
- const enableContentType = async ({ oldContentTypes, contentTypes: contentTypes2 }) => {
2628
- const { isLocalizedContentType: isLocalizedContentType2 } = getService("content-types");
2629
- const { getDefaultLocale: getDefaultLocale2 } = getService("locales");
2630
- if (!oldContentTypes) {
2631
- return;
2632
- }
2633
- for (const uid in contentTypes2) {
2634
- if (!oldContentTypes[uid]) {
2635
- continue;
2636
- }
2637
- const oldContentType = oldContentTypes[uid];
2638
- const contentType = contentTypes2[uid];
2639
- if (!isLocalizedContentType2(oldContentType) && isLocalizedContentType2(contentType)) {
2640
- const defaultLocale = await getDefaultLocale2() || DEFAULT_LOCALE.code;
2641
- await strapi.db.query(uid).updateMany({
2642
- where: { locale: null },
2643
- data: { locale: defaultLocale }
2644
- });
2645
- }
2646
- }
2647
- };
2648
- const disableContentType = async ({ oldContentTypes, contentTypes: contentTypes2 }) => {
2649
- const { isLocalizedContentType: isLocalizedContentType2 } = getService("content-types");
2650
- const { getDefaultLocale: getDefaultLocale2 } = getService("locales");
2651
- if (!oldContentTypes) {
2652
- return;
2653
- }
2654
- for (const uid in contentTypes2) {
2655
- if (!oldContentTypes[uid]) {
2656
- continue;
2657
- }
2658
- const oldContentType = oldContentTypes[uid];
2659
- const contentType = contentTypes2[uid];
2660
- if (isLocalizedContentType2(oldContentType) && !isLocalizedContentType2(contentType)) {
2661
- const defaultLocale = await getDefaultLocale2() || DEFAULT_LOCALE.code;
2662
- await Promise.all([
2663
- // Delete all entities that are not in the default locale
2664
- strapi.db.query(uid).deleteMany({
2665
- where: { locale: { $ne: defaultLocale } }
2666
- }),
2667
- // Set locale to null for the rest
2668
- strapi.db.query(uid).updateMany({
2669
- where: { locale: { $eq: defaultLocale } },
2670
- data: { locale: null }
2671
- })
2672
- ]);
2673
- }
2674
- }
2675
- };
2676
- const register = ({ strapi: strapi2 }) => {
2677
- extendContentTypes(strapi2);
2678
- addContentManagerLocaleMiddleware(strapi2);
2679
- addContentTypeSyncHooks(strapi2);
2680
- };
2681
- const addContentManagerLocaleMiddleware = (strapi2) => {
2682
- strapi2.server.router.use("/content-manager/collection-types/:model", (ctx, next) => {
2683
- if (ctx.method === "POST" || ctx.method === "PUT") {
2684
- return validateLocaleCreation(ctx, next);
2685
- }
2686
- return next();
2687
- });
2688
- strapi2.server.router.use("/content-manager/single-types/:model", (ctx, next) => {
2689
- if (ctx.method === "POST" || ctx.method === "PUT") {
2690
- return validateLocaleCreation(ctx, next);
2691
- }
2692
- return next();
2693
- });
2694
- };
2695
- const addContentTypeSyncHooks = (strapi2) => {
2696
- strapi2.hook("strapi::content-types.beforeSync").register(disableContentType);
2697
- strapi2.hook("strapi::content-types.afterSync").register(enableContentType);
2698
- };
2699
- const extendContentTypes = (strapi2) => {
2700
- Object.values(strapi2.contentTypes).forEach((contentType) => {
2701
- const { attributes: attributes2 } = contentType;
2702
- ___default.default.set(attributes2, "locale", {
2703
- writable: true,
2704
- private: false,
2705
- configurable: false,
2706
- visible: false,
2707
- type: "string"
2708
- });
2709
- ___default.default.set(attributes2, "localizations", {
2710
- type: "relation",
2711
- relation: "oneToMany",
2712
- target: contentType.uid,
2713
- writable: false,
2714
- private: false,
2715
- configurable: false,
2716
- visible: false,
2717
- joinColumn: {
2718
- name: "document_id",
2719
- referencedColumn: "document_id",
2720
- referencedTable: strapi2.db.metadata.identifiers.getTableName(contentType.collectionName),
2721
- // ensure the population will not include the results we already loaded
2722
- on({ results }) {
2723
- return {
2724
- id: {
2725
- $notIn: results.map((r) => r.id)
2726
- }
2727
- };
2728
- }
2729
- }
2730
- });
2731
- });
2732
- if (strapi2.plugin("graphql")) {
2733
- graphqlProvider({ strapi: strapi2 }).register();
2734
- }
2735
- };
2736
- const info = {
2737
- singularName: "locale",
2738
- pluralName: "locales",
2739
- collectionName: "locales",
2740
- displayName: "Locale",
2741
- description: ""
2742
- };
2743
- const options = {};
2744
- const pluginOptions = {
2745
- "content-manager": {
2746
- visible: false
2747
- },
2748
- "content-type-builder": {
2749
- visible: false
2750
- }
2751
- };
2752
- const attributes = {
2753
- name: {
2754
- type: "string",
2755
- min: 1,
2756
- max: 50,
2757
- configurable: false
2758
- },
2759
- code: {
2760
- type: "string",
2761
- unique: true,
2762
- configurable: false
2763
- }
2764
- };
2765
- const schema = {
2766
- info,
2767
- options,
2768
- pluginOptions,
2769
- attributes
2770
- };
2771
- const locale = {
2772
- schema
2773
- };
2774
- const contentTypes$1 = {
2775
- locale
2776
- };
2777
- const actions = [
2778
- {
2779
- section: "settings",
2780
- category: "Internationalization",
2781
- subCategory: "Locales",
2782
- pluginName: "i18n",
2783
- displayName: "Create",
2784
- uid: "locale.create"
2785
- },
2786
- {
2787
- section: "settings",
2788
- category: "Internationalization",
2789
- subCategory: "Locales",
2790
- pluginName: "i18n",
2791
- displayName: "Read",
2792
- uid: "locale.read",
2793
- aliases: [
2794
- { actionId: "plugin::content-manager.explorer.read", subjects: ["plugin::i18n.locale"] }
2795
- ]
2796
- },
2797
- {
2798
- section: "settings",
2799
- category: "Internationalization",
2800
- subCategory: "Locales",
2801
- pluginName: "i18n",
2802
- displayName: "Update",
2803
- uid: "locale.update"
2943
+ name: "Zarma (Niger) (dje-NE)"
2804
2944
  },
2805
2945
  {
2806
- section: "settings",
2807
- category: "Internationalization",
2808
- subCategory: "Locales",
2809
- pluginName: "i18n",
2810
- displayName: "Delete",
2811
- uid: "locale.delete"
2946
+ code: "zu",
2947
+ name: "Zulu (zu)"
2948
+ },
2949
+ {
2950
+ code: "zu-ZA",
2951
+ name: "Zulu (South Africa) (zu-ZA)"
2812
2952
  }
2813
2953
  ];
2814
- const addLocalesPropertyIfNeeded = ({ value: action }) => {
2815
- const {
2816
- section,
2817
- options: { applyToProperties }
2818
- } = action;
2819
- if (section !== "contentTypes") {
2820
- return;
2821
- }
2822
- if (fp.isArray(applyToProperties) && applyToProperties.includes("locales")) {
2823
- return;
2824
- }
2825
- action.options.applyToProperties = fp.isArray(applyToProperties) ? applyToProperties.concat("locales") : ["locales"];
2826
- };
2827
- const shouldApplyLocalesPropertyToSubject = ({ property, subject }) => {
2828
- if (property === "locales") {
2829
- const model = strapi.getModel(subject);
2830
- return getService("content-types").isLocalizedContentType(model);
2831
- }
2832
- return true;
2833
- };
2834
- const addAllLocalesToPermissions = async (permissions2) => {
2835
- const { actionProvider } = strapi.service("admin::permission");
2836
- const { find: findAllLocales } = getService("locales");
2837
- const allLocales = await findAllLocales();
2838
- const allLocalesCode = allLocales.map(fp.prop("code"));
2839
- return Promise.all(
2840
- permissions2.map(async (permission) => {
2841
- const { action, subject } = permission;
2842
- const appliesToLocalesProperty = await actionProvider.appliesToProperty(
2843
- "locales",
2844
- action,
2845
- subject
2954
+ const getInitLocale = () => {
2955
+ const envLocaleCode = process.env.STRAPI_PLUGIN_I18N_INIT_LOCALE_CODE;
2956
+ if (envLocaleCode) {
2957
+ const matchingLocale = isoLocales.find(({ code }) => code === envLocaleCode);
2958
+ if (!matchingLocale) {
2959
+ throw new Error(
2960
+ "Unknown locale code provided in the environment variable STRAPI_PLUGIN_I18N_INIT_LOCALE_CODE"
2846
2961
  );
2847
- if (!appliesToLocalesProperty) {
2848
- return permission;
2849
- }
2850
- const oldPermissionProperties = fp.getOr({}, "properties", permission);
2851
- return { ...permission, properties: { ...oldPermissionProperties, locales: allLocalesCode } };
2852
- })
2853
- );
2854
- };
2855
- const syncSuperAdminPermissionsWithLocales = async () => {
2856
- const roleService = strapi.service("admin::role");
2857
- const permissionService = strapi.service("admin::permission");
2858
- const superAdminRole = await roleService.getSuperAdmin();
2859
- if (!superAdminRole) {
2860
- return;
2861
- }
2862
- const superAdminPermissions = await permissionService.findMany({
2863
- where: {
2864
- role: {
2865
- id: superAdminRole.id
2866
- }
2867
- }
2868
- });
2869
- const newSuperAdminPermissions = await addAllLocalesToPermissions(superAdminPermissions);
2870
- await roleService.assignPermissions(superAdminRole.id, newSuperAdminPermissions);
2871
- };
2872
- const registerI18nActions = async () => {
2873
- const { actionProvider } = strapi.service("admin::permission");
2874
- await actionProvider.registerMany(actions);
2875
- };
2876
- const registerI18nActionsHooks = () => {
2877
- const { actionProvider } = strapi.service("admin::permission");
2878
- const { hooks } = strapi.service("admin::role");
2879
- actionProvider.hooks.appliesPropertyToSubject.register(shouldApplyLocalesPropertyToSubject);
2880
- hooks.willResetSuperAdminPermissions.register(addAllLocalesToPermissions);
2881
- };
2882
- const updateActionsProperties = () => {
2883
- const { actionProvider } = strapi.service("admin::permission");
2884
- actionProvider.hooks.willRegister.register(addLocalesPropertyIfNeeded);
2885
- actionProvider.values().forEach((action) => addLocalesPropertyIfNeeded({ value: action }));
2886
- };
2887
- const i18nActionsService = {
2888
- actions,
2889
- registerI18nActions,
2890
- registerI18nActionsHooks,
2891
- updateActionsProperties,
2892
- syncSuperAdminPermissionsWithLocales
2893
- };
2894
- const localesPropertyHandler = async ({ action, section }) => {
2895
- const { actionProvider } = strapi.service("admin::permission");
2896
- const locales2 = await getService("locales").find();
2897
- if (fp.isEmpty(locales2)) {
2898
- return;
2899
- }
2900
- for (const subject of section.subjects) {
2901
- const applies = await actionProvider.appliesToProperty("locales", action.actionId, subject.uid);
2902
- const hasLocalesProperty = subject.properties.find(
2903
- (property) => property.value === "locales"
2904
- );
2905
- if (applies && !hasLocalesProperty) {
2906
- subject.properties.push({
2907
- label: "Locales",
2908
- value: "locales",
2909
- children: locales2.map(({ name, code }) => ({ label: name || code, value: code }))
2910
- });
2911
- }
2912
- }
2913
- };
2914
- const registerLocalesPropertyHandler = () => {
2915
- const { sectionsBuilder } = strapi.service("admin::permission");
2916
- sectionsBuilder.addHandler("singleTypes", localesPropertyHandler);
2917
- sectionsBuilder.addHandler("collectionTypes", localesPropertyHandler);
2918
- };
2919
- const sectionsBuilderService = {
2920
- localesPropertyHandler,
2921
- registerLocalesPropertyHandler
2922
- };
2923
- const willRegisterPermission = (context) => {
2924
- const { permission, condition, user } = context;
2925
- const { subject, properties } = permission;
2926
- const isSuperAdmin = strapi.service("admin::role").hasSuperAdminRole(user);
2927
- if (isSuperAdmin) {
2928
- return;
2929
- }
2930
- const { locales: locales2 } = properties || {};
2931
- const { isLocalizedContentType: isLocalizedContentType2 } = getService("content-types");
2932
- if (!subject) {
2933
- return;
2934
- }
2935
- const ct = strapi.contentTypes[subject];
2936
- if (!isLocalizedContentType2(ct)) {
2937
- return;
2938
- }
2939
- if (locales2 === null) {
2940
- return;
2941
- }
2942
- condition.and({
2943
- locale: {
2944
- $in: locales2 || []
2945
2962
  }
2946
- });
2947
- };
2948
- const registerI18nPermissionsHandlers = () => {
2949
- const { engine } = strapi.service("admin::permission");
2950
- engine.hooks["before-register.permission"].register(willRegisterPermission);
2951
- };
2952
- const engineService = {
2953
- willRegisterPermission,
2954
- registerI18nPermissionsHandlers
2955
- };
2956
- const permissions = () => ({
2957
- actions: i18nActionsService,
2958
- sectionsBuilder: sectionsBuilderService,
2959
- engine: engineService
2960
- });
2961
- const sendDidInitializeEvent = async () => {
2962
- const { isLocalizedContentType: isLocalizedContentType2 } = getService("content-types");
2963
- const numberOfContentTypes = fp.reduce(
2964
- (sum, contentType) => isLocalizedContentType2(contentType) ? sum + 1 : sum,
2965
- 0
2966
- )(strapi.contentTypes);
2967
- await strapi.telemetry.send("didInitializeI18n", { groupProperties: { numberOfContentTypes } });
2968
- };
2969
- const sendDidUpdateI18nLocalesEvent = async () => {
2970
- const numberOfLocales = await getService("locales").count();
2971
- await strapi.telemetry.send("didUpdateI18nLocales", {
2972
- groupProperties: { numberOfLocales }
2973
- });
2974
- };
2975
- const metrics = () => ({
2976
- sendDidInitializeEvent,
2977
- sendDidUpdateI18nLocalesEvent
2978
- });
2979
- const syncNonLocalizedAttributes = async (sourceEntry, model) => {
2980
- const { copyNonLocalizedAttributes: copyNonLocalizedAttributes2 } = getService("content-types");
2981
- const nonLocalizedAttributes = copyNonLocalizedAttributes2(model, sourceEntry);
2982
- if (fp.isEmpty(nonLocalizedAttributes)) {
2983
- return;
2963
+ return { ...matchingLocale };
2984
2964
  }
2985
- const uid = model.uid;
2986
- const documentId = sourceEntry.documentId;
2987
- const locale2 = sourceEntry.locale;
2988
- const status = sourceEntry?.publishedAt ? "published" : "draft";
2989
- const localeEntriesToUpdate = await strapi.db.query(uid).findMany({
2990
- where: {
2991
- documentId,
2992
- publishedAt: status === "published" ? { $ne: null } : null,
2993
- locale: { $ne: locale2 }
2994
- },
2995
- select: ["locale", "id"]
2996
- });
2997
- const entryData = await strapi.documents(uid).omitComponentData(nonLocalizedAttributes);
2998
- await utils.async.map(localeEntriesToUpdate, async (entry) => {
2999
- const transformedData = await strapi.documents.utils.transformData(
3000
- fp.cloneDeep(nonLocalizedAttributes),
3001
- {
3002
- uid,
3003
- status,
3004
- locale: entry.locale,
3005
- allowMissingId: true
3006
- }
3007
- );
3008
- const componentData = await strapi.documents(uid).updateComponents(entry, transformedData);
3009
- await strapi.db.query(uid).update({
3010
- where: {
3011
- documentId,
3012
- publishedAt: status === "published" ? { $ne: null } : null,
3013
- locale: { $eq: entry.locale }
3014
- },
3015
- // The data we send to the update function is the entry data merged with
3016
- // the updated component data
3017
- data: Object.assign(fp.cloneDeep(entryData), componentData)
3018
- });
3019
- });
2965
+ return {
2966
+ code: "en",
2967
+ name: "English (en)"
2968
+ };
3020
2969
  };
3021
- const localizations = () => ({
3022
- syncNonLocalizedAttributes
3023
- });
2970
+ const DEFAULT_LOCALE = getInitLocale();
3024
2971
  const find = (params = {}) => strapi.db.query("plugin::i18n.locale").findMany({ where: params });
3025
2972
  const findById = (id) => strapi.db.query("plugin::i18n.locale").findOne({ where: { id } });
3026
2973
  const findByCode = (code) => strapi.db.query("plugin::i18n.locale").findOne({ where: { code } });
@@ -3088,91 +3035,6 @@ const getIsoLocales = () => isoLocales;
3088
3035
  const isoLocalesService = () => ({
3089
3036
  getIsoLocales
3090
3037
  });
3091
- const LOCALE_QUERY_FILTER = "locale";
3092
- const SINGLE_ENTRY_ACTIONS = ["findOne", "update", "delete"];
3093
- const BULK_ACTIONS = ["delete"];
3094
- const paramsContain = (key, params) => {
3095
- return fp.has(key, params.filters) || fp.isArray(params.filters) && params.filters.some((clause) => fp.has(key, clause)) || fp.isArray(fp.get("$and", params.filters)) && params.filters.$and.some((clause) => fp.has(key, clause));
3096
- };
3097
- const wrapParams = async (params = {}, ctx = {}) => {
3098
- const { action } = ctx;
3099
- if (fp.has(LOCALE_QUERY_FILTER, params)) {
3100
- if (params[LOCALE_QUERY_FILTER] === "all") {
3101
- return fp.omit(LOCALE_QUERY_FILTER, params);
3102
- }
3103
- return {
3104
- ...fp.omit(LOCALE_QUERY_FILTER, params),
3105
- filters: {
3106
- $and: [{ locale: params[LOCALE_QUERY_FILTER] }].concat(params.filters || [])
3107
- }
3108
- };
3109
- }
3110
- const entityDefinedById = paramsContain("id", params) && SINGLE_ENTRY_ACTIONS.includes(action);
3111
- const entitiesDefinedByIds = paramsContain("id.$in", params) && BULK_ACTIONS.includes(action);
3112
- if (entityDefinedById || entitiesDefinedByIds) {
3113
- return params;
3114
- }
3115
- const { getDefaultLocale: getDefaultLocale2 } = getService("locales");
3116
- return {
3117
- ...params,
3118
- filters: {
3119
- $and: [{ locale: await getDefaultLocale2() }].concat(params.filters || [])
3120
- }
3121
- };
3122
- };
3123
- const decorator = (service) => ({
3124
- /**
3125
- * Wraps result
3126
- * @param {object} result - result object of query
3127
- * @param {object} ctx - Query context
3128
- * @param {object} ctx.model - Model that is being used
3129
- */
3130
- async wrapResult(result = {}, ctx = {}) {
3131
- return service.wrapResult.call(this, result, ctx);
3132
- },
3133
- /**
3134
- * Wraps query options. In particular will add default locale to query params
3135
- * @param {object} params - Query options object (params, data, files, populate)
3136
- * @param {object} ctx - Query context
3137
- * @param {object} ctx.model - Model that is being used
3138
- */
3139
- async wrapParams(params = {}, ctx = {}) {
3140
- const wrappedParams = await service.wrapParams.call(this, params, ctx);
3141
- const model = strapi.getModel(ctx.uid);
3142
- const { isLocalizedContentType: isLocalizedContentType2 } = getService("content-types");
3143
- if (!isLocalizedContentType2(model)) {
3144
- return wrappedParams;
3145
- }
3146
- return wrapParams(wrappedParams, ctx);
3147
- },
3148
- /**
3149
- * Find an entry or several if fetching all locales
3150
- * @param {string} uid - Model uid
3151
- * @param {object} opts - Query options object (params, data, files, populate)
3152
- */
3153
- async findMany(uid, opts) {
3154
- const model = strapi.getModel(uid);
3155
- const { isLocalizedContentType: isLocalizedContentType2 } = getService("content-types");
3156
- if (!isLocalizedContentType2(model)) {
3157
- return service.findMany.call(this, uid, opts);
3158
- }
3159
- const { kind } = model;
3160
- if (kind === "singleType") {
3161
- if (opts[LOCALE_QUERY_FILTER] === "all") {
3162
- const wrappedParams = await this.wrapParams(opts, { uid, action: "findMany" });
3163
- const query = strapi.get("query-params").transform(uid, wrappedParams);
3164
- const entities = await strapi.db.query(uid).findMany(query);
3165
- return this.wrapResult(entities, { uid, action: "findMany" });
3166
- }
3167
- return service.findMany.call(this, uid, opts);
3168
- }
3169
- return service.findMany.call(this, uid, opts);
3170
- }
3171
- });
3172
- const entityServiceDecorator = () => ({
3173
- decorator,
3174
- wrapParams
3175
- });
3176
3038
  const {
3177
3039
  isRelationalAttribute,
3178
3040
  getVisibleAttributes,
@@ -3297,13 +3159,33 @@ const contentTypes = () => ({
3297
3159
  fillNonLocalizedAttributes,
3298
3160
  getNestedPopulateOfNonLocalizedAttributes
3299
3161
  });
3162
+ const LOCALIZATION_FIELDS = ["locale", "localizations"];
3163
+ const sanitize = ({ strapi: strapi2 }) => {
3164
+ const { isLocalizedContentType: isLocalizedContentType2 } = getService("content-types");
3165
+ const sanitizeLocalizationFields = fp.curry(
3166
+ (schema2, entity) => utils.traverseEntity(
3167
+ ({ key, schema: schema22 }, { remove }) => {
3168
+ const isLocalized = isLocalizedContentType2(schema22);
3169
+ const isLocalizationField = LOCALIZATION_FIELDS.includes(key);
3170
+ if (!isLocalized && isLocalizationField) {
3171
+ remove(key);
3172
+ }
3173
+ },
3174
+ { schema: schema2, getModel: strapi2.getModel.bind(strapi2) },
3175
+ entity
3176
+ )
3177
+ );
3178
+ return {
3179
+ sanitizeLocalizationFields
3180
+ };
3181
+ };
3300
3182
  const services = {
3301
3183
  permissions,
3302
3184
  metrics,
3303
3185
  localizations,
3304
3186
  locales,
3187
+ sanitize,
3305
3188
  "iso-locales": isoLocalesService,
3306
- "entity-service-decorator": entityServiceDecorator,
3307
3189
  "content-types": contentTypes
3308
3190
  };
3309
3191
  const admin = {