Skip to content

Add more content languages#125

Merged
paulocastellano merged 14 commits into
trypostit:mainfrom
rallisf1:feat/content_languages
Jul 3, 2026
Merged

Add more content languages#125
paulocastellano merged 14 commits into
trypostit:mainfrom
rallisf1:feat/content_languages

Conversation

@rallisf1

Copy link
Copy Markdown
Contributor

Should close #124

Let me know if you need changes.

@paulocastellano

Copy link
Copy Markdown
Contributor

@rallisf1 i will review it soon!

Thank you for submit.

The new content-language options were hand-duplicated across request
validation, the UI picker, and homepage detection, while the brand
analyzer's structured-output enum and the AI image prompt's language
name still only knew about en/pt-BR/es. That left autofill unable to
detect the new languages and made image text fall back to English for
them.

Introduce App\Enums\Workspace\ContentLanguage as the single source of
truth and derive every site from it:

- Store/UpdateWorkspaceRequest validate against ContentLanguage::values()
- BrandAnalyzer's language enum uses ContentLanguage::values()
- AiImageClient::languageName() resolves via the enum's englishName()
- HomepageMetaExtractor detects through ContentLanguage::fromHtmlLang()
- BrandForm consumes availableContentLanguages from the backend, like
  availableFonts/availableImageStyles, instead of a hardcoded list

Also fix two labels: nl "Nederlandse" -> "Nederlands", zh -> "中文".
Register the 12 additional languages (fr, de, it, nl, pl, el, ja, ko, zh,
ru, tr, ar) as available UI locales so the language switcher and the API
accept them, keeping the set in lockstep with the ContentLanguage enum.

- config/languages.php lists all 15 UI locales with their native names.
- ContentLanguage::isRtl() drives the document `dir`; SetLocale shares it
  to the Blade root and HandleInertiaRequests shares it to Inertia, and
  app.ts mirrors it on SPA navigations so RTL locales lay out correctly.
- dayjs imports the 12 new locales so dates localize instead of falling
  back to English.
- A LocalizationParityTest guards against key drift: every locale must
  ship every base translation file with exactly the keys of lang/en.
Add full lang/ translations for fr, de, it, nl, pl, el, ja, ko, zh, ru,
tr, and ar — all 23 base files each, with identical key trees to lang/en,
preserved :placeholders and plural forms, and native product terminology.
Extend the homepage-language detection dataset to assert all 15 supported
languages resolve from their <html lang> subtag, and pin the primary-subtag
matching so a malformed tag ("english") no longer resolves via a two-letter
prefix.
…I-language switch

Replace the plain content-language <Select> in the brand form with a
searchable LanguagePicker combobox (Popover + Command), matching the
FontPicker. i18n the combobox placeholder/search/empty strings across all
15 locales.

Switch the UI language via a full page reload instead of client-side dir
syncing, so the server-rendered <html dir> flips LTR<->RTL correctly
without a manual refresh.
Move the nav badge from the physical right-2 to the logical end-2 so it
sits at the end of the row in both directions (right in LTR, left in RTL).

Replace the hardcoded 'Beta' string with a global common.beta translation
key across all 15 locales.
…er the gaps with tests

- Brand-analyzer prompt now lists every supported language instead of only
  en/pt-BR/es, so onboarding autofill can detect the 12 added languages. The
  backtick-formatted list is built in BrandAnalyzer::instructions(), keeping the
  Blade clean and the enum free of prompt presentation.
- Translate the delete-confirmation keyword for el/ja/zh/ar (the four locales
  that still shipped the English "delete").
- Make the language and font comboboxes RTL-correct (logical ms-* instead of
  physical ml-*), and let the language combobox be searched by English name via
  a visually-hidden label (ContentLanguage::options() now exposes englishName).
- Correct the ContentLanguage class docblock: the enum is also the source of
  truth for the UI locales' text direction.

Tests: SetLocale middleware dir/RTL, isRtl and the full 15-language
englishName/label match arms, LLM language detection beyond en/es/pt-BR, and
store-path persistence of a non-default content language plus rejection of an
unsupported one.
…guage

isRtl() existed only to be mapped to an 'rtl'/'ltr' string in SetLocale, so the
boolean was the redundant concept. direction() returns the string the one caller
needs, which drops the null-safe-plus-nested-ternary from the middleware and lets
it resolve the language once with an explicit DEFAULT fallback.
The PopoverContent used w-[--reka-popover-trigger-width], which Tailwind v4
compiles to the invalid `width: --reka-popover-trigger-width`, so the dropdown
collapsed to its content width. Use the v4 CSS-variable syntax
w-(--reka-popover-trigger-width) so it resolves to var(...) and spans the full
trigger width, matching the SearchableSelect component.
- Extract ContentLanguageOption into @/types and use it for availableContentLanguages
  across BrandForm, BrandTab, Brand, Create, and the LanguagePicker options, so the
  englishName field that drives search is visible to TypeScript instead of being
  dropped silently by the pass-through prop types.
- Add BrandAnalyzerTest: assert the language schema enum equals the full 15-language
  set (guards against it shrinking back to a hardcoded subset behind ::fake()) and
  that instructions() lists every code.
- Isolate the "LLM language wins" autofill test by declaring the page as `en` while
  the LLM returns `de`, so it actually proves mergeLlm precedence instead of both
  paths agreeing.
- Cover SetLocale's cookie side effect: the default locale cookie is set on an
  invalid/absent cookie and left untouched for a valid locale.
The workspace switcher menu, the label filter, and the templates category
popover still used the v3 bracket syntax w-[--reka-*-trigger-width], which
Tailwind v4 compiles to an invalid `width: --reka-...` so the floating content
no longer matched its trigger. Switch them to the v4 CSS-variable syntax
w-(--reka-*-trigger-width), matching the FontPicker/LanguagePicker fix.
@paulocastellano

Copy link
Copy Markdown
Contributor

@rallisf1 i have improved your PR, adding it for app language too!

@paulocastellano paulocastellano merged commit 6f13e2c into trypostit:main Jul 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Support more content languages

2 participants