{"id":36979038,"url":"https://github.com/vertilia/text","last_synced_at":"2026-01-13T22:48:36.419Z","repository":{"id":58699201,"uuid":"529975190","full_name":"vertilia/text","owner":"vertilia","description":"Translation library based on concept of gettext .po files","archived":false,"fork":false,"pushed_at":"2024-04-11T10:58:41.000Z","size":118,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-21T17:34:42.718Z","etag":null,"topics":["gettext","i18n","internationalization","l10n","language","localization","nls","php","translation"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vertilia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-08-28T20:43:38.000Z","updated_at":"2023-07-18T09:15:51.000Z","dependencies_parsed_at":"2024-04-11T11:33:22.213Z","dependency_job_id":null,"html_url":"https://github.com/vertilia/text","commit_stats":{"total_commits":19,"total_committers":2,"mean_commits":9.5,"dds":"0.10526315789473684","last_synced_commit":"267b9cf7373c095fc4fcf029fb3b086c8a5f7447"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/vertilia/text","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vertilia%2Ftext","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vertilia%2Ftext/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vertilia%2Ftext/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vertilia%2Ftext/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vertilia","download_url":"https://codeload.github.com/vertilia/text/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vertilia%2Ftext/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28403698,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T14:36:09.778Z","status":"ssl_error","status_checked_at":"2026-01-13T14:35:19.697Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gettext","i18n","internationalization","l10n","language","localization","nls","php","translation"],"created_at":"2026-01-13T22:48:35.880Z","updated_at":"2026-01-13T22:48:36.408Z","avatar_url":"https://github.com/vertilia.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# text\n\nTranslation library based on [gettext](https://www.gnu.org/software/gettext) concept, tools and PO files.\n\n## Description\n\nThis library is intended to continue using gettext as project internationalization approach, keeping proven localization\nmethodology but eliminate dependency on complex target system configuration and on `gettext` php extension. This latter\nmay become unsuitable for specific environments.\n\n`xgettext` extractor from standard gettext toolchain does not handle correctly PHP heredoc/nowdoc syntax on all systems.\nHere we provide a replacement `xtext` script that extracts strings into a POT format, which may be used as a source file\nfor your gettext workflow.\n\nPO catalogue (representing a gettext domain), extracted from the codebase (by standard gettext method or using `xtext`)\nis then transformed by the bundled `po2php` tool to a native `.php` class. This class keeps translations in memory and\nimplements language-specific rules as native php code.\n\nSimple messages are maintained via the `_()` method, plural forms and contexts are maintained via corresponding\n`nget()`, `pget()` and `npget()` methods.\n\n## Advantages\n\n\u003e **From manual:**\u003cbr\u003e\n\u003e GNU `gettext` is designed to minimize the impact of internationalization on program sources, keeping this impact as\n\u003e small and hardly noticeable as possible. Internationalization has better chances of succeeding if it is very light\n\u003e weighted, or at least, appear to be so, when looking at program sources.\n\n- no need to use constants or other intermediate constructs replacing messages in source code;\n- handling of plural forms;\n- handling of context-aware functions (`pgettext` family) that are currently missing from php extension;\n- no need to install additional locales on target OS or setting environment variables;\n- standard translation process based on PO files; multitude of editors or processes may be used;\n- translations are stored in `.php` files which allows for a quick autoloading and opcode caching, minimized runtime\n  effort to get translation into memory and finding translation for source string;\n- stable and predictable work in multiprocess web environments, not tied to host system configuration and currently\n  installed system locales;\n- bundled tools to correctly handle PHP heredoc/nowdoc syntax and to compile .PO files to native php code.\n\n## Installation\n\n```shell\ncomposer require vertilia/text\n```\n\n## Usage\n\nProgramming in C with gettext historically consists of the following phases (simplified):\n\n1. define the source messages language used in your code (normally english)\n2. use gettext functions in your source code when working with localized messages\n   - (without existing translations, gettext functions simply return the passed strings, so the code is already\n     working at this stage, returning english messages in all environments)\n3. use gettext utilities to scan your code and extract localized messages, producing (or updating) `.po` text files\n   - (`.po` text files contain messages extracted from code, translations to the target language and rules for plural\n     forms of the target language)\n4. translate new/updated messages in `.po` file\n5. compile text `.po` file into binary `.mo` file\n6. copy `.mo` file into your code, so that now gettext functions (mentioned in 2.) could use them to extract\n   translations for their arguments\n7. if new language is added to application, assure the corresponding locale is configured on target system\n\nIn `Text` we can bypass phases 5., 6. and 7., and compile `.po` files directly into `.php` classes, containing\ntranslated strings and plural forms rules for target language.\n\nThese generated classes are stored in `locale/` folder and are configured via composer autoloader. They are handled by\nopcode cache just like other php code and use the lowest footprint at runtime since only need one CRC32 transformation\nto return existing translation (or a lack of one).\n\nFor large codebases, as with normal gettext, you can break down translations on domains, which mostly signifies using\ndifferent `.po` files per domain.\n\nWhen you start the project, and while you have no `.po` file yet, you can use the base `\\Vertilia\\Text\\Text` object to\nprovide translated text. It will simply return the passed argument as translated message, which is mostly ok for\ndebugging purposes. Even in its basic form, it will already be smart enough to correctly handle plural forms for English\nmessages:\n\n```php\n\u003c?php\n\ninclude __DIR__ . '/../vendor/autoload.php';\n\n$t = new Vertilia\\Text\\Text();\n\necho $t-\u003e_('Just a test'), PHP_EOL;                      // output: Just a test\necho $t-\u003epget('page', 'Next'), PHP_EOL;                  // output: Next\necho $t-\u003enget('One page', 'Multiple pages', 1), PHP_EOL; // output: One page\necho $t-\u003enget('One page', 'Multiple pages', 5), PHP_EOL; // output: Multiple pages\necho $t-\u003enpget('page', 'One sent', 'Multiple sent', 5), PHP_EOL; // output: Multiple sent\n```\n\nWhen you extract messages from the above code with gettext tools (we highly recommend using widely-available translation\ntools, like [POedit](https://poedit.net/) or others) you'll produce a text file with `.po` extension containing source\nlanguage messages, placeholders for target language translations, say French, and a rule for French plural form\nconversion. After translating the placeholders in `.po` file into French, you normally include the result file in your\nproject as `locale/fr_FR/LC_MESSAGES/messages.po`. This is a GNU norm, but with `Text` you may use any folder and\nfilename. The most important part here is that you (and other users of your codebase) could easily locate translation\nfiles and clearly distinguish languages and domains.\n\n\u003e See [Keywords for `xgettext`](#keywords-for-xgettext) below for things to configure when running gettext tools on your\n\u003e codebase.\n\n\u003e See [`xtext` reference](#xtext-reference) below if `xgettext` is not working correctly on your system.\n\nSimplified view of the contents of `messages.po` file for our project:\n\n| source (en)                       | target (fr)          |\n|-----------------------------------|----------------------|\n| `plural form rule:`               | `(n \u003e 1)`            |\n| \"Just a test\"                     | \"Juste un test\"      |\n| \"Next\" (context: \"page\")          | \"Suivante\"           |\n| \"One page\"                        | \"Une page\"           |\n| \"Multiple pages\"                  | \"Plusieurs pages\"    |\n| \"One sent\" (context: \"page\")      | \"Une envoyée\"        |\n| \"Multiple sent\" (context: \"page\") | \"Plusieurs envoyées\" |\n\nNote where you stored the resulting `messages.po` file, since you'll need it right away to produce translations class.\nTo do this you'll run the bundled `po2php` command (see examples [below](#po2php-reference)) and give it the path to the\n`messages.po` file. It will output the php code that you will include in your project as an easily located\n`locale-src/MessagesFr.php` file:\n\n```shell\nvendor/bin/po2php -n App\\\\L10n -c MessagesFr \\\n  locale/fr_FR/LC_MESSAGES/messages.po \\\n  \u003esrc/L10n/MessagesFr.php\n```\n\nSo for now, you generated 2 additional files in your project:\n\n- `locale/fr_FR/LC_MESSAGES/messages.po`: text file with English source messages from your application code, French\n  translations for every message and a simple rule that describes the use of plural form in target language, French. You\n  will need this file to update existing translations, add new ones and remove unused ones. This is a standard PO file\n  that you may edit with many available tools. Every translation bureau will handle this format (if it does not, you\n  better choose another one).\n- `locale-src/MessagesFr.php`: php class generated from `messages.po` file that encapsulates translations for target\n  language and a method for handling French plural form.\n\nNow it's time to use the generated `MessagesFr` class instead of the base `Text` to display your translated messages:\n\n```php\n\u003c?php\n\nrequire_once __DIR__ . '/../vendor/autoload.php';\n\n$t = new App\\L10n\\MessagesFr();\n\necho $t-\u003e_('Just a test'), PHP_EOL;                      // output: Juste un test\necho $t-\u003epget('page', 'Next'), PHP_EOL;                  // output: Suivante\necho $t-\u003enget('One page', 'Multiple pages', 1), PHP_EOL; // output: Une page\necho $t-\u003enget('One page', 'Multiple pages', 5), PHP_EOL; // output: Plusieurs pages\necho $t-\u003enpget('page', 'One sent', 'Multiple sent', 5), PHP_EOL; // output: Plusieurs envoyées\n```\n\n\u003e See [Proposed configuration for `composer` and `git`](#proposed-configuration-for-composer-and-git) below for sample\n\u003e `composer` configuration.\n\nNow it's up to you to create translations for other languages.\n\n## Process overview\n\n`Vertilia\\Text\\Text` object contains methods to handle translated messages. Base class does not have translations, so\nits methods simply return passed arguments. When translations are added to `.po` files, they are saved as language\nclasses extending `Vertilia\\Text\\Text` base class with translations and overridden `plural()` method to select\ncorrect plural forms in target languages.\n\nNormally your code will consist of injecting `Vertilia\\Text\\TextInterface` objects, creating messages and work with\nexternal PO editor program to extract messages from code, handle translations in `.po` files, and update language\nclasses with `po2php` tool.\n\nYour localization process with `Text` will follow the following path:\n```mermaid\ngraph\n    A[Add/Update messages wrapped by Text methods] --\u003e|gettext| B\n    B[Update .po files] --\u003e|po2php| C\n    C[Update .php files] --\u003e|Autoloader| A\n```\n\n## `Text` reference\n\n### `Text::_()`\n\nTranslate message\n\n```php\npublic function _(string $message): string;\n```\n\n#### Parameters\n- `$message` Message in source language to translate.\n\n#### Return value\nMessage translated into target language (or original message if translation not found).\n\n#### Example 1: base class, no translation\n```php\n$t = \\Vertilia\\Text\\Text();\necho $t-\u003e_(\"Several words\"); // output: Several words\n```\n\n#### Example 2: `MessagesRu` class created after translating `messages.po` into Russian\n```php\n$t = \\App\\L10n\\MessagesRu();\necho $t-\u003e_(\"Several words\"); // output: Несколько слов\n```\n\n### `Text::nget()`\n\nTranslation for plural forms of the message, based on argument.\n\n```php\npublic function nget(string $singular, string $plural, int $count): string;\n```\n\n#### Parameters\n- `$singular` Singular form of message in source language.\n- `$plural` Plural form of message in source language.\n- `$count` Counter to select the plural form.\n\n#### Return value\nOne of plural forms of translated message in target language for provided `$count`.\n\n#### Example 1: base class, no translation\n```php\n$t = \\Vertilia\\Text\\Text();\nprintf($t-\u003enget(\"%u word\", \"%u words\", 1), 1); // output: 1 word\nprintf($t-\u003enget(\"%u word\", \"%u words\", 2), 2); // output: 2 words\nprintf($t-\u003enget(\"%u word\", \"%u words\", 5), 5); // output: 5 words\n```\n\n#### Example 2: `MessagesRu` class created after translating `messages.po` into Russian\n```php\n$t = \\App\\L10n\\MessagesRu();\nprintf($t-\u003enget(\"%u word\", \"%u words\", 1), 1); // output: 1 слово\nprintf($t-\u003enget(\"%u word\", \"%u words\", 2), 2); // output: 2 слова\nprintf($t-\u003enget(\"%u word\", \"%u words\", 5), 5); // output: 5 слов\n```\n\n### `Text::npget()`\n\nTranslate plural form of the message in given context, based on argument.\n\n```php\npublic function npget(string $context, string $singular, string $plural, int $count): string;\n```\n\n#### Parameters\n- `$context` Context of message in source language.\n- `$singular` Singular form of message in source language.\n- `$plural` Plural form of message in source language.\n- `$count` Counter to select the plural form.\n\n#### Return value\nOne of plural forms of translated message in target language and context for provided `$count`.\n\n#### Example 1: base class, no translation\n```php\n$t = \\Vertilia\\Text\\Text();\nprintf($t-\u003enpget(\"star\", \"%u bright\", \"%u bright\", 1), 1); // output: 1 bright\nprintf($t-\u003enpget(\"star\", \"%u bright\", \"%u bright\", 2), 2); // output: 2 bright\nprintf($t-\u003enpget(\"star\", \"%u bright\", \"%u bright\", 5), 5); // output: 5 bright\n```\n\n#### Example 2: `MessagesRu` class created after translating `messages.po` into Russian\n```php\n$t = \\App\\L10n\\MessagesRu();\nprintf($t-\u003enpget(\"star\", \"%u bright\", \"%u bright\", 1), 1); // output: 1 яркая\nprintf($t-\u003enpget(\"star\", \"%u bright\", \"%u bright\", 2), 2); // output: 2 яркие\nprintf($t-\u003enpget(\"star\", \"%u bright\", \"%u bright\", 5), 5); // output: 5 ярких\n```\n\n### `Text::pget()`\n\nTranslate the message in given context.\n\n```php\npublic function pget(string $context, string $message): string;\n```\n\n#### Parameters\n- `$context` Context of the message in source language.\n- `$message` Message in source language to translate.\n\n#### Return value\nTranslated message in target language and context.\n\n#### Example 1: base class, no translation\n```php\n$t = \\Vertilia\\Text\\Text();\nprintf($t-\u003enpget(\"star\", \"It's bright\")); // output: It's bright\n```\n\n#### Example 2: `MessagesRu` class created after translating `messages.po` into Russian\n```php\n$t = \\App\\L10n\\MessagesRu();\nprintf($t-\u003enpget(\"star\", \"It's bright\")); // output: Она яркая\n```\n\n## Keywords for `xgettext`\n\nTo allow `xgettext` to extract messages from `Text` methods that replace classic gettext functions, the\nfollowing configuration should be provided for `xgettext` command line utility:\n```\nxgettext ... --keyword=_ --keyword=pget:1c,2 --keyword=nget:1,2 --keyword=npget:1c,2,3\n```\n\nGUI utilities like POedit will provide a configuration screen where the keywords may be specified as the following list:\n\n- `nget:1,2`\n- `pget:1c,2`\n- `npget:1c,2,3`\n\n## Proposed configuration for `composer` and `git`\n\nWhen producing `Text` classes we recommend you to store them in `L10n/` folder of your application. Consider the\nfollowing layout (simplified, 3 languages):\n```\n/app/\n├─ locale/\n│  ├─ en_US/\n│  │  └─ LC_MESSAGES/\n│  │     └─ messages.po\n│  ├─ fr_FR/\n│  │  └─ LC_MESSAGES/\n│  │     └─ messages.po\n│  ├─ ru_RU/\n│  │  └─ LC_MESSAGES/\n│  │     └─ messages.po\n│  └─ messages.pot\n├─ src/\n│  ├─ L10n/\n│  │  ├─ MessagesEn.php\n│  │  ├─ MessagesFr.php\n│  │  └─ MessagesRu.php\n│  └─ ...\n├─ vendor/\n│  ├─ autoload.php\n│  ├─ composer/\n│  └─ vertilia/\n├─ www/\n│  └─ index.php\n├─ .gitattributes\n└─ composer.json\n```\n\nHere, your application code is located in `src/` and `www/` folders and, presuming the application namespace is `App`,\nmessages classes namespace is `App\\L10n`, your composer `autoload` directive is configured as follows:\n```json\n{\n  \"autoload\": {\n    \"psr-4\": {\n      \"App\\\\\": \"src/\"\n    }\n  }\n}\n```\n\nPlease note, `locale/` folder storing `.po` files is separated from `L10n/` folder with `Text` message classes to\nsimplify exclusion of this folder from the binary version of your application. You don't need intermediate files on\nproduction hosts, so you will most likely include the following line into your `.gitattributes`:\n```\n/locale export-ignore\n```\n\n## `xtext` reference\n\n```shell\nvendor/bin/xtext -h\n```\n```\nUsage: xtext [OPTIONS] FILES,\nOPTIONS:\n-h      Display usage message and quit\n-x EXCLUDE_PATH\n        Pattern to exclude when scanning FILES, may be repeated to\n        provide multiple patterns. Executes before -i.\n-i INCLUDE_PATH\n        Pattern to include when scanning FILES, may be repeated to\n        provide multiple paths. Executes after -x. Default: *\n-c COMMENT_TAG\n        Comment tag to mark extractable comments from the source code.\n        Use empty string to extract all comments\nFILES:\n        A list of one or more file or directory. If directory names are\n        specified, they are scanned recursively. Options -x and -i are\n        used to limit processed files.\nEXAMPLES:\nxtext *.php\n        Scan all .php files in current dir\nxtext -c '' *.php\n        Scan all .php files in current dir, also extract comments\nxtext -x /*/vendor -x /*/tests -i '*.php' -c TRANSLATORS: /app \u003emsg.pot\n        Scan all .php files in /app directory and sub-directories,\n        excluding vendor and tests folders, extract comments starting\n        with TRANSLATORS: tag and write output to msg.pot file\n```\n\nOn some systems, `xgettext` tool used to scan php sources and extract translatable strings may break if php files use\nheredoc/nowdoc syntax, especially with indented closing identifier (available since php 7.3).\n\nTo allow correct extraction of gettext lines from the sources, bundled `xtext` utility may be used instead. This tool\nwill scan the source folders, find the translatable strings and output a POT file, containing all detected strings. It\nmaintains the code references for translations and may also include comments that precede calls to corresponding `Text`\nand `gettext` functions in the code.\n\nPOedit tool mentioned above may use both methods of updating existing PO files, either by scanning source directories\nwith `xgettext` to produce the POT file and transparently merge it with existing translations, or use an existing POT\nfile with extracted translations. First method is simpler, but if POedit cannot extract translations from all files in\nyour codebase automatically, you may generate the POT file with `xtext` and use it to update translations.\n\n## `po2php` reference\n\n```shell\nvendor/bin/po2php --help\n```\n```\nUsage: po2php [OPTIONS] messages.po\nOPTIONS:\n-n, --namespace=NAMESPACE   Namespace to use (default: none)\n-c, --class=CLASS_NAME      Class name (default: Messages)\n-e, --extends=PARENT_CLASS  Parent class name implementing \\Vertilia\\Text\\TextInterface\n                            (default: \\Vertilia\\Text\\Text)\n-5, --php5                  Produce php5-compatible code (use php5 branch of Text)\n-h, --help                  Print this screen\n```\n\n#### Example 1: generate `MessagesRu` catalog in `tests/locale`\n\n```shell\nvendor/bin/po2php -n App\\\\Tests\\\\Locale -c MessagesRu \\\n  tests/locale/ru_RU/LC_MESSAGES/messages.po \\\n  \u003etests/locale/MessagesRu.php\n```\n\n#### Example 2: generate `MessagesRu` catalog in `tests/locale` with `docker`\n\n```shell\ndocker run --rm --volume \"$PWD\":/app php \\\n  /app/vendor/bin/po2php -n App\\\\Tests\\\\Locale -c MessagesRu \\\n  /app/tests/locale/ru_RU/LC_MESSAGES/messages.po \\\n  \u003etests/locale/MessagesRu.php\n```\n\n## Plural forms in different languages\n\nThe plural form selector, incorporated into PO files, is in fact a C language code snippet that returns a 0-based index\nof a plural form. In most cases this code translates to php in quite a straightforward way, like in example below:\n```\n# English, nplurals = 2\n(n != 1)\n```\nGermanic-family languages like English or German only have 2 plural forms and every number which is not 1 is actually a\nplural:\n\n| example | plural form |\n|---------|-------------|\n| 0 lines | 1           |\n| 1 line  | 0           |\n| 2 lines | 1           |\n| 3 lines | 1           |\n\nOther language families may contain a more complex condition:\n```\n# Russian, nplurals = 3\n(n%10==1 \u0026\u0026 n%100!=11 ? 0 : n%10\u003e=2 \u0026\u0026 n%10\u003c=4 \u0026\u0026 (n%100\u003c12 || n%100\u003e14) ? 1 : 2)\n```\n```\n# Russian (alternative form), nplurals = 3\n(n%10==0 || n%10\u003e4 || (n%100\u003e=11 \u0026\u0026 n%100\u003c=14) ? 2 : n%10 != 1)\n```\nSlavic-family languages like Russian or Serbian have 3 plural forms, which are close to impossible to describe in one\nphrase. Single form is for every number that terminates by 1 (but not by 11). First plural form is for numbers\nterminated by 2, 3 or 4 (but not by 12, 13 or 14). All the rest (including 0, 11, 12, 13 and 14) are second plural form.\nExamples:\n\n| example    | plural form |\n|------------|-------------|\n| 0 строк    | 2           |\n| 1 строка   | 0           |\n| 2 строки   | 1           |\n| 5 строк    | 2           |\n| 10 строк   | 2           |\n| 11 строк   | 2           |\n| 12 строк   | 2           |\n| 15 строк   | 2           |\n| 20 строк   | 2           |\n| 21 строка  | 0           |\n| 22 строки  | 1           |\n| 25 строк   | 2           |\n| 100 строк  | 2           |\n| 101 строка | 0           |\n| 102 строки | 1           |\n| 105 строк  | 2           |\n\nYou can find more examples at [gettext manual](https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html).\n\n## Plural forms rules rewrite for specific languages (before php 8.0)\n\nTernary condition statement in php before version 8.0 has other associativity than in C, so the default rule in PO file\nfor languages using chained ternary operators for plural form selector needs to be corrected with additional parenthesis\nin PO file:\n```\n# Russian (php7-compat), nplurals = 3\n(n%10==1 \u0026\u0026 n%100!=11 ? 0 : (n%10\u003e=2 \u0026\u0026 n%10\u003c=4 \u0026\u0026 (n%100\u003c12 || n%100\u003e14) ? 1 : 2))\n```\n```\n# Russian (php7-compat alternative form), nplurals = 3\n(n%10==0 || n%10\u003e4 || (n%100\u003e=11 \u0026\u0026 n%100\u003c=14) ? 2 : (n%10 != 1))\n```\n\n## Plural forms usage with `printf()`\n\nIn brief: to produce lines using plural forms based on a variable value, use the following construct:\n```php\nprintf($t-\u003enget(\"%d file removed\", \"%d files removed\", $n), $n);\n```\n\nHere, the first call to `nget` with `$n` will select corresponding format string either for single or for plural form,\nand then this selected string will be passed to `printf` with another `$n` which will now be inserted in corresponding\n`%d` placeholder.\n\nFor detailed discussion see [gettext manual](https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html).\n\n## Class methods replacement for gettext functions\n\n| `gettext` function                  | `Text` method |\n|-------------------------------------|---------------|\n| `_()`, `gettext()`                  | `_()`         |\n| `ngettext()`                        | `nget()`      |\n| context-aware (`pgettext` in C API) | `pget()`      |\n| `npgettext()`                       | `npget()`     |\n\nNote the lack of domain functions (`dgettext`, ...) Domains in gettext are represented by different translation files,\nso to use a translation from another domain one should instantiate another `Text` object.\n\nExample:\n- `gettext`-style:\n  ```php\n  putenv('LC_ALL=fr_FR');\n  setlocale(LC_ALL, 'fr_FR');\n  bindtextdomain(\"myPHPApp\", \"./locale\");\n  textdomain(\"myPHPApp\");\n  echo gettext(\"Welcome to My PHP Application\"), \"\\n\";\n  echo dgettext(\"anotherPHPApp\", \"Welcome to Another PHP Application\"), \"\\n\";\n  ```\n- `Text`-style:\n  ```php\n  $myDomain = \\App\\L10n\\MyPhpAppFr();\n  $anotherDomain = \\App\\L10n\\AnotherPhpAppFr();\n  echo $myDomain-\u003e_(\"Welcome to My PHP Application\"), \"\\n\";\n  echo $anotherDomain-\u003e_(\"Welcome to Another PHP Application\"), \"\\n\";\n  ```\n\nAlso, while context-aware functions (`pgettext` family) are missing from a bundled PHP gettext extension, `xtext` will\nextract strings from similarly named functions when possible.\n\n## Resources\n\n- GNU gettext manual:\n  https://www.gnu.org/software/gettext/manual/gettext.html\n- Preparing translatable strings:\n  https://www.gnu.org/software/gettext/manual/html_node/Preparing-Strings.html#Preparing-Strings\n- POedit translations editor:\n  https://poedit.net/\n- `.gitattributes`:\n  https://git-scm.com/docs/gitattributes#_creating_an_archive","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvertilia%2Ftext","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvertilia%2Ftext","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvertilia%2Ftext/lists"}