concept24/nova-astrotranslatable-ai

Concept24 Translatable AI Field for Laravel Nova. Based on Astrotomic Laravel Translatable.

Maintainers

Package info

bitbucket.org/concept24/nova-astrotranslatable-ai

pkg:composer/concept24/nova-astrotranslatable-ai

Statistics

Installs: 100

Dependents: 0

Suggesters: 0

v2.4.0 2026-04-02 12:00 UTC

This package is auto-updated.

Last update: 2026-04-02 12:01:04 UTC


README

A Laravel Nova package that makes any input field astrotomic/laravel-translatable compatible, with built-in AI-powered text generation, translation, image generation, and visual image analysis via OpenRouter.

Requirements

  • PHP: >= 8.0
  • laravel/nova: ^4.12 | ^5.0
  • astrotomic/laravel-translatable: ^11.10

Features

  • Supports almost all fields (including third-party ones)
  • Supports default validation automatically
  • Simple to implement with minimal code changes
  • Locale tabs to switch between different locale values of the same field
  • Double click on a tab to switch all fields to that locale
  • AI Text Generation — generate content using OpenAI, Anthropic, Google, xAI, or Perplexity models via OpenRouter
  • AI Translation — auto-translate field values to all configured locales
  • AI Image Generation — generate images using Google Gemini or OpenAI GPT image models
  • AI Visual Image Analysis — describe any image using vision-capable models (Google Gemini, OpenAI, xAI Grok, Anthropic Claude)
  • Multilanguage UI — all interface strings are translatable; language files are publishable
  • Supports nova-settings package

Known non-working fields

Limitations

  • The following methods can not be used, as this package uses them internally:
    • resolveUsing
    • fillUsing

Installation

Firstly, set up astrotomic/laravel-translatable.

Install the package via Composer:

composer require concept24/nova-astrotranslatable-ai

Publish the configuration files:

php artisan vendor:publish --tag="nova-translatable-config"

This publishes two config files:

  • config/nova-translatable.php — locales and display options
  • config/openrouter.php — AI/OpenRouter settings

Optionally publish the language files to customize UI translations:

php artisan vendor:publish --tag="nova-translatable-lang"

This copies ro.json and en.json to resources/lang/vendor/nova-astrotranslatable/. Published files take priority over the package defaults.

Configuration

config/nova-translatable.php

return [
    // Define the locales available for translation
    'locales' => ['en' => 'English', 'ro' => 'Romanian'],

    // Display Nova's current locale first in the tabs
    'prioritize_nova_locale' => true,

    // Tab layout: 'row', 'column', or 'none'
    'display_type' => 'row',

    // Locale select position: 'left-absolute', 'left-static', 'right-absolute', 'right-static'
    'locale_select.display_type' => 'right-absolute',

    // Auto-fill other locales when saving from this locale (e.g. 'en'), or null to disable
    'fill_other_locales_from' => null,
];

config/openrouter.php

All AI behaviour is controlled via this file — no code changes needed to adjust providers, families, or defaults.

return [
    // API credentials & timeouts
    'openrouter_api_key'   => env('OPENROUTER_API_KEY'),
    'field_with_ai_active' => env('FIELD_WITH_AI_ACTIVE', false),
    'timeout_text'         => env('OPENROUTER_TIMEOUT_TEXT', 30),
    'timeout_image'        => env('OPENROUTER_TIMEOUT_IMAGE', 60),

    // Text generation: providers (display order) and model families per provider
    'text_models' => [
        'providers' => ['openai', 'anthropic', 'google', 'x-ai', 'perplexity'],
        'families'  => [
            'openai'     => ['mini', 'standard'],
            'google'     => ['flash-lite-preview', 'flash-preview', 'pro-preview'],
            'anthropic'  => ['sonnet', 'opus', 'haiku'],
            'perplexity' => ['sonar-base', 'sonar-pro', 'sonar-deep-research', 'sonar-pro-search'],
            'x-ai'       => ['grok-fast'],
        ],
    ],

    // Vision analysis: providers, families, default model and max output tokens
    'vision_models' => [
        'providers' => ['google', 'openai', 'x-ai', 'anthropic'],
        'families'  => [
            'google'    => ['gemini-flash-preview'],
            'openai'    => ['gpt-full'],
            'x-ai'      => ['grok-fast'],
            'anthropic' => ['claude-sonnet', 'claude-opus'],
        ],
        'default_model'         => env('OPENROUTER_DEFAULT_VISION_MODEL', 'google/gemini-3-flash-preview'),
        'max_completion_tokens' => env('OPENROUTER_VISION_MAX_TOKENS', 1000),
    ],

    // Image generation: providers, aspect ratios and sizes (served to frontend at runtime)
    'image_generation' => [
        'providers'          => ['google', 'openai'],
        'openai_id_contains' => 'gpt',
        'provider_priority'  => ['google' => 0, 'openai' => 1],
        'aspect_ratios'      => [
            '1:1', '2:3', '3:2', '3:4', '4:2', '4:3', '4:5', '5:4',
            '9:16', '16:9', '21:9', '1:4', '4:1', '1:8', '8:1',
        ],
        'sizes' => ['0.5K', '1K', '2K', '4K'],
    ],
];

Add the following to your .env file:

OPENROUTER_API_KEY=your-openrouter-api-key
FIELD_WITH_AI_ACTIVE=true
OPENROUTER_TIMEOUT_TEXT=30
OPENROUTER_TIMEOUT_IMAGE=60
OPENROUTER_DEFAULT_VISION_MODEL=google/gemini-3-flash-preview
OPENROUTER_VISION_MAX_TOKENS=1000

Usage

Call ->translatable() on any field:

// Any Nova field
Text::make('Name')
    ->rules('required', 'min:2')
    ->translatable(),

// Any third-party input field
Multiselect::make('Football teams')
    ->rules('required')
    ->translatable(),

// Optionally pass custom locales on a per-field basis
Number::make('Population')
    ->translatable([
        'en' => 'English',
        'ro' => 'Romanian',
    ]),

AI Features

When FIELD_WITH_AI_ACTIVE=true, an AI button appears on translatable fields. The drawer has three tabs:

Tab 1 — Text Generation

Generate content using pre-defined templates:

TemplateDescription
generalGeneral purpose text
product_titleE-commerce product title (max 70 chars)
product_descriptionProduct description with optional title, attributes, benefits, brand, category, main image analysis
seo_meta_titleSEO meta title (max 60 chars, with optional site name suffix)
seo_meta_descriptionSEO meta description (max 155 chars)
social_media_postSocial media post with hashtags
testimonial_requestCustomer testimonial request email
button_nameCTA button label (max 4 words)
article_titleArticle/blog title (max 80 chars)
article_contentFull article with structured paragraphs

Available models (text): OpenAI · Anthropic · Google · xAI · Perplexity — fetched dynamically from OpenRouter, cached 1 hour.

Auto-translation — after generation, translate the result to all other configured locales directly from the results section.

Product context (on product resources):

  • Use product title, characteristics, benefits, brand, category in the prompt
  • "Toate" toggle checkbox to select/deselect all context options at once (indeterminate state when partially selected)
  • Use main gallery image for visual context (the image is read from disk, encoded as base64, and analyzed via a vision model before the description is generated)

Tab 2 — Image Generation

Generate images using Google Gemini or OpenAI GPT image models.

  • Custom aspect ratios: 1:1, 2:3, 3:2, 4:3, 16:9, 9:16, and more (extended set for Gemini 3.1)
  • Image sizes: 0.5K, 1K, 2K, 4K
  • Multi-image source upload — up to 3 source images for image-to-image generation (multimodal models); grid preview with per-image remove, drag & drop support
  • Image generation gallery — history preserved between generations
  • Download generated images directly from the interface

Tab 3 — Visual Image Analysis (Analiză vizuală imagine)

Describe an image in detail using a vision-capable LLM.

Image source (two sub-tabs):

  • Upload image — drag & drop or click to upload any image from your computer
  • From model gallery — select one image from the current resource's media library (gallery collection), displayed as a thumbnail grid (up to 20 images); click again to deselect

Vision models available:

  • Googlegemini-3-flash-preview (default)
  • OpenAI — latest GPT-4o (non-mini, non-pro, non-audio)
  • xAI — latest Grok fast variant
  • Anthropic — Claude Sonnet / Opus

Models are fetched from OpenRouter using the input_modalities=image&output_modalities=text filter, cached 1 hour.

Controls:

  • Model selector — grouped by provider
  • Max tokens — configurable output length (default: 1000, range: 100–4000); help text: 1 token ≈ 3–4 characters
  • Prompt — pre-filled with a default Romanian prompt instructing the model to describe the image in detail without HTML/Markdown formatting; fully editable

Default prompt:

Descrie imaginea in detaliu. Concentreaza-te pe detaliile tehnice, forma, culoare, formă, materiale vizibile, caracteristici estetice și orice detalii relevante pentru o descriere de produs. Nu formata descrierea in HTML sau Markdown.

The default prompt is translatable via language files (see Multilanguage section).

Result: plain text description in a textarea with a copy button.

Credits & Cost Tracking

All operations display real-time cost in the drawer header:

  • Credite — remaining OpenRouter balance (refreshed after each operation, cached 60s)
  • Last operation cost — cost of the most recent call
  • Session total — cumulative cost for the current drawer session

Multilanguage UI

All static strings in the interface are translatable. The package ships with Romanian (ro.json) and English (en.json) language files.

To customize translations, publish the lang files:

php artisan vendor:publish --tag="nova-translatable-lang"

Published files are located at resources/lang/vendor/nova-astrotranslatable/ and take priority over package defaults. The correct locale file is loaded automatically based on app()->getLocale().

To add a new language, create a new JSON file (e.g. fr.json) in the published directory following the same key structure as en.json.

Validation

Define locale-specific validation rules with ->rulesFor() and the HandlesTranslatable trait:

use Kiritokatklian\NovaAstrotranslatable\HandlesTranslatable;

class Product extends Resource
{
    use HandlesTranslatable;

    public function fields(Request $request)
    {
        return [
            Text::make(__('Name'), 'name')
                ->sortable()
                ->translatable()
                ->rules(['max:255'])
                ->rulesFor('en', [
                    'required',
                ])
                ->rulesFor(['en', 'ro'], function ($locale) {
                    return ["unique:products,name->$locale{{resourceId}}"];
                }),
        ];
    }
}

Edge Cases

BelongsToMany allowDuplicateRelations corner-case

When using this field inside a BelongsToMany as a pivot field with ->allowDuplicateRelations() and you want to filter out exact matches using the NotExactlyAttached rule, use the BelongsToManyTranslatable field instead of the regular BelongsToMany.

Versioning

VersionLaravel NovaLaravelPHPNotes
^1.0^4.12^10|^11>=8.0
^2.0^4.12|^5.0^11|^12>=8.0AI text & image generation
^2.1^4.12|^5.0^11|^12>=8.0Product image analysis, select-all toggle
^2.2^4.12|^5.0^11|^12>=8.0Visual image analysis tab, multilanguage UI (ro/en/fr/de/es), publishable lang files, config-driven providers/families/models/aspect-ratios/sizes
^2.3^4.12|^5.0^11|^12>=8.0Image generation gallery with history, Nova domain route constraint
^2.4^4.12|^5.0^11|^12>=8.0Multi-image source upload (up to 3 images) for image-to-image generation

Credits

License

This project is open-sourced software licensed under the MIT license.