feat: add i18n to admin panel (#1326)
Co-authored-by: shikhantmaungs <shinkhantmaungs@gmail.com> Co-authored-by: Thomas Ghysels <info@thomasg.be> Co-authored-by: Kokutse Djoguenou <kokutse@Kokutses-MacBook-Pro.local> Co-authored-by: Christian Gil <47041342+ChrisGV04@users.noreply.github.com> Co-authored-by: Łukasz Rabiec <lukaszrabiec@gmail.com> Co-authored-by: Jenny <jennifer.eberlei@gmail.com> Co-authored-by: Hung Vu <hunghvu2017@gmail.com> Co-authored-by: Shin Khant Maung <101539335+shinkhantmaungs@users.noreply.github.com> Co-authored-by: Carlo Brualdi <carlo.brualdi@gmail.com> Co-authored-by: Ariel Tonglet <ariel.tonglet@gmail.com> Co-authored-by: Roman Ryzhikov <general+github@ya.ru> Co-authored-by: maekoya <maekoya@stromatolite.jp> Co-authored-by: Emilia Trollros <3m1l1a@emiliatrollros.se> Co-authored-by: Kokutse J Djoguenou <90865585+Julesdj@users.noreply.github.com> Co-authored-by: Mitch Dries <mitch.dries@gmail.com> BREAKING CHANGE: If you assigned labels to collections, globals or block names, you need to update your config! Your GraphQL schema and generated Typescript interfaces may have changed. Payload no longer uses labels for code based naming. To prevent breaking changes to your GraphQL API and typescript types in your project, you can assign the below properties to match what Payload previously generated for you from labels. On Collections Use `graphQL.singularName`, `graphQL.pluralName` for GraphQL schema names. Use `typescript.interface` for typescript generation name. On Globals Use `graphQL.name` for GraphQL Schema name. Use `typescript.interface` for typescript generation name. On Blocks (within Block fields) Use `graphQL.singularName` for graphQL schema names.
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const BeforeLogin: React.FC = () => {
|
||||
const BeforeLogin: React.FC<{i18n}> = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
<h3>Welcome</h3>
|
||||
<h3>{t('general:welcome')}</h3>
|
||||
<p>
|
||||
This demo is a set up to configure Payload for the develop and testing of features. To see a product demo of a Payload project
|
||||
please visit:
|
||||
|
||||
@@ -51,6 +51,15 @@ export default buildConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
resources: {
|
||||
en: {
|
||||
general: {
|
||||
dashboard: 'Home',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
collections: [
|
||||
{
|
||||
slug: 'users',
|
||||
@@ -59,13 +68,28 @@ export default buildConfig({
|
||||
},
|
||||
{
|
||||
slug,
|
||||
labels: {
|
||||
singular: {
|
||||
en: 'Post en',
|
||||
es: 'Post es',
|
||||
},
|
||||
plural: {
|
||||
en: 'Posts en',
|
||||
es: 'Posts es',
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
description: { en: 'Description en', es: 'Description es' },
|
||||
listSearchableFields: ['title', 'description', 'number'],
|
||||
group: 'One',
|
||||
group: { en: 'One', es: 'Una' },
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: {
|
||||
en: 'Title en',
|
||||
es: 'Title es',
|
||||
},
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
@@ -81,7 +105,7 @@ export default buildConfig({
|
||||
{
|
||||
slug: 'group-one-collection-ones',
|
||||
admin: {
|
||||
group: 'One',
|
||||
group: { en: 'One', es: 'Una' },
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
@@ -93,7 +117,7 @@ export default buildConfig({
|
||||
{
|
||||
slug: 'group-one-collection-twos',
|
||||
admin: {
|
||||
group: 'One',
|
||||
group: { en: 'One', es: 'Una' },
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
@@ -130,6 +154,10 @@ export default buildConfig({
|
||||
globals: [
|
||||
{
|
||||
slug: globalSlug,
|
||||
label: {
|
||||
en: 'Global en',
|
||||
es: 'Global es',
|
||||
},
|
||||
admin: {
|
||||
group: 'Group',
|
||||
},
|
||||
|
||||
@@ -158,7 +158,7 @@ describe('admin', () => {
|
||||
await page.locator('#action-delete').click();
|
||||
await page.locator('#confirm-delete').click();
|
||||
|
||||
await expect(page.locator(`text=Post "${id}" successfully deleted.`)).toBeVisible();
|
||||
await expect(page.locator(`text=Post en "${id}" successfully deleted.`)).toBeVisible();
|
||||
expect(page.url()).toContain(url.list);
|
||||
});
|
||||
|
||||
@@ -173,6 +173,29 @@ describe('admin', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('i18n', () => {
|
||||
test('should allow changing language', async () => {
|
||||
await page.goto(url.account);
|
||||
|
||||
const field = page.locator('.account__language .react-select');
|
||||
|
||||
await field.click({ delay: 100 });
|
||||
const options = page.locator('.rs__option');
|
||||
await options.locator('text=Español').click();
|
||||
|
||||
await expect(page.locator('.step-nav')).toContainText('Tablero');
|
||||
|
||||
await field.click({ delay: 100 });
|
||||
await options.locator('text=English').click();
|
||||
await field.click({ delay: 100 });
|
||||
await expect(page.locator('.form-submit .btn')).toContainText('Save');
|
||||
});
|
||||
|
||||
test('should allow custom translation', async () => {
|
||||
await expect(page.locator('.step-nav')).toContainText('Home');
|
||||
});
|
||||
});
|
||||
|
||||
describe('list view', () => {
|
||||
const tableRowLocator = 'table >> tbody >> tr';
|
||||
|
||||
@@ -318,6 +341,55 @@ describe('admin', () => {
|
||||
expect(await page.locator('.row-2 .cell-id').innerText()).toEqual(secondId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('i18n', () => {
|
||||
test('should display translated collections and globals config options', async () => {
|
||||
await page.goto(url.list);
|
||||
|
||||
// collection label
|
||||
await expect(page.locator('#nav-posts')).toContainText('Posts en');
|
||||
|
||||
// global label
|
||||
await expect(page.locator('#nav-global-global')).toContainText('Global en');
|
||||
|
||||
// view description
|
||||
await expect(page.locator('.view-description')).toContainText('Description en');
|
||||
});
|
||||
|
||||
test('should display translated field titles', async () => {
|
||||
await createPost();
|
||||
|
||||
// column controls
|
||||
await page.locator('.list-controls__toggle-columns').click();
|
||||
await expect(await page.locator('.column-selector__column >> text=Title en')).toHaveText('Title en');
|
||||
|
||||
// filters
|
||||
await page.locator('.list-controls__toggle-where').click();
|
||||
await page.locator('.where-builder__add-first-filter').click();
|
||||
await page.locator('.condition__field .rs__control').click();
|
||||
const options = page.locator('.rs__option');
|
||||
await expect(await options.locator('text=Title en')).toHaveText('Title en');
|
||||
|
||||
// list columns
|
||||
await expect(await page.locator('#heading-title .sort-column__label')).toHaveText('Title en');
|
||||
await expect(await page.locator('.search-filter input')).toHaveAttribute('placeholder', /(Title en)/);
|
||||
});
|
||||
|
||||
test('should use fallback language on field titles', async () => {
|
||||
// change language German
|
||||
await page.goto(url.account);
|
||||
const field = page.locator('.account__language .react-select');
|
||||
await field.click({ delay: 100 });
|
||||
const languageSelect = page.locator('.rs__option');
|
||||
// text field does not have a 'de' label
|
||||
await languageSelect.locator('text=Deutsch').click();
|
||||
|
||||
await page.goto(url.list);
|
||||
await page.locator('.list-controls__toggle-columns').click();
|
||||
// expecting the label to fall back to english as default fallbackLng
|
||||
await expect(await page.locator('.column-selector__column >> text=Title en')).toHaveText('Title en');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user