Multi-Language Integration & Source of Truth

Last updated on May 21, 2026 05:06

PolyCMS offers a robust, globally available translation architecture that automatically discovers and merges localization files from the Core, Modules, and Themes.

To ensure performance without sacrificing ease of use for administrators, the system uses a Compilation Pipeline architecture. As a developer, it is critical to understand the concept of the Source of Truth to prevent data loss or silent translation failures.

1. The Compilation Pipeline Architecture

PolyCMS separates translation storage into two formats:

  1. .json files (The Source of Truth): These are human-readable, administrator-editable dictionary files.
  2. .php files (The Compiled Cache): These are high-performance PHP arrays generated by the system.

How it works at Runtime

When a page loads (Backend or Frontend) and a string needs translation, the core LanguageHelper only reads from the compiled .php cache files. It does not scan or parse .json files at runtime to guarantee maximum rendering speed.

How it works during Editing

When an administrator edits a language via the PolyCMS Admin UI (Settings > Languages), the system reads and writes to the .json files. After saving, the system automatically triggers the compiler to regenerate the .php cache.

2. Developer Workflow: Adding Translations

When developing a Module or Theme, you will often need to add new translatable strings.

File Location

Translations should be placed in the lang/ directory at the root of your extension:

  • Module: modules/Vendor/ModuleName/lang/{locale}.json
  • Theme: themes/theme-slug/lang/{locale}.json

The Golden Rule

CRITICAL: The {locale}.json file is the absolute Source of Truth. You must never manually edit the {locale}.php file, as the system compiler will overwrite it.

Step-by-Step Translation Workflow

  1. Add to JSON: Open your module or theme's lang/en.json or lang/vi.json and add your new key-value pairs.

    {
        "My Custom Setting": "Cài đặt tùy chỉnh của tôi",
        "Show Blog Header": "Hiển thị Header Blog"
    }
    
  2. Compile to PHP: Because the system only reads from .php, your new JSON keys will not take effect until they are compiled. You must manually trigger the compiler in your development environment.

    Using Artisan Tinker:

    php artisan tinker --execute="app(\App\Services\LanguageService::class)->compileToPhp('vi');"
    

    Alternatively, you can go to the PolyCMS Admin panel: Settings > Languages, and click the Compile button.

  3. Verify: Hard reload (Ctrl + F5) your browser. The new translations should now appear in the UI.

3. Formatting Warnings & Silent Failures

If you run the compilation command but your translations still do not appear, it is almost always due to an invalid JSON file.

If json_decode() fails to parse your .json file, the LanguageService will silently skip it to prevent crashing the application.

Common JSON formatting errors to avoid:

  • UTF-8 BOM: Ensure your code editor (or terminal commands like PowerShell's Set-Content) does not inject a Byte Order Mark (BOM). The file must be pure UTF-8.
  • Trailing Commas: JSON does not allow trailing commas after the last item.
  • Unescaped Quotes: Ensure any double quotes inside your string are properly escaped (e.g., \").

4. Using Translations in Code

Once compiled, you can use the translation strings seamlessly across different layers of your module or theme.

In Blade Templates

Always wrap hardcoded text in Laravel's native translator __() or the PolyCMS helper _l():

<button>{{ __('Show Blog Header') }}</button>

In PHP (Controllers, Service Providers)

Use the _l() helper for administrative strings, menu labels, or settings:

$menuRegistry->addChild('content', [
    'key' => 'blog_enhancer_tools',
    'label' => _l('SEO Tools'), // Translates automatically based on Admin's language
    'url' => '/admin/blog-enhancer'
]);

In Vue Components

Import and use the global useTranslation composable:

import { useTranslation } from '@/admin/composables/useTranslation';

const { t } = useTranslation();

// In your Vue template:
// <h2>{{ t('SEO Tools') }}</h2>