Laravel localization in Romanian Română

Everything you need to ship Romanian in a Laravel app: the right locale codes, the exact plural forms trans_choice() expects, real localized Carbon output and Latin-script considerations. Every value on this page was generated by running ICU, CLDR and Carbon — not copied from another article.

ISO 639-1

ro

Script / Direction

Latin · LTR

Plural forms (Laravel)

3

Text vs English

Expands

Locale codes

Set the base locale in config/app.php. For regional variants, Romanian commonly uses: ro_RO, ro_MD

// config/app.php
'locale' => 'ro',
'fallback_locale' => 'en',

// Or switch at runtime
App::setLocale('ro');

Plural rules: what Romanian actually needs

CLDR defines 3 cardinal categories for Romanian. The sample numbers below were computed by ICU for this exact locale:

CLDR category Numbers that select it
one 1
few 0, 2–19, 101–119, 1.5
other 20–100, 120–130, 200, 1000, 1000000

Laravel's trans_choice() maps numbers to 3 pipe-separated forms for this locale:

Form index Numbers that select it
0 1
1 0, 2–19, 101–119
2 20–100, 120–130, 200, 1000
// lang/ro/messages.php
'items' => ':count item|:count items'

// Usage
trans_choice('messages.items', $count, ['count' => $count]);

This language uses 3 plural forms. Laravel's built-in trans_choice only resolves two, so for grammatically correct output use an ICU MessageFormat package or handle the extra forms explicitly against the CLDR categories above.

Try any number live in the pluralization tester.

Localized dates with Carbon

Real output for Romanian (ro locale), generated by Carbon for March 21, 2026:

$date = now()->locale('ro');

$date->translatedFormat('l, j F Y');
// "sâmbătă, 21 martie 2026"

$date->isoFormat('LLLL');
// "sâmbătă, 21 martie 2026 14:30"

$date->isoFormat('L');
// "21.03.2026"

$date->subDays(3)->diffForHumans();
// "3 luni în urmă"

What to watch out for in Romanian

A Romance language with cases and suffixed articles

Romanian is the outlier of the Romance family. It kept features the others lost and acquired others from its Slavic and Balkan neighbours, so code and copy patterns that work for Spanish, French and Italian frequently do not transfer.

The most visible difference is that the definite article is a suffix rather than a separate word, as in the Scandinavian languages. Fișier is a file; fișierul is the file. There is no word to concatenate, because the grammar lives inside the noun a placeholder would hold.

English Romanian Form
a file un fișier indefinite
the file fișierul definite suffix
files fișiere indefinite plural
the files fișierele definite plural suffix
of the file fișierului genitive definite

Romanian also retains a case system — nominative-accusative, genitive-dative and vocative — which combines with the article suffix, so a single noun has a substantial number of forms and the one required depends on its role in the sentence.

There are three genders, and the third is genuinely unusual: neuter nouns behave as masculine in the singular and feminine in the plural. That means adjective agreement can switch gender between one and many, which no amount of template logic will reproduce.

Neuter nouns are common rather than exotic — many everyday objects are neuter — so this is not an edge case to be handled later. A shared adjective string reused between a singular and a plural context will produce the wrong agreement for every neuter noun in the interface, which is a substantial fraction of them.

Three plural forms, including one driven by the number itself

Romanian uses three CLDR categories, and the rule is unusual: the few category applies not to small numbers but to numbers whose last two digits fall between 1 and 19, excluding 1 itself, plus zero.

Category Selected by Example
one 1 1 fișier
few 0, 2–19, 101–119, 201–219… 3 fișiere
other 20–100, 120–200… 25 de fișiere

The other form carries a grammatical marker the others do not: numbers from twenty upward require the preposition de between the number and the noun. Three files is 3 fișiere, but twenty-five files is 25 de fișiere, and omitting the de is a basic grammatical error.

Three forms, and the de that appears at twenty

// lang/ro/messages.php
// one|few|other
'files' => ':count fișier|:count fișiere|:count de fișiere',

trans_choice('messages.files', $count, ['count' => $count]);

A two-form translation therefore fails twice: it gets the boundary wrong and it drops the preposition entirely for every number above nineteen. Test with 1, 3, 20 and 101 — the last value is the one that catches people, since 101 takes the few form despite being large.

The rule reads oddly to speakers of two-form languages because size and category come apart: 3 and 119 share a form, while 20 and 101 do not. It is worth writing the four test values into an automated check rather than relying on a reviewer to remember them, since nothing about the rendered string suggests which branch produced it.

The comma-below characters that fonts get wrong

Romanian uses five diacritics: ă, â, î, ș and ț. The last two are the problem. They are correctly written with a comma below, not a cedilla, and Unicode encodes the two forms as different characters.

Correct (comma below) Wrong (cedilla) Code points
ș ş U+0219 vs U+015F
ț ţ U+021B vs U+0163

The cedilla forms belong to Turkish and were used for Romanian historically because early character sets and fonts lacked the comma-below variants. A great deal of legacy Romanian text and many older fonts still use them, so real-world data contains both, and they compare unequal while looking nearly identical.

Normalising cedilla forms to comma-below

<?php

$value = strtr($value, [
    "\u{015F}" => "\u{0219}",  // ş -> ș
    "\u{015E}" => "\u{0218}",  // Ş -> Ș
    "\u{0163}" => "\u{021B}",  // ţ -> ț
    "\u{0162}" => "\u{021A}",  // Ţ -> Ț
]);

Normalise to the comma-below forms on input and verify that the fonts you ship actually contain them — many otherwise complete fonts include the cedilla variants only, and the browser then falls back to a different typeface for those two letters, producing text where a few characters are visibly mismatched.

Sorting needs a Romanian collator. The accented letters sort immediately after their base letters rather than at the end of the alphabet, and a byte-order sort places all of them after z.

Romanian is also written in Moldova, where it is the official language and where it was written in Cyrillic until 1989. Modern Moldovan uses the Latin alphabet and is linguistically the same language, so a single Romanian translation serves both markets — but the currency differs, the Moldovan leu and the Romanian leu being distinct, so the market must be stored separately from the language.

Formality, layout and formatting

Romanian distinguishes informal tu from formal dumneavoastră, with an intermediate dumneata that is now dated. The formal pronoun is notably long, which has a real layout consequence: a sentence addressing the user formally is meaningfully wider than the informal equivalent.

Consumer software generally uses tu; banking, government and formal business use dumneavoastră. As in the other Romance languages, buttons commonly take the infinitive — Salvează, Anulează, Șterge — which sidesteps the register question for short labels.

Romanian expands roughly twenty to thirty per cent over English, and the suffixed articles plus case endings make individual words longer than their Romance cousins.

Romanian convention
Thousands 1.234.567
Decimal 1234,56
Currency 1.234,56 lei (after the amount)
Short date 21.03.2026
Long date 21 martie 2026
First day of week Monday

Romania is in the European Union and uses the leu rather than the euro, which billing code written for a generic European market regularly gets wrong. Month names are lowercase, and the decimal comma creates the usual numeric-input hazard.

One layout consequence of the suffixed article is worth planning for: a Romanian noun grows when it becomes definite, so labels expand between their indefinite and definite forms. A column header or a button sized against the dictionary form of a word may not fit the form that actually appears in the interface, which is a failure mode with no equivalent in the other Romance languages.

Getting Romanian into the application

Romanian concentrates its difficulty in two places — the character encoding of two letters, and a plural rule with a grammatical marker attached — and both are the kind of problem that is cheap to prevent and expensive to discover late.

Normalise the comma-below characters at every entry point: form submissions, imports, API payloads and the translation files themselves. Doing it in only some places is worse than doing it nowhere, because it guarantees the same word exists in two forms in your data, and reconciling that after several years of records is genuinely hard.

Verify font coverage explicitly rather than trusting a font's description. Many otherwise complete typefaces include only the Turkish cedilla variants, and the browser then falls back for exactly two letters — producing Romanian text where ș and ț are visibly a different typeface. Test with a string containing both rather than with a character sample.

Keyboard layouts are the other source of drift. Romanian layouts configured on older systems produce the cedilla forms by default, so data typed by users accumulates both variants over time even when your own strings are correct.

Treat the plural forms as three plus a preposition rather than as three strings. The de that appears from twenty upward is easy for a translator to omit when working from a two-form English source, and easy for a reviewer to miss because the sentence still reads as a number followed by a noun. Test at 20 specifically.

Set the full locale so dates render with the genitive-free Romanian pattern and amounts show the leu rather than the euro, and install the Laravel-Lang Romanian files for framework strings rather than reproducing the case agreement across several hundred validation messages by hand.

Romanian is a useful sanity check on a localisation pipeline that already handles Slavic languages, because it combines a Romance vocabulary with a case system and suffixed articles. Code that assumes Romance means simple, or that cases mean Cyrillic, will have made an assumption somewhere that Romanian exposes.

What ships broken most often

The comma-below issue is the one worth automating a check for, because it is invisible in review at normal sizes and corrupts data rather than only presentation. Normalise at every entry point and assert that stored Romanian text contains no cedilla code points.

The second automated check worth having is on the plural strings themselves: assert that every Romanian counting message has exactly three pipe-separated segments and that the third contains de. Both failures are silent, both are easy for a translator to introduce from a two-form source, and neither is visible to a reviewer who does not read Romanian.

Framework strings already translated

Laravel's own validation, auth and pagination strings are maintained in Romanian by the open-source Laravel-Lang project (MIT). Install them, then manage your app's own strings live:

composer require laravel-lang/common --dev
php artisan lang:add ro
php artisan lang:update

Translate your app into Romanian today

Import your lang files, translate every key into Romanian with one AI click, and publish changes live — no deploy. Set up in 5 minutes.

We use cookies to improve your experience and analyze site traffic. Cookie Policy

Cookie Preferences

Essential

Required for the site to work

Analytics

Help us improve the site

Marketing

Personalized ads and content