Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/www/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ const nextConfig = async (phase: string) => {
destination: '/:path*?locale=cn', // Rewrite it to the corresponding path without /cn
source: '/cn/:path*', // Match any path under /cn
},
{
destination: '/?locale=pt-br',
source: '/pt-br',
},
{
destination: '/:path*?locale=pt-br', // Rewrite it to the corresponding path without /pt-br
source: '/pt-br/:path*', // Match any path under /pt-br
},
];
},

Expand Down
3 changes: 3 additions & 0 deletions apps/www/src/app/(app)/_components/announcement-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ const i18n = {
en: {
description: 'MCP, AI, Upload, and more',
},
'pt-br': {
description: 'MCP, IA, Upload, e mais',
},
};

export function AnnouncementButton() {
Expand Down
7 changes: 7 additions & 0 deletions apps/www/src/app/(app)/_components/potion-lazy-block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ const i18n = {
github: 'GitHub',
potionDescription: 'A Notion-like AI template.',
},
'pt-br': {
buildYourRichTextEditor: 'Construa seu editor de texto',
description: 'Framework · Plugins · Componentes · Temas',
getStarted: 'Começar',
github: 'GitHub',
potionDescription: 'Um template de IA similar ao Notion.',
},
};

export function PotionLazyBlock() {
Expand Down
5 changes: 5 additions & 0 deletions apps/www/src/app/(app)/editors/editor-description.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ const i18n = {
description: 'Beautifully designed. Copy and paste into your apps.',
title: 'Building Editors for the Web',
},
'pt-br': {
browseEditors: 'Navegar por Editores',
description: 'Desenhado elegantemente. Copie e cole em suas aplicações.',
title: 'Construindo Editores para a Web',
},
};

export function EditorDescription() {
Expand Down
7 changes: 7 additions & 0 deletions apps/www/src/app/(app)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ const i18n = {
github: 'GitHub',
potionDescription: 'A Notion-like AI template.',
},
'pt-br': {
buildYourRichTextEditor: 'Construa seu editor de texto',
description: 'Framework · Plugins · Componentes · Temas',
getStarted: 'Começar',
github: 'GitHub',
potionDescription: 'Um template de IA similar ao Notion.',
},
};

const title = 'Build your rich-text editor';
Expand Down
39 changes: 26 additions & 13 deletions apps/www/src/components/languages-dropdown-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,26 @@ export function LanguagesDropdownMenu() {

const handleClick = (locale?: string) => {
const url = new URL(window.location.href);
const supportedLocales = ['cn', 'pt-br'];

if (locale) {
if (pathname?.includes(locale)) {
return;
}
if (!pathname) return;

const segments = pathname.split('/').filter((p) => !!p);
const currentLocale = supportedLocales.includes(segments[0])
? segments[0]
: undefined;

// If the target locale is the same as the current, do nothing
if (locale === currentLocale) return;

url.pathname = `/${locale}${pathname}`;
// Remove the current locale from the segments if it exists
const newSegments = currentLocale ? segments.slice(1) : segments;

if (locale) {
url.pathname = `/${locale}/${newSegments.join('/')}`;
url.searchParams.set('locale', locale);
} else {
if (!pathname?.includes('cn')) return;
if (pathname) {
const segments = pathname.split('/').filter((p) => !!p);
const newSegments = segments.filter((segment) => segment !== 'cn');
url.pathname =
newSegments.length > 0 ? `/${newSegments.join('/')}` : '/';
}

url.pathname = `/${newSegments.join('/')}`;
url.searchParams.delete('locale');
}

Expand Down Expand Up @@ -66,6 +69,16 @@ export function LanguagesDropdownMenu() {
中文
</button>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<button
type="button"
className="w-full cursor-pointer"
onClick={() => handleClick('pt-br')}
>
Português
<span className="text-xs text-muted-foreground">Beta</span>
</button>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
Expand Down
6 changes: 6 additions & 0 deletions apps/www/src/components/main-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ const i18n = {
editors: 'Editors',
templates: 'Templates',
},
'pt-br': {
components: 'Componentes',
docs: 'Documentação',
editors: 'Editores',
templates: 'Templates',
},
Comment on lines +30 to +35

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve pt-br locale when navigating

Adding the pt-br labels here makes the nav show Portuguese, but clicks still call getLocalizedPath(locale, ...) which only prefixes cn, so after selecting Portuguese the next nav click drops ?locale=pt-br and pages revert to English; Portuguese users can’t stay localized.

Useful? React with 👍 / 👎.

};

export function MainNav({
Expand Down
5 changes: 5 additions & 0 deletions apps/www/src/components/open-in-plus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ const i18n = {
getAccess: 'Get all-access',
productionReady: 'Production-ready AI template and reusable components.',
},
'pt-br': {
buildYourEditor: 'Construa seu editor',
getAccess: 'Obtenha acesso completo',
productionReady: 'Template de IA pronto para produção e componentes reutilizáveis.',
},
};

export function OpenInPlus({ className }: { className?: string }) {
Expand Down
3 changes: 3 additions & 0 deletions apps/www/src/components/playground-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ const i18n = {
en: {
description: 'An AI editor',
},
'pt-br': {
description: 'Um editor de IA',
},
};

// TODO: sync
Expand Down
168 changes: 132 additions & 36 deletions apps/www/src/i18n/getI18nValues.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// #region en
import { aiValue as aiValueEn } from '@/registry/examples/values/ai-value';
import { alignValue as alignValueEn } from '@/registry/examples/values/align-value';
import { autoformatValue as autoformatValueEn } from '@/registry/examples/values/autoformat-value';
Expand All @@ -6,6 +7,46 @@ import { basicMarksValue as basicMarksValueEn } from '@/registry/examples/values
import { basicNodesValue as basicNodesValueEn } from '@/registry/examples/values/basic-nodes-value';
import { blockMenuValue as blockMenuValueEn } from '@/registry/examples/values/block-menu-value';
import { blockSelectionValue as blockSelectionValueEn } from '@/registry/examples/values/block-selection-value';
import { columnValue as columnValueEn } from '@/registry/examples/values/column-value';
import { copilotValue as copilotValueEn } from '@/registry/examples/values/copilot-value';
import { cursorOverlayValue as cursorOverlayValueEn } from '@/registry/examples/values/cursor-overlay-value';
import { dateValue as dateValueEn } from '@/registry/examples/values/date-value';
import { deserializeCsvValue as deserializeCsvValueEn } from '@/registry/examples/values/deserialize-csv-value';
import { deserializeDocxValue as deserializeDocxValueEn } from '@/registry/examples/values/deserialize-docx-value';
import { deserializeHtmlValue as deserializeHtmlValueEn } from '@/registry/examples/values/deserialize-html-value';
import { deserializeMdValue as deserializeMdValueEn } from '@/registry/examples/values/deserialize-md-value';
import { discussionValue as commentValueEn } from '@/registry/examples/values/discussion-value';
import { dndValue as dndValueEn } from '@/registry/examples/values/dnd-value';
import { editableVoidsValue as editableVoidsValueEn } from '@/registry/examples/values/editable-voids-value';
import { emojiValue as emojiValueEn } from '@/registry/examples/values/emoji-value';
import { equationValue as equationValueEn } from '@/registry/examples/values/equation-value';
import { excalidrawValue as excalidrawValueEn } from '@/registry/examples/values/excalidraw-value';
import {
exitBreakValue as exitBreakValueEn,
trailingBlockValue as trailingBlockValueEn,
} from '@/registry/examples/values/exit-break-value';
import { findReplaceValue as findReplaceValueEn } from '@/registry/examples/values/find-replace-value';
import { floatingToolbarValue as floatingToolbarValueEn } from '@/registry/examples/values/floating-toolbar-value';
import { fontValue as fontValueEn } from '@/registry/examples/values/font-value';
import { iframeValue as iframeValueEn } from '@/registry/examples/values/iframe-value';
import { indentValue as indentValueEn } from '@/registry/examples/values/indent-value';
import { lineHeightValue as lineHeightValueEn } from '@/registry/examples/values/line-height-value';
import { linkValue as linkValueEn } from '@/registry/examples/values/link-value';
import { listValue as listValueEn } from '@/registry/examples/values/list-value';
import { mediaValue as mediaValueEn } from '@/registry/examples/values/media-value';
import { mentionValue as mentionValueEn } from '@/registry/examples/values/mention-value';
import { blockPlaceholderValue as placeholderValueEn } from '@/registry/examples/values/placeholder-value';
import { playgroundValue as playgroundValueEn } from '@/registry/examples/values/playground-value';
import { pluginRulesValue as pluginRulesValueEn } from '@/registry/examples/values/plugin-rules-value';
import { previewMdValue as previewMdValueEn } from '@/registry/examples/values/preview-md-value';
import { slashCommandValue as slashCommandValueEn } from '@/registry/examples/values/slash-command-value';
import { tabbableValue as tabbableValueEn } from '@/registry/examples/values/tabbable-value';
import { tableValue as tableValueEn } from '@/registry/examples/values/table-value';
import { tocValue as tocValueEn } from '@/registry/examples/values/toc-value';
import { toggleValue as toggleValueEn } from '@/registry/examples/values/toggle-value';
// #endregion

// #region cn
import { aiValue as aiValueCn } from '@/registry/examples/values/cn/ai-value';
import { alignValue as alignValueCn } from '@/registry/examples/values/cn/align-value';
import { autoformatValue as autoformatValueCn } from '@/registry/examples/values/cn/autoformat-value';
Expand Down Expand Up @@ -51,43 +92,54 @@ import { tabbableValue as tabbableValueCn } from '@/registry/examples/values/cn/
import { tableValue as tableValueCn } from '@/registry/examples/values/cn/table-value';
import { tocValue as tocValueCn } from '@/registry/examples/values/cn/toc-value';
import { toggleValue as toggleValueCn } from '@/registry/examples/values/cn/toggle-value';
import { columnValue as columnValueEn } from '@/registry/examples/values/column-value';
import { copilotValue as copilotValueEn } from '@/registry/examples/values/copilot-value';
import { cursorOverlayValue as cursorOverlayValueEn } from '@/registry/examples/values/cursor-overlay-value';
import { dateValue as dateValueEn } from '@/registry/examples/values/date-value';
import { deserializeCsvValue as deserializeCsvValueEn } from '@/registry/examples/values/deserialize-csv-value';
import { deserializeDocxValue as deserializeDocxValueEn } from '@/registry/examples/values/deserialize-docx-value';
import { deserializeHtmlValue as deserializeHtmlValueEn } from '@/registry/examples/values/deserialize-html-value';
import { deserializeMdValue as deserializeMdValueEn } from '@/registry/examples/values/deserialize-md-value';
import { discussionValue as commentValueEn } from '@/registry/examples/values/discussion-value';
import { dndValue as dndValueEn } from '@/registry/examples/values/dnd-value';
import { editableVoidsValue as editableVoidsValueEn } from '@/registry/examples/values/editable-voids-value';
import { emojiValue as emojiValueEn } from '@/registry/examples/values/emoji-value';
import { equationValue as equationValueEn } from '@/registry/examples/values/equation-value';
import { excalidrawValue as excalidrawValueEn } from '@/registry/examples/values/excalidraw-value';
// #endregion

// #region pt-br
import { aiValue as aiValuePt } from '@/registry/examples/values/pt-br/ai-value';
import { alignValue as alignValuePt } from '@/registry/examples/values/pt-br/align-value';
import { autoformatValue as autoformatValuePt } from '@/registry/examples/values/pt-br/autoformat-value';
import { basicBlocksValue as basicBlocksValuePt } from '@/registry/examples/values/pt-br/basic-blocks-value';
import { basicMarksValue as basicMarksValuePt } from '@/registry/examples/values/pt-br/basic-marks-value';
import { basicNodesValue as basicNodesValuePt } from '@/registry/examples/values/pt-br/basic-nodes-value';
import { blockMenuValue as blockMenuValuePt } from '@/registry/examples/values/pt-br/block-menu-value';
import { blockSelectionValue as blockSelectionValuePt } from '@/registry/examples/values/pt-br/block-selection-value';
import { columnValue as columnValuePt } from '@/registry/examples/values/pt-br/column-value';
import { copilotValue as copilotValuePt } from '@/registry/examples/values/pt-br/copilot-value';
import { cursorOverlayValue as cursorOverlayValuePt } from '@/registry/examples/values/pt-br/cursor-overlay-value';
import { dateValue as dateValuePt } from '@/registry/examples/values/pt-br/date-value';
import { deserializeCsvValue as deserializeCsvValuePt } from '@/registry/examples/values/pt-br/deserialize-csv-value';
import { deserializeDocxValue as deserializeDocxValuePt } from '@/registry/examples/values/pt-br/deserialize-docx-value';
import { deserializeHtmlValue as deserializeHtmlValuePt } from '@/registry/examples/values/pt-br/deserialize-html-value';
import { deserializeMdValue as deserializeMdValuePt } from '@/registry/examples/values/pt-br/deserialize-md-value';
import { discussionValue as discussionValuePt } from '@/registry/examples/values/pt-br/discussion-value';
import { dndValue as dndValuePt } from '@/registry/examples/values/pt-br/dnd-value';
import { editableVoidsValue as editableVoidsValuePt } from '@/registry/examples/values/pt-br/editable-voids-value';
import { emojiValue as emojiValuePt } from '@/registry/examples/values/pt-br/emoji-value';
import { equationValue as equationValuePt } from '@/registry/examples/values/pt-br/equation-value';
import { excalidrawValue as excalidrawValuePt } from '@/registry/examples/values/pt-br/excalidraw-value';
import {
exitBreakValue as exitBreakValueEn,
trailingBlockValue as trailingBlockValueEn,
} from '@/registry/examples/values/exit-break-value';
import { findReplaceValue as findReplaceValueEn } from '@/registry/examples/values/find-replace-value';
import { floatingToolbarValue as floatingToolbarValueEn } from '@/registry/examples/values/floating-toolbar-value';
import { fontValue as fontValueEn } from '@/registry/examples/values/font-value';
import { iframeValue as iframeValueEn } from '@/registry/examples/values/iframe-value';
import { indentValue as indentValueEn } from '@/registry/examples/values/indent-value';
import { lineHeightValue as lineHeightValueEn } from '@/registry/examples/values/line-height-value';
import { linkValue as linkValueEn } from '@/registry/examples/values/link-value';
import { listValue as listValueEn } from '@/registry/examples/values/list-value';
import { mediaValue as mediaValueEn } from '@/registry/examples/values/media-value';
import { mentionValue as mentionValueEn } from '@/registry/examples/values/mention-value';
import { blockPlaceholderValue as placeholderValueEn } from '@/registry/examples/values/placeholder-value';
import { playgroundValue as playgroundValueEn } from '@/registry/examples/values/playground-value';
import { pluginRulesValue as pluginRulesValueEn } from '@/registry/examples/values/plugin-rules-value';
import { previewMdValue as previewMdValueEn } from '@/registry/examples/values/preview-md-value';
import { slashCommandValue as slashCommandValueEn } from '@/registry/examples/values/slash-command-value';
import { tabbableValue as tabbableValueEn } from '@/registry/examples/values/tabbable-value';
import { tableValue as tableValueEn } from '@/registry/examples/values/table-value';
import { tocValue as tocValueEn } from '@/registry/examples/values/toc-value';
import { toggleValue as toggleValueEn } from '@/registry/examples/values/toggle-value';
exitBreakValue as exitBreakValuePt,
trailingBlockValue as trailingBlockValuePt,
} from '@/registry/examples/values/pt-br/exit-break-value';
import { findReplaceValue as findReplaceValuePt } from '@/registry/examples/values/pt-br/find-replace-value';
import { floatingToolbarValue as floatingToolbarValuePt } from '@/registry/examples/values/pt-br/floating-toolbar-value';
import { fontValue as fontValuePt } from '@/registry/examples/values/pt-br/font-value';
import { iframeValue as iframeValuePt } from '@/registry/examples/values/pt-br/iframe-value';
import { indentValue as indentValuePt } from '@/registry/examples/values/pt-br/indent-value';
import { lineHeightValue as lineHeightValuePt } from '@/registry/examples/values/pt-br/line-height-value';
import { linkValue as linkValuePt } from '@/registry/examples/values/pt-br/link-value';
import { listValue as listValuePt } from '@/registry/examples/values/pt-br/list-value';
import { mediaValue as mediaValuePt } from '@/registry/examples/values/pt-br/media-value';
import { mentionValue as mentionValuePt } from '@/registry/examples/values/pt-br/mention-value';
import { blockPlaceholderValue as placeholderValuePt } from '@/registry/examples/values/pt-br/placeholder-value';
import { playgroundValue as playgroundValuePt } from '@/registry/examples/values/pt-br/playground-value';
import { previewMdValue as previewMdValuePt } from '@/registry/examples/values/pt-br/preview-md-value';
import { slashCommandValue as slashCommandValuePt } from '@/registry/examples/values/pt-br/slash-command-value';
import { tabbableValue as tabbableValuePt } from '@/registry/examples/values/pt-br/tabbable-value';
import { tableValue as tableValuePt } from '@/registry/examples/values/pt-br/table-value';
import { tocValue as tocValuePt } from '@/registry/examples/values/pt-br/toc-value';
import { toggleValue as toggleValuePt } from '@/registry/examples/values/pt-br/toggle-value';
// #endregion

const i18n = {
cn: {
Expand Down Expand Up @@ -180,6 +232,50 @@ const i18n = {
toggle: toggleValueEn,
trailingBlock: trailingBlockValueEn,
},
'pt-br': {
ai: aiValuePt,
autoformat: autoformatValuePt,
basicBlocks: basicBlocksValuePt,
basicMarks: basicMarksValuePt,
basicNodes: basicNodesValuePt,
blockMenu: blockMenuValuePt,
blockSelection: blockSelectionValuePt,
column: columnValuePt,
copilot: copilotValuePt,
cursorOverlay: cursorOverlayValuePt,
date: dateValuePt,
deserializeCsv: deserializeCsvValuePt,
deserializeDocx: deserializeDocxValuePt,
deserializeHtml: deserializeHtmlValuePt,
deserializeMd: deserializeMdValuePt,
discussion: discussionValuePt,
dnd: dndValuePt,
editableVoids: editableVoidsValuePt,
emoji: emojiValuePt,
equation: equationValuePt,
excalidraw: excalidrawValuePt,
exitBreak: exitBreakValuePt,
findReplace: findReplaceValuePt,
floatingToolbar: floatingToolbarValuePt,
font: fontValuePt,
iframe: iframeValuePt,
indent: indentValuePt,
lineHeight: lineHeightValuePt,
link: linkValuePt,
list: listValuePt,
media: mediaValuePt,
mention: mentionValuePt,
placeholder: placeholderValuePt,
playground: playgroundValuePt,
previewMd: previewMdValuePt,
slashCommand: slashCommandValuePt,
tabbable: tabbableValuePt,
table: tableValuePt,
'text-align': alignValuePt,
toc: tocValuePt,
toggle: toggleValuePt,
trailingBlock: trailingBlockValuePt,
},
};

export const getI18nValues = (locale: string) =>
Expand Down
Loading