trypost/app/Enums/Workspace/ContentLanguage.php
Axi 3baf2e9c41
Add Ukrainian as a supported platform language (#219)
* Default AI content language to Ukrainian

* Keep English as the default content language

* refactor: update content language handling in workspace creation

- Changed the default content language in CreateWorkspace to inherit the app's locale instead of defaulting to English.
- Updated related tests to reflect this change, ensuring that the content language aligns with the application's current locale settings.
- Cleaned up unnecessary code in the BrandTab component for better readability.

* Add Ukrainian as a full platform UI locale.

Wire uk into languages config with complete lang/uk translations and restore ContentLanguage↔UI parity checks.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Wire Ukrainian into dayjs and locale coverage tests.

Import the uk dayjs locale with Monday week-start and cover uk/uk-UA in brand autofill and SetLocale assertions.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Harden Ukrainian UI and AI content-language coverage.

Cover generator/reviewer/humanizer/image prompts, workspace pickers, persona labels, UI locale switch, and README for uk as a first-class platform language.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add Ukrainian Pinterest form strings after main merge.

Restore lang/uk posts.php key parity for the new pin title and destination link fields.

Co-authored-by: Cursor <cursoragent@cursor.com>

* List every supported UI language in the README.

Replace the abbreviated multi-language blurb with the full locale set from config.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Paulo Castellano <paulo@castellanos.llc>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 19:45:30 -03:00

149 lines
4.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Enums\Workspace;
/**
* The set of languages the app supports, and the single source of truth for it:
* request validation, the brand analyzer's structured-output enum, homepage
* language detection, the AI image prompt's language name, the content-language
* picker options, and the right-to-left direction of the UI all derive from it.
*
* The string value is the language code stored on the workspace and passed
* straight to the content prompts (`content_language`); the same codes also back
* the application's UI locales, so `direction()` drives the document `dir` attribute.
*/
enum ContentLanguage: string
{
case English = 'en';
case Ukrainian = 'uk';
case PortugueseBrazil = 'pt-BR';
case Spanish = 'es';
case French = 'fr';
case German = 'de';
case Italian = 'it';
case Dutch = 'nl';
case Polish = 'pl';
case Greek = 'el';
case Japanese = 'ja';
case Korean = 'ko';
case Chinese = 'zh';
case Russian = 'ru';
case Turkish = 'tr';
case Arabic = 'ar';
public const DEFAULT = self::English;
/**
* The language's own name, shown in the content-language picker.
*/
public function label(): string
{
return match ($this) {
self::English => 'English',
self::Ukrainian => 'Українська',
self::PortugueseBrazil => 'Português (Brasil)',
self::Spanish => 'Español',
self::French => 'Français',
self::German => 'Deutsch',
self::Italian => 'Italiano',
self::Dutch => 'Nederlands',
self::Polish => 'Polski',
self::Greek => 'Ελληνικά',
self::Japanese => '日本語',
self::Korean => '한국어',
self::Chinese => '中文',
self::Russian => 'Русский',
self::Turkish => 'Türkçe',
self::Arabic => 'العربية',
};
}
/**
* The English name of the language, injected into AI image prompts so the
* in-image text is rendered in the workspace's content language.
*/
public function englishName(): string
{
return match ($this) {
self::English => 'English',
self::Ukrainian => 'Ukrainian',
self::PortugueseBrazil => 'Brazilian Portuguese',
self::Spanish => 'Spanish',
self::French => 'French',
self::German => 'German',
self::Italian => 'Italian',
self::Dutch => 'Dutch',
self::Polish => 'Polish',
self::Greek => 'Greek',
self::Japanese => 'Japanese',
self::Korean => 'Korean',
self::Chinese => 'Chinese',
self::Russian => 'Russian',
self::Turkish => 'Turkish',
self::Arabic => 'Arabic',
};
}
/**
* The text direction (`ltr` or `rtl`) for the document root when this is the
* active UI locale — only Arabic is written right to left.
*/
public function direction(): string
{
return $this === self::Arabic ? 'rtl' : 'ltr';
}
/**
* Resolve a raw `<html lang>` value (e.g. "pt-PT", "zh-Hans") to a supported
* language by matching its primary subtag, or null if none is supported.
*/
public static function fromHtmlLang(string $lang): ?self
{
$subtag = self::primarySubtag($lang);
if (strlen($subtag) < 2) {
return null;
}
foreach (self::cases() as $language) {
if (self::primarySubtag($language->value) === $subtag) {
return $language;
}
}
return null;
}
/**
* The lowercased primary subtag of a BCP 47 language tag ("pt-BR" => "pt").
*/
private static function primarySubtag(string $tag): string
{
return strtolower(explode('-', trim($tag), 2)[0]);
}
/**
* @return array<int, string>
*/
public static function values(): array
{
return array_map(fn (self $language) => $language->value, self::cases());
}
/**
* @return array<int, array{value: string, label: string, englishName: string}>
*/
public static function options(): array
{
return array_map(
fn (self $language) => [
'value' => $language->value,
'label' => $language->label(),
'englishName' => $language->englishName(),
],
self::cases(),
);
}
}