{"id":22290684,"url":"https://github.com/xfra35/f3-multilang","last_synced_at":"2025-07-28T23:31:39.056Z","repository":{"id":23747742,"uuid":"27121775","full_name":"xfra35/f3-multilang","owner":"xfra35","description":"Create multilingual apps with this localization plugin for the PHP Fat-Free Framework","archived":false,"fork":false,"pushed_at":"2020-05-14T15:43:55.000Z","size":96,"stargazers_count":48,"open_issues_count":4,"forks_count":13,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-07-04T10:55:56.970Z","etag":null,"topics":["fat-free-framework","i18n","localization","multilang","multilingual","php"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xfra35.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}},"created_at":"2014-11-25T11:01:39.000Z","updated_at":"2024-02-24T00:44:36.000Z","dependencies_parsed_at":"2022-08-24T14:09:32.733Z","dependency_job_id":null,"html_url":"https://github.com/xfra35/f3-multilang","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/xfra35/f3-multilang","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xfra35%2Ff3-multilang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xfra35%2Ff3-multilang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xfra35%2Ff3-multilang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xfra35%2Ff3-multilang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xfra35","download_url":"https://codeload.github.com/xfra35/f3-multilang/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xfra35%2Ff3-multilang/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267604302,"owners_count":24114521,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-28T02:00:09.689Z","response_time":68,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["fat-free-framework","i18n","localization","multilang","multilingual","php"],"created_at":"2024-12-03T17:13:46.236Z","updated_at":"2025-07-28T23:31:38.735Z","avatar_url":"https://github.com/xfra35.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Multilang\n**…for F3 polyglots!**\n\nThis plugin for [Fat-Free Framework](http://github.com/bcosca/fatfree) provides a URL-friendly mean to localize your web site/app.\n\nDemo: [here](http://ml.aesx.fr).\n\n* [Basic usage](#basic-usage)\n* [Root URL](#root-url)\n* [Advanced usage](#advanced-usage)\n    * [Rewrite URLs](#rewrite-urls)\n    * [Exclude a language from a route](#exclude-a-language-from-a-route)\n    * [Global routes](#global-routes)\n* [Rerouting](#rerouting)\n* [Migration mode](#migration-mode)\n* [Strict mode](#strict-mode)\n* [Passthru mode](#passthru-mode)\n* [API](#api)\n* [Potential improvements](#potential-improvements)\n\n## Basic usage\n\n### Step 1:\nDeclare the languages of your app in the `MULTILANG.languages` variable:\n```php\n$f3-\u003eset('MULTILANG.languages',array(\n  'en' =\u003e 'en-GB,en-US,en',\n  'ja' =\u003e 'ja-JP,ja',\n  'es' =\u003e 'es-ES,es'\n));\n```\nThe same declaration can be achieved in a configuration file using the following syntax:\n```ini\n[MULTILANG.languages]\nen = en-GB, en-US, en\nja = ja-JP, ja\nes = es-ES, es\n```\n\n**NB1:** each entry maps a language identifier (`en`, `ja`, `es`) to one or more locales. The language identifiers are arbitrary (`english`, `en-GB`, `japan`, etc) but remember: they will appear in your URLs.\n\n**NB2:** The first defined language is considered as the primary language, which means it is set as [FALLBACK](http://fatfreeframework.com/quick-reference#FALLBACK). In our example, on a japanese page, the locales and dictionaries would be searched in the following order: ja-JP, ja, en-GB, en, en-US.\n\n**NB3:** It is strongly advised to include a country-independant language code (`en`, `ja`, `es`, etc...) in the list of locales for a better browser language detection.\n\n### Step 2:\nStart the plugin by instantiating the class, **just before** the call to `$f3-\u003erun`:\n```php\n$f3-\u003econfig('config.ini');\nMultilang::instance();\n$f3-\u003erun();\n```\n\n### That's it!\nNow every existing URL has been duplicated into as many languages you've declared, using identifiers as prefixes:\n```\n         =\u003e /en/contact\n/contact =\u003e /ja/contact\n         =\u003e /es/contact\n```\n* How about the original URLs? =\u003e They have been removed.\n* How about the original root `/`? =\u003e It autodetects the browser language. [See below](#root-url)\n\n## Root URL\n\nBy default, the root URL autodetects the browser language and performs a redirection to its root page.\n\nIn our example, a Spanish user would be redirected to `/es` while a Russian user would be redirected to `/en` (fallback to the primary language).\n\nYou can override this default behaviour by setting the `MULTILANG.root` to a custom handler:\n```ini\n[MULTILANG]\nroot = App\\MyRoot\n```\n\nUse case: display a splash page with the list of available languages.\n\n\n## Advanced usage\n(requires the usage of [route aliases](http://fatfreeframework.com/routing-engine#named-routes))\n\n### Rewrite URLs\nEach translated URL consists of a language identifier followed by the original URL:\n```\n/es + /terms-and-conditions = /es/terms-and-conditions\n```\nYou can customize the second part by setting the `MULTILANG.rules` variable.\nFor example, in order to translate the Spanish URL above, you could write (assuming the route is named `terms`):\n```php\n$f3-\u003eset('MULTILANG.rules',array(\n  'es' =\u003e array(\n    'terms' =\u003e '/terminos-y-condiciones'\n  )\n));\n```\nThe same declaration can be achieved in a configuration file using the following syntax:\n```ini\n[MULTILANG.rules.es]\nterms = /terminos-y-condiciones\n```\n\n### Exclude a language from a route\n\nWhen translating a website, you may need to perform a progressive translation, route by route.\nIt could also occur that some parts won't be localized at all (for example a blog).\n\nFor this purpose, you can remove a route for a specific language by setting it to `FALSE`:\n```ini\n[MULTILANG.rules.es]\nblog = FALSE\n```\nA request to `/es/blog` will return a 404 error.\n\n### Global routes\n\nSome routes have to stay language-independant, for example a captcha doesn't have to be localized.\nAlso back offices often happen to be monolingual.\n\nThose global routes are not rewritten: they keep their original URL.\nThey are defined using the `MULTILANG.global` variable:\n```ini\n[MULTILANG]\nglobal = captcha\n;could also be an array:\nglobal = alias1, alias2, alias3, /admin, /foo/bar\n```\n\nEach entry can be a route alias or a URI prefix.\n\n**NB:** on a global route, the language is auto-detected by default.\nSo in the case of a monolingual back office, you may need to force the language at the controller level.\n\n## Rerouting\n\n### If you're using named routes...\n\n`$f3-\u003ereroute` will work as expected, that is to say, it will reroute to the\ncurrent language URL of the provided named route. E.g:\n\n```php\n$f3-\u003ereroute('@contact'); // OK =\u003e reroute to /xx/contact where xx is the current language\n```\n\n### If you're using unnamed routes...\n\nIn that case, you have to provide a language-prefixed URL to `$f3-\u003ereroute`:\n\n```php\n$f3-\u003ereroute('/en/contact'); // OK\n$f3-\u003ereroute('/contact'); // Error =\u003e 404 Not Found\n```\n\nIf you'd prefer to give the short URL to the framework and have it automatically prefix the URL with the current language,\nuse the `$ml-\u003ereroute` method provided by the plugin:\n\n```php\n$ml-\u003ereroute('/en/contact'); // OK\n$ml-\u003ereroute('/contact'); // OK =\u003e reroute to /xx/contact where xx is the current language\n```\n\nIn the situation where you'd like to quickly localize an existing project with unnamed routes, and would prefer to avoid\nhaving to rewrite every `$f3-\u003ereroute` into `$ml-\u003ereroute`, you can simply use the framework's `ONREROUTE` hook:\n\n```php\n$f3-\u003eset('ONREROUTE',function($url,$permanent) use($f3,$ml){\n  $f3-\u003eclear('ONREROUTE');\n  $ml-\u003ereroute($url,$permanent);\n});\n\n// then in your controller, existing reroutes will keep on working:\n$f3-\u003ereroute('/contact'); // OK =\u003e reroute to /xx/contact where xx is the current language\n```\n\n## Migration mode\n\nWhen translating an existing monolingual site, it is often interesting to redirect the old monolingual URIs to the new multilingual ones.\nThe plugin does it automatically for you if you set `MULTILANG.migrate` to `TRUE`.\n\nExample:\n\n* when migration mode is disabled, `/contact` throws a 404 error\n* when migration mode is enabled, `/contact` performs a 301 redirection to `/en/contact` (the primary language)\n\n## Strict mode\n\nWhen URLs are rewritten, an error is thrown if duplicate URLs are detected. E.g:\n\n```ini\n[MULTILANG.rules.en]\ncontact = /contact\nblog = /contact\n```\n\nThis behaviour is called \"strict mode\" and is enabled by default.\n\nYou can disable it by setting `MULTILANG.strict` to `FALSE`, in which case no error will be thrown.\n\n## Passthru mode\n\nWhen starting a new monolingual project, it may be interesting to keep the dependency to the Multilang plugin\nwhile not altering routes, just in case one day the project turns multilingual.\n\nThe `passthru` mode is meant for this situation. Set `MULTILANG.passthru` to `TRUE` to enable it.\n\nWhen the passthru mode is enabled, the plugin doesn't do much:\n\n* it reads the `MULTILANG` configuration variable\n* it sets `$f3-\u003eLANGUAGE` accordingly to the primary language\n* it leaves the framework routes untouched\n* it provides its usual public methods and properties\n\n## API\n\n```php\n$ml = Multilang::instance();\n```\n\n### current\n\n**Return the language detected for the current URL**\n\n```php\necho $ml-\u003ecurrent;// ja\n```\n\n### primary\n\n**Return the name of the primary language**\n\n```php\necho $ml-\u003eprimary;// en\n```\n\n### auto\n\n**TRUE if language has been auto-detected**\n\n```php\necho $ml-\u003eauto;//FALSE\n```\n\n### passthru\n\n**TRUE if passthru mode is enabled**\n\n```php\necho $ml-\u003epassthru;//FALSE\n```\n\n### locale()\n\n**Return the currently selected locale**\n\nNB: the value returned by this function can be different from what you're expecting\nif the locales configured in `MULTILANG.languages` are not present on your system.\n\n```php\necho $ml-\u003elocale();// en_GB.UTF-8\n```\n\n### displayLanguage( $iso )\n\n**Return the language name corresponding to the given ISO code**\n\nNB: the name is localized if the intl extension is installed, otherwise it is returned in English.\n\n```php\n// on a Danish route (with intl)\necho $ml-\u003edisplayLanguage('fr');// fransk\n\n// on a Russian route (with intl)\necho $ml-\u003edisplayLanguage('fr');// французский\n\n// on any route (without intl)\necho $ml-\u003edisplayLanguage('fr');// French\n```\n\n### displayCountry( $iso )\n\n**Return the country name corresponding to the given ISO code**\n\nNB: the name is localized if the intl extension is installed, otherwise it is returned in English.\n\n```php\n// on a Danish route (with intl)\necho $ml-\u003edisplayCountry('ru');// Rusland\n\n// on a Russian route (with intl)\necho $ml-\u003edisplayCountry('ru');// Россия\n\n// on any route (without intl)\necho $ml-\u003edisplayCountry('ru');// Russia\n```\n\n### display( $iso )\n\n**Alias for displayLanguage( $iso ) [deprecated]**\n\n### languages()\n\n**Return the list of available languages**\n\n```php\n$ml-\u003elanguages();// array('en','ja','es')\n```\n\n### locales()\n\n**Return the list of available locales (indexed by languages)**\n\n```php\n$ml-\u003elocales();\n/* array(\n    'en' =\u003e 'en-GB,en-US,en',\n    'ja' =\u003e 'ja-JP,ja',\n    'es' =\u003e 'es-ES,es'\n)*/\n```\n\n### aliases()\n\n**Return the list of all aliases**\n\n(even those not available for the current language)\n\n```php\n$ml-\u003ealiases();// array('terms','blog','captcha')\n```\n\n### isLocalized( $name, $lang=NULL )\n\n**Check if a route is localized in a given language (default=current)**\n\n(localized = not global nor excluded)\n\n```php\n$ml-\u003eisLocalized('terms');// TRUE (current language)\n$ml-\u003eisLocalized('terms','es');// TRUE\n$ml-\u003eisLocalized('blog','es');// FALSE (excluded for Spanish)\n$ml-\u003eisLocalized('captcha');// FALSE (global)\n$ml-\u003eisLocalized('foo');// FALSE (non-existent route)\n```\n\n### isGlobal( $name )\n\n**Check if a route is global**\n\n```php\n$ml-\u003eisGlobal('captcha');// TRUE\n```\n\n### alias( $name, $params=NULL, $lang=NULL )\n\n**Assemble url from alias name**\n\nThis function is a language-aware version of `$f3-\u003ealias()`.\n\n```php\necho $ml-\u003ealias('terms',NULL,'es');// /es/terminos-y-condiciones [local route]\necho $ml-\u003ealias('captcha');// /captcha [global route]\n```\n\n### reroute( $url=NULL, $permanent=FALSE )\n\n**Reroute to specified URI**\n\nThis function is a language-aware version of `$f3-\u003ereroute()`.\n\nUse it if you want an automatic language prefix on **unnamed** routes. Cf. [rerouting](#rerouting).\n\n```php\n$ml-\u003ereroute('/en/contact'); // OK\n$ml-\u003ereroute('/contact'); // OK =\u003e reroute to /xx/contact where xx is the current language\n```\n\n## Potential improvements\n\n* Allow domain level recognition (mydomain.jp/es, or jp/es.mydomain.com)\n* Hook on \"run\" event if an event system is implemented in F3 [core](https://github.com/bcosca/fatfree-core).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxfra35%2Ff3-multilang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxfra35%2Ff3-multilang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxfra35%2Ff3-multilang/lists"}