定制 omega-mvc/gettext 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

omega-mvc/gettext

Composer 安装命令:

composer require omega-mvc/gettext

包简介

PHP gettext manager

README 文档

README

Gettext is a PHP 8.4+ library for working with internationalization (i18n) and localization (l10n).
It provides tools to import, export, edit, and merge translations across multiple formats, such as PO, MO, PHP arrays, JSON, and JavaScript files.

The library is designed to be flexible: you can scan source code to extract translatable strings, load existing translations from files, manipulate them in PHP, and finally generate the compiled output in the format required by your application or framework.

Whether you are building a multilingual website, a PHP application, or a project that integrates templates and scripts, this package aims to provide a consistent API for all gettext-related operations.

Table of Contents

Installation

composer require omegamvc/gettext

Running Test

vendor/bin/phpunit

Classes and functions

This package contains the following classes:

  • Gettext\Translation - A translation definition
  • Gettext\Translations - A collection of translations (under the same domain)
  • Gettext\Translator -
  • Gettext\Scanner\* - Scan files to extract translations (php, js, twig templates, ...)
  • Gettext\Loader\* - Load translations from different formats (po, mo, json, ...)
  • Gettext\Generator\* - Export translations to various formats (po, mo, json, ...)

Quick Start

use Gettext\Loader\PoLoader;
use Gettext\Generator\MoGenerator;

//import from a .po file:
$loader = new PoLoader();
$translations = $loader->loadFile('locales/gl.po');

//edit some translations:
$translation = $translations->find(null, 'apple');

if ($translation) {
    $translation->translate('Mazá');
}

//export to a .mo file:
$generator = new MoGenerator();
$generator->generateFile($translations, 'Locale/gl/LC_MESSAGES/messages.mo');

Translation

The Gettext\Translation class stores all information about a translation: the original text, the translated text, source references, comments, etc.

use Gettext\Translation;

$translation = Translation::create('comments', 'One comment', '%s comments');

$translation->translate('Un comentario');
$translation->translatePlural('%s comentarios');

$translation->getReferences()->add('templates/comments/comment.php', 34);
$translation->getComments()->add('To display the amount of comments in a post');

echo $translation->getContext(); // comments
echo $translation->getOriginal(); // One comment
echo $translation->getTranslation(); // Un comentario

// etc...

Translations

The Gettext\Translations class stores a collection of translations:

use Gettext\Translations;

$translations = Translations::create('my-domain');

//You can add new translations:
$translation = Translation::create('comments', 'One comment', '%s comments');
$translations->add($translation);

//Find a specific translation
$translation = $translations->find('comments', 'One comment');

//Edit headers, domain, etc
$translations->getHeaders()->set('Last-Translator', 'Oscar Otero');
$translations->setDomain('my-blog');

Translator

use Gettext\Translator;

//Create a new instance of the translator
$t = new Translator();

//Load the translations from php files (generated by Gettext\Extractors\PhpArray)
$t->loadTranslations(
    'locales/gl/domain1.php',
    'locales/gl/domain2.php',
    'locales/gl/domain3.php',
);

//Now you can use it in your templates
echo $t->gettext('apple');

GettextTranslator

The class Gettext\GettextTranslator uses the gettext extension. It's useful because combines the performance of using real gettext functions but with the same API as Translator class, so you can switch to one or other translator without change code of your app.

use Gettext\GettextTranslator;

//Create a new instance
$t = new GettextTranslator();

//It detects the environment variables to set the locale, but you can change it:
$t->setLanguage('gl');

//Load the domains:
$t->loadDomain('messages', 'project/Locale');
//this means you have the file "project/Locale/gl/LC_MESSAGES/messages.mo"

//Now you can use it in your templates
echo $t->gettext('apple');

Translator functions

To ease the use of translations in your php templates, you can use the provided functions:

use Gettext\TranslatorFunctions;

//Register the translator to use the global functions
TranslatorFunctions::register($t);

echo __('apple'); // it's the same as $t->gettext('apple');

You can scan the php files containing these functions and extract the values with the PhpCode extractor:

<!-- index.php -->
<html lang="en">
    <body>
        <?= __('Hello world'); ?>
    </body>
</html>

Loaders

This package includes the following loaders:

  • ArrayLoader
  • JsonLoader
  • MoLoader
  • PoLoader
  • StrictPoLoader

The loaders allow to get gettext values from multiple formats. For example, to load a .po file:

ArrayLoader

JsonLoader

MoLoader

PoLoader

use Gettext\Loader\PoLoader;

$loader = new PoLoader();

//From a file
$translations = $loader->loadFile('locales/en.po');

//From a string
$string = file_get_contents('locales2/en.po');
$translations = $loader->loadString($string);

StrictPoLoader

StrictPoLoader is a parser more aligned to the GNU gettext tooling with the same expectations and failures (see the tests for more details).

  • It will fail with an exception when there's anything wrong with the syntax, and display the reason together with the line/byte where it happened.
  • It might also emit useful warnings, e.g. when there are more/less plural translations than needed, missing translation header, dangling comments not associated with any translation, etc.
  • Due to its strictness and speed (about 50% slower than the PoLoader), it might be interesting to be used as a kind of .po linter in a build system.
  • It also implements the previous translation comment (e.g. #| msgid "previous") and extra escapes (16-bit unicode \u, 32-bit unicode \U, hexadecimal \xFF and octal \77).

The usage is basically the same as the PoLoader:

use Gettext\Loader\StrictPoLoader;

$loader = new StrictPoLoader();

//From a file
$translations = $loader->loadFile('locales/en.po');

//From a string
$string = file_get_contents('locales2/en.po');
$translations = $loader->loadString($string);

//Display error messages using "at line X column Y" instead of "at byte X"
$loader->displayErrorLine = true;
//Throw an exception when a warning happens
$loader->throwOnWarning = true;
//Retrieve the warnings
$loader->getWarnings();

Generators

The generators export a Gettext\Translations instance to any format (po, mo, etc.).

This package includes the following generators:

  • ArrayGenerator
  • JsonGenerator
  • MoGenerator
  • PoGenerator

ArrayGenerator

ArrayGenerator generates PHP array files from gettext translations with optional pretty-printing and strict types.

use Gettext\Generator\ArrayGenerator;
use Gettext\Loader\PoLoader;

// Load translations from a .po file
$loader = new PoLoader();
$translations = $loader->loadFile('locales/en.po');

// Generate PHP array file with pretty print and strict types
$generator = new ArrayGenerator([
    'pretty' => true,
    'strictTypes' => true
]);

$phpCode = $generator->generateString($translations);
file_put_contents('locale/en/messages.php', $phpCode);

JsonGenerator

JsonGenerator generates JSON files from gettext translations with configurable JSON options.

use Gettext\Loader\PoLoader;
use Gettext\Loader\JsonLoader;
use Gettext\Generator\JsonGenerator;
use Gettext\Translations;

//Load a .po file and export to .json
$translations = (new PoLoader())->loadFile('locales/translations.po');
(new JsonGenerator())->generateFile($translations, 'locales/translations.json');

//You can load the json file with JsonLoader
$loadedTranslations = (new JsonLoader())->loadFile('locales/translations.json');

MoGenerator

MoGenerator generates binary .mo files from gettext translations, optionally including headers

use Gettext\Loader\PoLoader;
use Gettext\Generator\MoGenerator;

//Load a PO file
$poLoader = new PoLoader();

$translations = $poLoader->loadFile('locales/en.po');

//Save to MO file
$moGenerator = new MoGenerator();

$moGenerator->generateFile($translations, 'locales/en.mo');

//Or return as a string
$content = $moGenerator->generateString($translations);
file_put_contents('locales/en.mo', $content);

PoGenerator

PoGenerator generates human-readable .po files from gettext translations.

use Gettext\Loader\PoLoader;
use Gettext\Generator\PoGenerator;

// Load translations from a PO file
$loader = new PoLoader();
$translations = $loader->loadFile('locales/en.po');

// Generate a .po string
$generator = new PoGenerator();
$poContent = $generator->generateString($translations);

// Save to a file
file_put_contents('locales/generated.po', $poContent);

Scanners

Scanners allow to search and extract new gettext entries from different sources like php files, twig templates, blade templates, etc. Unlike loaders, scanners allows to extract gettext entries with different domains at the same time:

This package includes the following scanners:

  • JsScanner
  • PhpScanner

JsScanner

use Gettext\Scanner\JsScanner;
use Gettext\Generator\PoGenerator;
use Gettext\Translations;

//Create a new scanner, adding a translation for each domain we want to get:
$jsScanner = new JsScanner(
    Translations::create('domain1'),
    Translations::create('domain2'),
    Translations::create('domain3')
);

//Scan files
foreach (glob('*.js') as $file) {
    $jsScanner->scanFile($file);
}

//Save the translations in .po files
$generator = new PoGenerator();

foreach ($jsScanner->getTranslations() as $translations) {
    $domain = $translations->getDomain();
    $generator->generateFile($translations, "locales/{$domain}.po");
}

PhpScanner

use Gettext\Scanner\PhpScanner;
use Gettext\Generator\PoGenerator;
use Gettext\Translations;

//Create a new scanner, adding a translation for each domain we want to get:
$phpScanner = new PhpScanner(
    Translations::create('domain1'),
    Translations::create('domain2'),
    Translations::create('domain3')
);

//Set a default domain, so any translations with no domain specified, will be added to that domain
$phpScanner->setDefaultDomain('domain1');

//Extract all comments starting with 'i18n:' and 'Translators:'
$phpScanner->extractCommentsStartingWith('i18n:', 'Translators:');

//Scan files
foreach (glob('*.php') as $file) {
    $phpScanner->scanFile($file);
}

//Save the translations in .po files
$generator = new PoGenerator();

foreach ($phpScanner->getTranslations() as $domain => $translations) {
    $generator->generateFile($translations, "locales/{$domain}.po");
}

Merging translations

You will want to update or merge translations. The function mergeWith create a new Translations instance with other translations merged:

$translations3 = $translations1->mergeWith($translations2);

But sometimes this is not enough, and this is why we have merging options, allowing to configure how two translations will be merged. These options are defined as constants in the Gettext\Merge class, and are the following:

Constant Description
Merge::TRANSLATIONS_OURS Use only the translations present in $translations1
Merge::TRANSLATIONS_THEIRS Use only the translations present in $translations2
Merge::TRANSLATIONS_OVERRIDE Override the translation and plural translations with the value of $translation2
Merge::HEADERS_OURS Use only the headers of $translations1
Merge::HEADERS_REMOVE Use only the headers of $translations2
Merge::HEADERS_OVERRIDE Overrides the headers with the values of $translations2
Merge::COMMENTS_OURS Use only the comments of $translation1
Merge::COMMENTS_THEIRS Use only the comments of $translation2
Merge::EXTRACTED_COMMENTS_OURS Use only the extracted comments of $translation1
Merge::EXTRACTED_COMMENTS_THEIRS Use only the extracted comments of $translation2
Merge::FLAGS_OURS Use only the flags of $translation1
Merge::FLAGS_THEIRS Use only the flags of $translation2
Merge::REFERENCES_OURS Use only the references of $translation1
Merge::REFERENCES_THEIRS Use only the references of $translation2

Use the second argument to configure the merging strategy:

$strategy = Merge::TRANSLATIONS_OURS | Merge::HEADERS_OURS;

$translations3 = $translations1->mergeWith($translations2, $strategy);

There are some typical scenarios, one of the most common:

  • Scan php templates searching for entries to translate
  • Complete these entries with the translations stored in a .po file
  • You may want to add new entries to the .po file
  • And also remove those entries present in the .po file but not in the templates (because they were removed)
  • But you want to update some translations with new references and extracted comments
  • And keep the translations, comments and flags defined in .po file

For this scenario, you can use the option Merge::SCAN_AND_LOAD with the combination of options to fit this needs (SCAN new entries and LOAD a .po file).

$newEntries = $scanner->scanFile('template.php');
$previousEntries = $loader->loadFile('translations.po');

$updatedEntries = $newEntries->mergeWith($previousEntries);

gettext language list automatically generated from CLDR data

Static usage

To use the languages data generated from this tool you can use the bin/export-plural-rules command.

Export command line options

export-plural-rules supports the following options:

  • --us-ascii If specified, the output will contain only US-ASCII characters. If not specified, the output charset is UTF-8.
  • --languages=<LanguageId>[,<LanguageId>,...]] --language=<LanguageId>[,<LanguageId>,...]] Export only the specified language codes. Separate languages with commas; you can also use this argument more than once; it's case-insensitive and accepts both '_' and '-' as locale chunks separator (e.g. we accept it_IT as well as it-it). If this option is not specified, the result will contain all the available languages.
  • --reduce=yes|no If set to yes the output won't contain languages with the same base language and rules. For instance nl_BE (Flemish) will be omitted because it's the same as nl (Dutch). Defaults to no if --languages is specified, to yes otherwise.
  • --parenthesis=yes|no If set to no, extra parenthesis will be omitted in generated plural rules formulas. Those extra parenthesis are needed to create a PHP-compatible formula. Defaults to yes
  • --output=<file name> If specified, the output will be saved to <file name>. If not specified we'll output to standard output.

Export formats

export-plural-rules can generate data in the following formats:

  • json: compressed JSON data

    export-plural-rules json
  • prettyjson: uncompressed JSON data

    export-plural-rules prettyjson
  • html: html table (see the result)

    export-plural-rules html
  • php: build a php file that can be included

    export-plural-rules --output=yourfile.php php

    Then you can use that generated file in your php scripts:

    $languages = include 'yourfile.php';
  • ruby: build a ruby file that can be included

    export-plural-rules --parenthesis=no --output=yourfile.rb ruby

    Then you can use that generated file in your ruby scripts:

    require './yourfile.rb'
    PLURAL_RULES['en']
  • xml: generate an XML document (here you can find the xsd XML schema)

    export-plural-rules xml
  • po: generate the gettext .po headers for a single language

    export-plural-rules po --language=YourLanguageCode

Dynamic usage

With Composer

You can use Composer to include this tool in your project. Simply launch composer require gettext/languages or add "gettext/languages": "*" to the "require" section of your composer.json file.

Without Composer

If you don't use composer in your project, you can download this package in a directory of your project and include the autoloader file:

require_once 'path/to/src/autoloader.php';

Main methods

The most useful functions of these tools are the following

$allLanguages = Gettext\Languages\Language::getAll();
...
$oneLanguage = Gettext\Languages\Language::getById('en_US');
...

getAll returns a list of Gettext\Languages\Language instances, getById returns a single Gettext\Languages\Language instance (or null if the specified language identifier is not valid).

The main properties of the Gettext\Languages\Language instances are:

  • id: the normalized language ID (for instance en_US)
  • name: the language name (for instance American English for en_US)
  • supersededBy: the code of a language that supersedes this language code (for instance, jw is superseded by jv to represent the Javanese language)
  • script: the script name (for instance, for zh_Hans - Simplified Chinese - the script is Simplified Han)
  • territory: the name of the territory (for instance United States for en_US)
  • baseLanguage: the name of the base language (for instance English for en_US)
  • formula: the gettext formula to distinguish between different plural rules. For instance n != 1
  • categories: the plural cases applicable for this language. It's an array of Gettext\Languages\Category instances. Each instance has these properties:
    • id: can be (in this order) one of zero, one, two, few, many or other. The other case is always present.
    • examples: a representation of some values for which this plural case is valid (examples are simple numbers like 1 or complex ranges like 0, 2~16, 100, 1000, 10000, 100000, 1000000, …)

Is this data correct?

Yes - as far as you trust the Unicode CLDR project.

The conversion from CLDR to gettext includes also a lot of tests to check the results. And all passes 😉.

Reference

CLDR

The CLDR specifications define the following variables to be used in the CLDR plural formulas:

  • n: absolute value of the source number (integer and decimals) (eg: 9.870 => 9.87)
  • i: integer digits of n (eg: 9.870 => 9)
  • v: number of visible fraction digits in n, with trailing zeros (eg: 9.870 => 3)
  • w: number of visible fraction digits in n, without trailing zeros (eg: 9.870 => 2)
  • f: visible fractional digits in n, with trailing zeros (eg: 9.870 => 870)
  • t: visible fractional digits in n, without trailing zeros (eg: 9.870 => 87)
  • c: exponent of the power of 10 used in compact decimal formatting (eg: 98c7 => 7)
  • e: synonym for c

gettext

The gettext specifications define the following variables to be used in the gettext plural formulas:

  • n: unsigned long int

Conversion CLDR > gettext

CLDR variable gettext equivalent
n n
i n
v 0
w 0
f empty
t empty
c empty
e empty

Parenthesis in ternary operators

The generated gettext formulas contain some extra parenthesis, in order to avoid problems in some programming language. For instance, let's assume we have this formula: (0 == 0) ? 0 : (0 == 1) ? 1 : 2

So, in order to avoid problems, instead of a simple a ? 0 : b ? 1 : 2 the resulting formulas will be in this format: a ? 0 : (b ? 1 : 2)

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: GPL-3.0-or-later
  • 更新时间: 2026-07-12

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固