zendframework/zend-i18n-resources
Translation resources for zend-i18n: prebuilt translation files for validator messages, captcha, and other Zend Framework components. Load them into your Translator to localize error and UI strings without writing translations yourself.
This is a data-only package: no service provider, no facades, no config. It ships translated catalogs (PHP arrays keyed by English source strings) for Zend components — primarily zend-validator, plus captcha and i18n view-helper strings. Its only job is to be handed to Zend\I18n\Translator\Translator.
First, reconsider the dependency. The repo is archived and the last release was 2019-06-25. If you are actually running Laminas components, require the maintained fork instead — same data, new namespace:
composer require laminas/laminas-i18n-resources
Only keep zendframework/zend-i18n-resources if you are pinned to legacy Zend\* classes.
Where to look first. After install, inspect the catalog layout before writing any code:
ls vendor/zendframework/zend-i18n-resources/resources/languages/
ls vendor/zendframework/zend-i18n-resources/resources/languages/de/
You will see locale directories (de, fr, es, pt_BR, …) containing component-named files such as Zend_Validate.php. Each file is a plain return [ 'English message' => 'Übersetzung', ... ];.
First use case — translated validator messages. Build a translator, register the validator catalog under the text domain validators actually query (usually default), then make it the default for AbstractValidator:
use Zend\I18n\Translator\Translator;
use Zend\Validator\AbstractValidator;
$base = 'vendor/zendframework/zend-i18n-resources/resources/languages';
$translator = new Translator();
$translator->addTranslationFile(
'phpArray',
"$base/de/Zend_Validate.php",
'default', // must match the validator's translator text domain
'de'
);
AbstractValidator::setDefaultTranslator($translator);
If messages still come back in English, the text domain or locale string is wrong — see Gotchas.
Config-driven registration (Zend/Laminas MVC style). Use a file pattern so every locale is picked up without hardcoding filenames:
'translator' => [
'locale' => 'de_DE',
'translation_file_patterns' => [
[
'type' => 'phpArray',
'base_dir' => __DIR__ . '/../vendor/zendframework/zend-i18n-resources/resources/languages',
'pattern' => '%s/Zend_Validate.php',
],
],
],
Register the captcha and i18n catalogs the same way if you use those components, with their respective text domains (captcha, default).
Laravel service-container binding. In a service provider, build the translator once as a singleton and attach the catalogs. Long-running workers (Octane, queue) should not rebuild this per request:
$this->app->singleton(Translator::class, function ($app) {
$locale = str_replace('-', '_', $app->getLocale()); // pt-BR -> pt_BR
$base = base_path('vendor/zendframework/zend-i18n-resources/resources/languages');
$translator = new Translator();
$translator->setLocale($locale);
foreach (['Zend_Validate.php', 'Zend_Captcha.php', 'Zend_I18n.php'] as $file) {
$path = "$base/$locale/$file";
if (is_file($path)) {
$translator->addTranslationFile('phpArray', $path, 'default', $locale);
}
}
return $translator;
});
Then either call AbstractValidator::setDefaultTranslator(...) in a boot method, or inject per-validator with $validator->setTranslator($translator) when you only want scoped translation.
Locate the path without hardcoding. vendor/ layout is not guaranteed. On Composer 2:
$base = \Composer\InstalledVersions::getInstallPath('zendframework/zend-i18n-resources')
. '/resources/languages';
Scoped translators over the global default. If most of your app is English but one form is localized, prefer setTranslator() on the specific validator chain over AbstractValidator::setDefaultTranslator(), which is process-global and easy to leak between tests.
Converting to native Laravel translations. If you are migrating off Zend validators but want to keep the strings, the PHP arrays are directly readable — require them and merge into your own lang files. Note the keys are English sentences, not Laravel rule names (required, email), so this only works as a message catalog, not as a drop-in lang/{locale}/validation.php.
Overriding individual strings. Register the bundled file first, then your own phpArray file with the same domain and locale for the messages you want to change. Verify precedence in your installed zend-i18n version — TextDomain merges catalogs, and the win/lose order on duplicate keys is worth a one-line test rather than an assumption.
Archived upstream. No new locales, no fixes, no compatibility work with newer validator messages. Treat laminas/laminas-i18n-resources as the real dependency and this one as legacy-only.
Text domain mismatch is the #1 failure mode. Translations load fine but never apply. Check the domain the component queries (AbstractValidator defaults to default; captcha uses captcha) and register the file under exactly that domain. A quick probe:
$translator->translate('Invalid type given. String expected', 'default', 'de');
Locale string format. Catalogs use underscore codes (pt_BR), while Laravel and browsers often produce hyphens (pt-BR). Normalize before building paths, and guard with is_file() — a missing locale silently produces untranslated output, not an error.
Stale message keys. Validator messages have changed since 2019. Untranslated messages fall through as the English key, which looks like "translation is broken" but is really "key not present." Diff your installed validator's message strings against the catalog when a specific message stays English.
No classes, so no autoloading or discovery. Do not expect composer dump-autoload or package discovery to register anything. You must reference the filesystem path explicitly.
Don't edit files in vendor/. Package updates overwrite them. Put overrides in your own resources/lang/zend/ directory and layer them on top.
Custom validator messages lose translations. Catalog keys are the stock English strings. Once you call setMessage() with your own text, no bundled translation will match.
Debugging. Inspect what was actually loaded with $translator->getMessages(), and confirm the locale on the translator itself (getLocale()). Most "it doesn't work" cases resolve to one of: wrong domain, wrong locale code, file never added.
Extension point. The only real customization surface is composing more phpArray catalogs into the same translator. There is no plugin system, no cache layer, and nothing to extend.
When not to use it. If you are building a Laravel app and only need localized validation messages, Laravel's own lang/{locale}/validation.php already covers this. This package earns its place only when you are genuinely running zend-validator, zend-captcha, or zend-i18n helpers inside the app.
How can I help you explore Laravel packages today?