Skip to Content
ModulesTranslations & Config

Translations and Configuration

Flute CMS supports multilingualism and a flexible module configuration system. This section explains how to create translations and configuration.


Translation System

Translations allow displaying text in different languages without changing the code.

How It Works

Create Translation Files

For each language, a folder with PHP files is created, where key → translation.

Use in Code

The __('key') function returns text in the user’s current language.

Flute Detects Language

Based on URL parameter, cookie, or user settings.

File Structure

      • messages.php
      • validation.php
  • Each language is a separate folder (ru, en, de)
  • Inside are PHP files with translation arrays
  • Filename = translation domain (e.g., messages)

Creating Translation Files

Basic Messages

<?php // Resources/lang/ru/messages.php return [ // General 'welcome' => 'Добро пожаловать в блог', 'home' => 'Главная', 'back' => 'Назад', // Articles 'articles' => 'Статьи', 'article' => 'Статья', 'no_articles' => 'Статей пока нет', 'read_more' => 'Читать далее', // CRUD 'create' => 'Создать', 'edit' => 'Редактировать', 'delete' => 'Удалить', 'save' => 'Сохранить', 'cancel' => 'Отмена', // Success messages 'article_created' => 'Статья успешно создана', 'article_updated' => 'Статья обновлена', 'article_deleted' => 'Статья удалена', // Errors 'article_not_found' => 'Статья не найдена', 'access_denied' => 'Доступ запрещён', // With parameters (:name is used for substitution) 'hello_user' => 'Привет, :name!', 'articles_count' => 'Найдено статей: :count', ];
<?php // Resources/lang/en/messages.php return [ // General 'welcome' => 'Welcome to the blog', 'home' => 'Home', 'back' => 'Back', // Articles 'articles' => 'Articles', 'article' => 'Article', 'no_articles' => 'No articles yet', 'read_more' => 'Read more', // CRUD 'create' => 'Create', 'edit' => 'Edit', 'delete' => 'Delete', 'save' => 'Save', 'cancel' => 'Cancel', // Success messages 'article_created' => 'Article created successfully', 'article_updated' => 'Article updated', 'article_deleted' => 'Article deleted', // Errors 'article_not_found' => 'Article not found', 'access_denied' => 'Access denied', // With parameters 'hello_user' => 'Hello, :name!', 'articles_count' => 'Found :count articles', ];

Validation Messages

<?php // Resources/lang/ru/validation.php return [ // General rules 'required' => 'Это поле обязательно', 'email' => 'Введите корректный email', 'min' => 'Минимум :min символов', 'max' => 'Максимум :max символов', // For specific fields 'title' => [ 'required' => 'Введите заголовок статьи', 'min-str-len' => 'Заголовок слишком короткий (минимум :min символов)', 'max-str-len' => 'Заголовок слишком длинный (максимум :max символов)', ], 'content' => [ 'required' => 'Напишите содержание статьи', 'min-str-len' => 'Статья слишком короткая', ], 'category_id' => [ 'required' => 'Выберите категорию', 'exists' => 'Такой категории не существует', ], ];

Important: The filename becomes the translation “domain”. If both Blog module and News module have a messages.php file, keys might conflict. Use unique filenames or keys.


Using Translations

In PHP Code

<?php namespace Flute\Modules\Blog\Http\Controllers; use Flute\Core\Support\BaseController; class ArticleController extends BaseController { public function index() { // Simple translation $title = __('messages.articles'); // Result (ru): "Статьи" // Result (en): "Articles" // Translation with parameters $greeting = __('messages.hello_user', ['name' => user()->name]); // Result: "Hello, Ivan!" // Translation with quantity $count = count($articles); $countText = __('messages.articles_count', ['count' => $count]); // Result: "Found articles: 15" return response()->view('blog::index', compact('articles')); } public function store() { // Validation passed successfully $this->flash(__('messages.article_created'), 'success'); return redirect(route('blog.index')); } public function show(int $id) { $article = rep(Article::class)->findByPK($id); if (!$article) { // Show error in user's language return $this->error(__('messages.article_not_found'), 404); } return response()->view('blog::show', compact('article')); } }

In Blade Templates

{{-- Simple translation --}} <h1>{{ __('messages.articles') }}</h1> {{-- With parameters --}} <p>{{ __('messages.hello_user', ['name' => $user->name]) }}</p> {{-- Via directive --}} <button>@lang('messages.save')</button> {{-- In attributes --}} <input type="text" placeholder="{{ __('messages.search') }}"> {{-- Conditional translation --}} @if($articles->isEmpty()) <p>{{ __('messages.no_articles') }}</p> @endif {{-- In links --}} <a href="{{ route('blog.create') }}"> {{ __('messages.create') }} </a>

In JavaScript

Sometimes translations are needed on the client side (in JavaScript):

{{-- Pass needed translations in template --}} @push('scripts') <script> // Create object with translations window.translations = { confirmDelete: @json(__('messages.confirm_delete')), loading: @json(__('messages.loading')), success: @json(__('messages.success')), error: @json(__('messages.error')), }; </script> @endpush
// In JS file function deleteArticle(id) { if (confirm(window.translations.confirmDelete)) { // Delete article } } function showNotification(type, message) { const text = message || window.translations[type]; // Show notification }

Pluralization

For declining words depending on quantity:

<?php // Resources/lang/ru/messages.php return [ // Format: singular|plural // Or: one|few|many (for Russian) 'comments' => '{0} No comments|{1} :count comment|[2,4] :count comments|[5,*] :count comments', // Simplified version 'items' => ':count item|:count items', ];
// Usage (if trans_choice function is available) $text = trans_choice('messages.comments', 0); // "No comments" $text = trans_choice('messages.comments', 1); // "1 comment" $text = trans_choice('messages.comments', 3); // "3 comments" $text = trans_choice('messages.comments', 10); // "10 comments" // Alternative via __() with logic in code $text = __('messages.comments_count', ['count' => $count]);

Check availability of trans_choice() function in your Flute CMS version. If not available, use regular __() function and implement pluralization logic in code.


Managing Language

// Get current language $locale = app()->getLocale(); // 'ru', 'en' etc. // Set language app()->setLocale('en'); // Check which language if (app()->getLocale() === 'ru') { // Russian }

How Flute detects user language:

URL Parameter

?lang=en — highest priority

current_lang — if user selected language previously

User Settings

app()->getLang() — if user is authorized

Default Settings

config('lang.locale') — site default language


Configuration System

Configuration allows storing module settings in separate files and easily changing them.

File Structure

    • blog.php
    • cache.php
    • features.php

Creating Configuration

<?php // Resources/config/blog.php return [ /* |-------------------------------------------------------------------------- | General Settings |-------------------------------------------------------------------------- */ // Is module enabled 'enabled' => true, // Section title 'title' => 'Blog', /* |-------------------------------------------------------------------------- | Display Settings |-------------------------------------------------------------------------- */ // How many articles per page 'per_page' => 10, // Article excerpt length in chars 'excerpt_length' => 200, // Show author 'show_author' => true, // Show publish date 'show_date' => true, // Date format 'date_format' => 'd.m.Y', /* |-------------------------------------------------------------------------- | Image Settings |-------------------------------------------------------------------------- */ 'images' => [ // Max file size in KB 'max_size' => 2048, // Allowed formats 'allowed_types' => ['jpg', 'jpeg', 'png', 'webp'], // Compression quality (for JPEG) 'quality' => 85, // Thumbnail sizes 'thumbnail' => [ 'width' => 300, 'height' => 200, ], ], /* |-------------------------------------------------------------------------- | Comment Settings |-------------------------------------------------------------------------- */ 'comments' => [ // Are comments enabled 'enabled' => true, // Moderation (comments checked before publishing) 'moderation' => false, // Can guests comment 'guests_allowed' => false, // Max reply depth 'max_depth' => 3, ], /* |-------------------------------------------------------------------------- | Caching |-------------------------------------------------------------------------- */ 'cache' => [ // Article list cache time (seconds) 'list_ttl' => 3600, // 1 hour // Single article cache time 'article_ttl' => 1800, // 30 minutes ], ];

Using Configuration

In Controllers

<?php namespace Flute\Modules\Blog\Http\Controllers; use Flute\Core\Support\BaseController; class ArticleController extends BaseController { public function index() { // Get value from config $perPage = config('blog.per_page', 10); // If 'blog.per_page' is not set, 10 is returned $articles = rep(Article::class) ->select() ->where('published', true) ->paginate($perPage); return response()->view('blog::index', [ 'articles' => $articles, 'showAuthor' => config('blog.show_author'), 'showDate' => config('blog.show_date'), ]); } public function store() { // Check if module is enabled if (!config('blog.enabled')) { return $this->error('Blog is temporarily disabled', 503); } // Check image settings $image = request()->files->get('image'); if ($image) { $maxSize = config('blog.images.max_size') * 1024; // In bytes if ($image->getSize() > $maxSize) { return $this->error('Image too large', 422); } $allowedTypes = config('blog.images.allowed_types'); $extension = strtolower($image->getClientOriginalExtension()); if (!in_array($extension, $allowedTypes)) { return $this->error('Invalid image format', 422); } } // Create article... } }

In Services

<?php namespace Flute\Modules\Blog\Services; class ArticleService { public function getRecentArticles(): array { $cacheKey = 'blog_recent_articles'; $cacheTtl = config('blog.cache.list_ttl', 3600); // Try getting from cache $cached = cache()->get($cacheKey); if ($cached) { return $cached; } // Load from DB $articles = rep(Article::class) ->select() ->where('published', true) ->orderBy('created_at', 'DESC') ->limit(config('blog.per_page', 10)) ->fetchAll(); // Save to cache cache()->set($cacheKey, $articles, $cacheTtl); return $articles; } public function canComment(): bool { // Check comment settings if (!config('blog.comments.enabled')) { return false; } if (!user()->isLoggedIn() && !config('blog.comments.guests_allowed')) { return false; } return true; } }

In Templates

{{-- Get settings --}} @php $showAuthor = config('blog.show_author'); $showDate = config('blog.show_date'); $dateFormat = config('blog.date_format', 'd.m.Y'); @endphp <article class="article"> <h1>{{ $article->title }}</h1> <div class="article-meta"> @if($showAuthor) <span class="author">{{ $article->author->name }}</span> @endif @if($showDate) <time>{{ $article->created_at->format($dateFormat) }}</time> @endif </div> <div class="article-content"> {!! $article->content !!} </div> </article> {{-- Check if comments enabled --}} @if(config('blog.comments.enabled')) <section class="comments"> @include('blog::components.comments', ['article' => $article]) </section> @endif

Getting Entire Configuration

// Single key $value = config('blog.per_page'); // With default value $value = config('blog.per_page', 10); // Entire section as array $imagesConfig = config('blog.images'); // ['max_size' => 2048, 'allowed_types' => [...], ...] // Entire config file $blogConfig = config('blog');

Caching

Translations

  • Translations are cached automatically for 1 hour
  • In performance mode, they are compiled into storage/app/translations

Clear Translation Cache:

translation()->flushLocaleCache('ru'); // For specific language translation()->flushLocaleCache(); // For all languages

Configuration

  • Configuration is cached by the module provider
  • Default cache time: 1 hour

Managing Cache:

// In provider — change cache duration public function boot(\DI\Container $container): void { $this->setCacheDuration(7200); // 2 hours $this->bootstrapModule(); } // Clear entire module cache $provider->clearFileCache(); // Clear specific config cache cache()->delete('flute.config.blog');

Tips

Organizing Translations

  • Group by meaning — separate files for messages, validation, emails
  • Use prefixesarticle_created, article_deleted instead of created, deleted
  • Document parameters — comment // :count - quantity helps translators

Organizing Configuration

  • Separate by areas — separate sections for images, cache, comments
  • Always provide default valuesconfig('key', 'default')
  • Use env() for changeable settings — things that might change between environments

Supporting Multiple Languages

  • Create translations immediately — don’t postpone
  • Test in all languages — some texts might be longer
  • Consider RTL — Arabic and Hebrew read right-to-left