Skip to main content

Translation System

MMO-DEV WEB uses a translation system based on Symfony Translation with ICU MessageFormat support.

The |trans filter​

Basic usage​

Basic usage
{{ 'Welcome'|trans }}
{# Looks up the translation in the default domain (messages) #}

{{ 'site.welcome_message'|trans }}

Specifying a domain​

Specifying a domain
{{ 'login.title'|trans({}, 'user') }}
{# Looks up the translation in the "user" domain #}

{{ 'nav.home'|trans({}, 'navigation') }}
{{ 'market.buy_button'|trans({}, 'market') }}

Substitution parameters​

Substitution parameters
{{ 'Hello, %name%!'|trans({'%name%': user.name}) }}
{{ 'You have %count% messages'|trans({'%count%': count}, 'cabinet') }}

Translation file structure​

Translation files are located in the Language/ directory:

Language/ structure

Available domains​

DomainDescription
commonCommon phrases (buttons, statuses, errors)
siteMain site texts
userAuthentication, registration, profile
cabinetUser dashboard
adminAdmin panel
navigationNavigation menu
marketGame store
forumForum
newsNews
ticketsTicket system

Translation file format​

Each translation file is a PHP file that returns an array:

// Language/en/common.php
return [
'save' => 'Save',
'cancel' => 'Cancel',
'delete' => 'Delete',
'edit' => 'Edit',
'loading' => 'Loading...',
'error.not_found' => 'Not Found',
'error.access_denied' => 'Access Denied',
'status.active' => 'Active',
'status.inactive' => 'Inactive',
];
// Language/en/common.php
return [
'save' => 'Save',
'cancel' => 'Cancel',
'delete' => 'Delete',
'edit' => 'Edit',
'loading' => 'Loading...',
'error.not_found' => 'Not Found',
'error.access_denied' => 'Access Denied',
'status.active' => 'Active',
'status.inactive' => 'Inactive',
];

ICU MessageFormat​

ICU MessageFormat is used for complex translations.

Plurals​

// Language/en/cabinet.php
return [
'messages_count' => '{count, plural,
one {# message}
other {# messages}
}',
];
Plurals
{{ 'messages_count'|trans({count: 1}, 'cabinet') }}
{# 1 message #}

{{ 'messages_count'|trans({count: 3}, 'cabinet') }}
{# 3 messages #}

{{ 'messages_count'|trans({count: 5}, 'cabinet') }}
{# 5 messages #}

Gender​

return [
'user_action' => '{gender, select,
male {He updated the profile}
female {She updated the profile}
other {The profile was updated}
}',
];
Gender
{{ 'user_action'|trans({gender: user.gender}, 'user') }}

Ordinal numbers​

return [
'place' => '{place, selectordinal,
one {#st place}
two {#nd place}
few {#rd place}
other {#th place}
}',
];

Combining patterns​

return [
'welcome' => '{gender, select,
male {Dear {name}, you have {count, plural,
one {# new notification}
other {# new notifications}
}}
female {Dear {name}, you have {count, plural,
one {# new notification}
other {# new notifications}
}}
other {Hello, {name}! You have {count, plural,
one {# new notification}
other {# new notifications}
}}
}',
];
Combining patterns
{{ 'welcome'|trans({
gender: user.gender,
name: user.name,
count: notifications|length
}, 'site') }}

Fallback locales​

If a translation is not found in the current locale, the system looks for it in the fallback locale:

Request: de -> Fallback: en -> Fallback: ru

Lookup order:

  1. Current locale (de)
  2. First fallback (en)
  3. Primary fallback (ru)
  4. Translation key as-is

Usage in JavaScript​

Translations can be passed to JavaScript via data attributes or a global object:

Usage in JavaScript
<div data-confirm="{{ 'Are you sure?'|trans({}, 'common') }}">

<script>
const translations = {{ {
save: 'save'|trans({}, 'common'),
cancel: 'cancel'|trans({}, 'common'),
confirm: 'confirm'|trans({}, 'common')
}|json_encode|raw }};
</script>