mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Fix field/collection translated name not resetting after being removed (#16131)
* reset existing field translations * remove unused and malformed settings translations * reset existing collection translations * safer handling in case translations isn't an array * inverse locales to reset logic for correctness * add tests Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
This commit is contained in:
@@ -9,9 +9,6 @@ fields:
|
||||
options:
|
||||
iconRight: title
|
||||
placeholder: $t:field_options.directus_settings.project_name_placeholder
|
||||
translations:
|
||||
language: en-US
|
||||
translations: Name
|
||||
width: half
|
||||
|
||||
- field: project_descriptor
|
||||
@@ -19,9 +16,6 @@ fields:
|
||||
options:
|
||||
iconRight: title
|
||||
placeholder: $t:field_options.directus_settings.project_name_placeholder
|
||||
translations:
|
||||
language: en-US
|
||||
translations: Name
|
||||
width: half
|
||||
|
||||
- field: project_url
|
||||
@@ -29,9 +23,6 @@ fields:
|
||||
options:
|
||||
iconRight: link
|
||||
placeholder: https://example.com
|
||||
translations:
|
||||
language: en-US
|
||||
translations: Website
|
||||
width: half
|
||||
|
||||
- field: default_language
|
||||
@@ -39,9 +30,6 @@ fields:
|
||||
options:
|
||||
iconRight: language
|
||||
placeholder: en-US
|
||||
translations:
|
||||
language: en-US
|
||||
translations: Default Language
|
||||
width: half
|
||||
|
||||
- field: branding_divider
|
||||
@@ -57,31 +45,20 @@ fields:
|
||||
- field: project_color
|
||||
interface: select-color
|
||||
note: $t:field_options.directus_settings.project_color_note
|
||||
translations:
|
||||
language: en-US
|
||||
translations: Brand Color
|
||||
width: half
|
||||
|
||||
- field: project_logo
|
||||
interface: file
|
||||
note: $t:field_options.directus_settings.project_logo_note
|
||||
translations:
|
||||
language: en-US
|
||||
translations: Brand Logo
|
||||
width: half
|
||||
|
||||
- field: public_foreground
|
||||
interface: file
|
||||
translations:
|
||||
language: en-US
|
||||
translations: Login Foreground
|
||||
width: half
|
||||
|
||||
- field: public_background
|
||||
interface: file
|
||||
translations:
|
||||
language: en-US
|
||||
translations: Login Background
|
||||
width: half
|
||||
|
||||
- field: public_note
|
||||
|
||||
82
app/src/stores/collections.test.ts
Normal file
82
app/src/stores/collections.test.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { setActivePinia } from 'pinia';
|
||||
import { beforeEach, expect, test, vi } from 'vitest';
|
||||
|
||||
import { i18n } from '@/lang';
|
||||
import { Collection } from '@directus/shared/types';
|
||||
import { merge } from 'lodash';
|
||||
import { useCollectionsStore } from './collections';
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
stubActions: false,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const mockCollection = {
|
||||
collection: 'a',
|
||||
meta: {},
|
||||
schema: {},
|
||||
} as Collection;
|
||||
|
||||
test('parseField action should translate field name when translations are added then removed', async () => {
|
||||
const collectionsStore = useCollectionsStore();
|
||||
|
||||
const mockCollectionWithTranslations = merge({}, mockCollection, {
|
||||
meta: {
|
||||
translations: [
|
||||
{
|
||||
language: 'en-US',
|
||||
translation: 'Collection A en-US',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
collectionsStore.collections = [mockCollectionWithTranslations].map(collectionsStore.prepareCollectionForApp);
|
||||
expect(collectionsStore.collections[0].name).toEqual('Collection A en-US');
|
||||
expect(i18n.global.te(`collection_names.${mockCollection.collection}`)).toBe(true);
|
||||
|
||||
const mockCollectionWithMissingTranslations = merge({}, mockCollection, {
|
||||
meta: {
|
||||
translations: [
|
||||
{
|
||||
language: 'zh-CN',
|
||||
translation: 'Collection A zh-CN',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
collectionsStore.collections = [mockCollectionWithMissingTranslations].map(collectionsStore.prepareCollectionForApp);
|
||||
expect(collectionsStore.collections[0].name).toEqual('A');
|
||||
expect(i18n.global.te(`collection_names.${mockCollection.collection}`)).toBe(false);
|
||||
});
|
||||
|
||||
test('parseField action should translate field name when all translations are removed', async () => {
|
||||
const collectionsStore = useCollectionsStore();
|
||||
|
||||
const mockCollectionWithTranslations = merge({}, mockCollection, {
|
||||
meta: {
|
||||
translations: [
|
||||
{
|
||||
language: 'en-US',
|
||||
translation: 'Collection A en-US',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
collectionsStore.collections = [mockCollectionWithTranslations].map(collectionsStore.prepareCollectionForApp);
|
||||
expect(collectionsStore.collections[0].name).toEqual('Collection A en-US');
|
||||
expect(i18n.global.te(`collection_names.${mockCollection.collection}`)).toBe(true);
|
||||
|
||||
const mockCollectionWithoutTranslations = merge({}, mockCollection, {
|
||||
meta: {
|
||||
translations: null,
|
||||
},
|
||||
});
|
||||
collectionsStore.collections = [mockCollectionWithoutTranslations].map(collectionsStore.prepareCollectionForApp);
|
||||
expect(collectionsStore.collections[0].name).toEqual('A');
|
||||
expect(i18n.global.te(`collection_names.${mockCollection.collection}`)).toBe(false);
|
||||
});
|
||||
@@ -53,7 +53,18 @@ export const useCollectionsStore = defineStore({
|
||||
let name = formatTitle(collection.collection);
|
||||
const type = getCollectionType(collection);
|
||||
|
||||
if (collection.meta && !isNil(collection.meta.translations)) {
|
||||
const localesToKeep =
|
||||
collection.meta && !isNil(collection.meta.translations) && Array.isArray(collection.meta.translations)
|
||||
? collection.meta.translations.map((translation) => translation.language)
|
||||
: [];
|
||||
|
||||
for (const locale of i18n.global.availableLocales) {
|
||||
if (i18n.global.te(`collection_names.${collection.collection}`, locale) && !localesToKeep.includes(locale)) {
|
||||
i18n.global.mergeLocaleMessage(locale, { collection_names: { [collection.collection]: undefined } });
|
||||
}
|
||||
}
|
||||
|
||||
if (collection.meta && !isNil(collection.meta.translations) && Array.isArray(collection.meta.translations)) {
|
||||
for (let i = 0; i < collection.meta.translations.length; i++) {
|
||||
const { language, translation, singular, plural } = collection.meta.translations[i];
|
||||
|
||||
|
||||
91
app/src/stores/fields.test.ts
Normal file
91
app/src/stores/fields.test.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { setActivePinia } from 'pinia';
|
||||
import { beforeEach, expect, test, vi } from 'vitest';
|
||||
|
||||
import { i18n } from '@/lang';
|
||||
import { Field } from '@directus/shared/types';
|
||||
import { merge } from 'lodash';
|
||||
import { useFieldsStore } from './fields';
|
||||
|
||||
beforeEach(() => {
|
||||
setActivePinia(
|
||||
createTestingPinia({
|
||||
createSpy: vi.fn,
|
||||
stubActions: false,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
const mockField = {
|
||||
collection: 'a',
|
||||
field: 'name',
|
||||
type: 'string',
|
||||
schema: {},
|
||||
meta: {
|
||||
collection: 'a',
|
||||
field: 'name',
|
||||
options: null,
|
||||
display_options: null,
|
||||
note: null,
|
||||
validation_message: null,
|
||||
},
|
||||
} as Field;
|
||||
|
||||
test('parseField action should translate field name when translations are added then removed', async () => {
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
const mockFieldWithTranslations = merge({}, mockField, {
|
||||
meta: {
|
||||
translations: [
|
||||
{
|
||||
language: 'en-US',
|
||||
translation: 'Name en-US',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
fieldsStore.fields = [mockFieldWithTranslations].map(fieldsStore.parseField);
|
||||
expect(fieldsStore.fields[0].name).toEqual('Name en-US');
|
||||
expect(i18n.global.te(`fields.${mockField.collection}.${mockField.field}`)).toBe(true);
|
||||
|
||||
const mockFieldWithoutTranslations = merge({}, mockField, {
|
||||
meta: {
|
||||
translations: [
|
||||
{
|
||||
language: 'zh-CN',
|
||||
translation: 'Name zh-CN',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
fieldsStore.fields = [mockFieldWithoutTranslations].map(fieldsStore.parseField);
|
||||
expect(fieldsStore.fields[0].name).toEqual('Name');
|
||||
expect(i18n.global.te(`fields.${mockField.collection}.${mockField.field}`)).toBe(false);
|
||||
});
|
||||
|
||||
test('parseField action should translate field name when translations are added and reset when removed', async () => {
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
const mockFieldWithTranslations = merge({}, mockField, {
|
||||
meta: {
|
||||
translations: [
|
||||
{
|
||||
language: 'en-US',
|
||||
translation: 'name en-US',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
fieldsStore.fields = [mockFieldWithTranslations].map(fieldsStore.parseField);
|
||||
expect(fieldsStore.fields[0].name).toEqual('name en-US');
|
||||
expect(i18n.global.te(`fields.${mockField.collection}.${mockField.field}`)).toBe(true);
|
||||
|
||||
const mockFieldWithoutTranslations = merge({}, mockField, {
|
||||
meta: {
|
||||
translations: null,
|
||||
},
|
||||
});
|
||||
fieldsStore.fields = [mockFieldWithoutTranslations].map(fieldsStore.parseField);
|
||||
expect(fieldsStore.fields[0].name).toEqual('Name');
|
||||
expect(i18n.global.te(`fields.${mockField.collection}.${mockField.field}`)).toBe(false);
|
||||
});
|
||||
@@ -72,7 +72,22 @@ export const useFieldsStore = defineStore({
|
||||
parseField(field: FieldRaw): Field {
|
||||
let name = formatTitle(field.field);
|
||||
|
||||
if (field.meta && !isNil(field.meta.translations) && field.meta.translations.length > 0) {
|
||||
const localesToKeep =
|
||||
field.meta && !isNil(field.meta.translations) && Array.isArray(field.meta.translations)
|
||||
? field.meta.translations.map((translation) => translation.language)
|
||||
: [];
|
||||
|
||||
for (const locale of i18n.global.availableLocales) {
|
||||
if (
|
||||
i18n.global.te(`fields.${field.collection}.${field.field}`, locale) &&
|
||||
!localesToKeep.includes(locale) &&
|
||||
!field.meta?.system
|
||||
) {
|
||||
i18n.global.mergeLocaleMessage(locale, { fields: { [field.collection]: { [field.field]: undefined } } });
|
||||
}
|
||||
}
|
||||
|
||||
if (field.meta && !isNil(field.meta.translations) && Array.isArray(field.meta.translations)) {
|
||||
for (let i = 0; i < field.meta.translations.length; i++) {
|
||||
const { language, translation } = field.meta.translations[i];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user