Coder Social home page Coder Social logo

xfra35 / f3-multilang Goto Github PK

View Code? Open in Web Editor NEW
48.0 13.0 13.0 96 KB

Create multilingual apps with this localization plugin for the PHP Fat-Free Framework

License: GNU General Public License v3.0

PHP 92.49% CSS 4.38% HTML 3.14%
fat-free-framework php multilang multilingual i18n localization

f3-multilang's Introduction

Multilang

…for F3 polyglots!

This plugin for Fat-Free Framework provides a URL-friendly mean to localize your web site/app.

Demo: here.

Basic usage

Step 1:

Declare the languages of your app in the MULTILANG.languages variable:

$f3->set('MULTILANG.languages',array(
  'en' => 'en-GB,en-US,en',
  'ja' => 'ja-JP,ja',
  'es' => 'es-ES,es'
));

The same declaration can be achieved in a configuration file using the following syntax:

[MULTILANG.languages]
en = en-GB, en-US, en
ja = ja-JP, ja
es = es-ES, es

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.

NB2: The first defined language is considered as the primary language, which means it is set as 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.

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.

Step 2:

Start the plugin by instantiating the class, just before the call to $f3->run:

$f3->config('config.ini');
Multilang::instance();
$f3->run();

That's it!

Now every existing URL has been duplicated into as many languages you've declared, using identifiers as prefixes:

         => /en/contact
/contact => /ja/contact
         => /es/contact
  • How about the original URLs? => They have been removed.
  • How about the original root /? => It autodetects the browser language. See below

Root URL

By default, the root URL autodetects the browser language and performs a redirection to its root page.

In our example, a Spanish user would be redirected to /es while a Russian user would be redirected to /en (fallback to the primary language).

You can override this default behaviour by setting the MULTILANG.root to a custom handler:

[MULTILANG]
root = App\MyRoot

Use case: display a splash page with the list of available languages.

Advanced usage

(requires the usage of route aliases)

Rewrite URLs

Each translated URL consists of a language identifier followed by the original URL:

/es + /terms-and-conditions = /es/terms-and-conditions

You can customize the second part by setting the MULTILANG.rules variable. For example, in order to translate the Spanish URL above, you could write (assuming the route is named terms):

$f3->set('MULTILANG.rules',array(
  'es' => array(
    'terms' => '/terminos-y-condiciones'
  )
));

The same declaration can be achieved in a configuration file using the following syntax:

[MULTILANG.rules.es]
terms = /terminos-y-condiciones

Exclude a language from a route

When translating a website, you may need to perform a progressive translation, route by route. It could also occur that some parts won't be localized at all (for example a blog).

For this purpose, you can remove a route for a specific language by setting it to FALSE:

[MULTILANG.rules.es]
blog = FALSE

A request to /es/blog will return a 404 error.

Global routes

Some routes have to stay language-independant, for example a captcha doesn't have to be localized. Also back offices often happen to be monolingual.

Those global routes are not rewritten: they keep their original URL. They are defined using the MULTILANG.global variable:

[MULTILANG]
global = captcha
;could also be an array:
global = alias1, alias2, alias3, /admin, /foo/bar

Each entry can be a route alias or a URI prefix.

NB: on a global route, the language is auto-detected by default. So in the case of a monolingual back office, you may need to force the language at the controller level.

Rerouting

If you're using named routes...

$f3->reroute will work as expected, that is to say, it will reroute to the current language URL of the provided named route. E.g:

$f3->reroute('@contact'); // OK => reroute to /xx/contact where xx is the current language

If you're using unnamed routes...

In that case, you have to provide a language-prefixed URL to $f3->reroute:

$f3->reroute('/en/contact'); // OK
$f3->reroute('/contact'); // Error => 404 Not Found

If you'd prefer to give the short URL to the framework and have it automatically prefix the URL with the current language, use the $ml->reroute method provided by the plugin:

$ml->reroute('/en/contact'); // OK
$ml->reroute('/contact'); // OK => reroute to /xx/contact where xx is the current language

In the situation where you'd like to quickly localize an existing project with unnamed routes, and would prefer to avoid having to rewrite every $f3->reroute into $ml->reroute, you can simply use the framework's ONREROUTE hook:

$f3->set('ONREROUTE',function($url,$permanent) use($f3,$ml){
  $f3->clear('ONREROUTE');
  $ml->reroute($url,$permanent);
});

// then in your controller, existing reroutes will keep on working:
$f3->reroute('/contact'); // OK => reroute to /xx/contact where xx is the current language

Migration mode

When translating an existing monolingual site, it is often interesting to redirect the old monolingual URIs to the new multilingual ones. The plugin does it automatically for you if you set MULTILANG.migrate to TRUE.

Example:

  • when migration mode is disabled, /contact throws a 404 error
  • when migration mode is enabled, /contact performs a 301 redirection to /en/contact (the primary language)

Strict mode

When URLs are rewritten, an error is thrown if duplicate URLs are detected. E.g:

[MULTILANG.rules.en]
contact = /contact
blog = /contact

This behaviour is called "strict mode" and is enabled by default.

You can disable it by setting MULTILANG.strict to FALSE, in which case no error will be thrown.

Passthru mode

When starting a new monolingual project, it may be interesting to keep the dependency to the Multilang plugin while not altering routes, just in case one day the project turns multilingual.

The passthru mode is meant for this situation. Set MULTILANG.passthru to TRUE to enable it.

When the passthru mode is enabled, the plugin doesn't do much:

  • it reads the MULTILANG configuration variable
  • it sets $f3->LANGUAGE accordingly to the primary language
  • it leaves the framework routes untouched
  • it provides its usual public methods and properties

API

$ml = Multilang::instance();

current

Return the language detected for the current URL

echo $ml->current;// ja

primary

Return the name of the primary language

echo $ml->primary;// en

auto

TRUE if language has been auto-detected

echo $ml->auto;//FALSE

passthru

TRUE if passthru mode is enabled

echo $ml->passthru;//FALSE

locale()

Return the currently selected locale

NB: the value returned by this function can be different from what you're expecting if the locales configured in MULTILANG.languages are not present on your system.

echo $ml->locale();// en_GB.UTF-8

displayLanguage( $iso )

Return the language name corresponding to the given ISO code

NB: the name is localized if the intl extension is installed, otherwise it is returned in English.

// on a Danish route (with intl)
echo $ml->displayLanguage('fr');// fransk

// on a Russian route (with intl)
echo $ml->displayLanguage('fr');// французский

// on any route (without intl)
echo $ml->displayLanguage('fr');// French

displayCountry( $iso )

Return the country name corresponding to the given ISO code

NB: the name is localized if the intl extension is installed, otherwise it is returned in English.

// on a Danish route (with intl)
echo $ml->displayCountry('ru');// Rusland

// on a Russian route (with intl)
echo $ml->displayCountry('ru');// Россия

// on any route (without intl)
echo $ml->displayCountry('ru');// Russia

display( $iso )

Alias for displayLanguage( $iso ) [deprecated]

languages()

Return the list of available languages

$ml->languages();// array('en','ja','es')

locales()

Return the list of available locales (indexed by languages)

$ml->locales();
/* array(
    'en' => 'en-GB,en-US,en',
    'ja' => 'ja-JP,ja',
    'es' => 'es-ES,es'
)*/

aliases()

Return the list of all aliases

(even those not available for the current language)

$ml->aliases();// array('terms','blog','captcha')

isLocalized( $name, $lang=NULL )

Check if a route is localized in a given language (default=current)

(localized = not global nor excluded)

$ml->isLocalized('terms');// TRUE (current language)
$ml->isLocalized('terms','es');// TRUE
$ml->isLocalized('blog','es');// FALSE (excluded for Spanish)
$ml->isLocalized('captcha');// FALSE (global)
$ml->isLocalized('foo');// FALSE (non-existent route)

isGlobal( $name )

Check if a route is global

$ml->isGlobal('captcha');// TRUE

alias( $name, $params=NULL, $lang=NULL )

Assemble url from alias name

This function is a language-aware version of $f3->alias().

echo $ml->alias('terms',NULL,'es');// /es/terminos-y-condiciones [local route]
echo $ml->alias('captcha');// /captcha [global route]

reroute( $url=NULL, $permanent=FALSE )

Reroute to specified URI

This function is a language-aware version of $f3->reroute().

Use it if you want an automatic language prefix on unnamed routes. Cf. rerouting.

$ml->reroute('/en/contact'); // OK
$ml->reroute('/contact'); // OK => reroute to /xx/contact where xx is the current language

Potential improvements

  • Allow domain level recognition (mydomain.jp/es, or jp/es.mydomain.com)
  • Hook on "run" event if an event system is implemented in F3 core.

f3-multilang's People

Contributors

xfra35 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

f3-multilang's Issues

How can I add the region ?

Big fan of your work guys, thank you.
My issue, I was wondering if I could do it in the f3 "way" without a custom route controller
I wish to have an optional selected region added in the url
For example Is possible to implement ?
/fr/* as /fr-CH/* or /fr-FR/*
where $CH or $FR could be a default $WW (for world wide)

Lang selector as not a part of the URL

Hi, i hope that the url format is not /xx/user but /user?lang=xx.
and then, the ?lang=xx is can be removed as it was saved into cookie. So user can acces other page on the same Language.

Eg. /about without need to specify ?lang=xx again after accessing /user?lang=id (User no need to access this way: /about?lang=id.

Thankyou!!

alias with params

Hi,
I've setup the following route:

GET|POST @userrequest : /profile/@action=UserControl->@action

Now, i'm trying to pass the @action param (edit) within the alias function inside my view:

<a href="{{ $ml->alias('userrequest','edit',$ml->current) }}">Edit profile</a>

The current output is:

<a href="/profile/@action">Edit profile</a>

I was expecting the result to be:

<a href="/profile/edit">Edit profile</a>

my single route with @action should handle UserControl->edit & update & cancel & emailupdate.
Is the Alias/param var used for this purpose? Is it a bug? Can this param be localized (i.e French = edtion & mise-a-jour & supprimer & mise-a-jour-email? What I'm missing?

Migrate issue with latest F3 changes

Hey there xfra35,

I've been using this library with the migrate option enabled and recently the redirects began being made to:

/lang/somepath/@someparam

When it previously would redirect to:

/lang/somepath/theactualparam

That is, it redirects to a route without replacing the placeholder with the actual parameter.
Please see the screenshot from devtools where you can see the "Location" header redirecting to a route without substituting the route parameter with the actual variable.

f3multilang

I'm guessing this is happening since this change. At least if I use a base.php file before that I get the correct behavior.

Cheers

Detecting language not working properly

Hi,

On global routes, multilang detects the browser language and loads it.
The problem is that it is not working properly I think.

I have my browser options setup to use Spanish and other browser on english.
Both appear with the english options on you demo:
http://ml.aesx.fr/en
(sidebar information).
It looks like it consider the installation language and not the language set on options on the browser.

Why not use the fallback language on fatfree when the route is global?

Reroute bug?

Hello mate,
does rerouting function from f3 conflicts with multilang?

I got this line of code:

$f3->reroute('/'.$route);

This reroutes to app-path.com/route insted of app-path.com/en/route.

Should I use?

$f3->reroute('/'.$m->current().'/'.$route);

It was nice if this was automatically ;)

Thanks for your time,
cheers.

Dynamically change the language

Hey,
I've seen your great demo of the plug-in, but I'm wondering how you dynamically build links to change the language while staying on the same page (the flags on top of the page).
What I mean is if I'm on the /en/contact/ page, how can I make a /fr/contact link on the template ?
Thanks in advance for your answer =)

Generate equivalent URL in other languages when tokens are present

Let's say we want the language menu to take you to the same page in the other languages, no just the root of the language.

Example:

You're on /en/games/first-person and this route is defined as follows:

GET @gamessection: /games/@section = Controller\Games->section

The corresponding rule for Spanish looks like this:

[MULTILANG.rules.es]
gamessection = /juegos/@section

The language menu looks like this:

<a title="GamesSite - English" href="{{@ml->alias(@ALIAS, null, 'en')}}">English</a>
<a title="GamesSite - Español" href="{{@ml->alias(@ALIAS, null, 'es')}}">Español</a>

The URL for Spanish will be rendered as /es/juegos/first-person when what we want is /es/juegos/primera-persona. How do we localise the @section parameter as well as the fixed part of the URL?

Default language hidden from url

Hi, how are you?

Is there an option to hide the default language from the url?

Imagine a portuguese app, but also has english translation. 85% of the users are portuguese and 15% aren't. Would make sense to hide /pt since 85% users are portuguese and seeing that in the URL will not matter. Or in the case that I'm building an app that currently supports one language only (but in a near future could turn multilang), so I use Multilang Class thinking about the possibility of having a new language added in the future. If this is the case, all my urls will show /pt although my website only has Portuguese at the moment.

Example urls:

https://my-app.com/ (Portuguese)
https://my-app.com/users (Portuguese)
https://my-app.com/books (Portuguese)
https://my-app.com/books/1 (Portuguese)

https://my-app.com/en (English)
https://my-app.com/en/users (English)
https://my-app.com/en/books (English)
https://my-app.com/en/books/1 (English)

Its probably an aesthetic question, but would be nice to see this implemented. (Since I've faced this problem already).

Cheers.

Usage with f3-middleware

Seems both rewrite the $f3->ROUTES and can't figure which one to load and run first, not mention i'm also using f3-events, my project starts to turn into a mess.
Seems I have to use the full route with language for middleware to detect..
\Middleware::instance()->before('GET /en-GB/user'..
Moving the \Multilang initialization before/after/between the middleware on/run has no effect.

I can't seem to make multilang to work with the backend of fabulog

can you give some hints on how to write the mappings in the config.ini when f3 routes are like this:

$f3->route('GET /admin/@module/create', 'Controller\Backend->getSingle');
$f3->route('POST /admin/@module/create', 'Controller\Backend->post');
$f3->route('GET /admin/@module/edit/@id', 'Controller\Backend->getSingle');
$f3->route('POST /admin/@module/edit/@id', 'Controller\Backend->post');

Can't add both /admin and /login to either global ignore list or exclusion for a lang

Hi I am using fabulog and I can't seem to add both /admin and /login to the global ignore list, can you tell me how do I do that? Below's my config.ini:

menu = about, press, news, terms, privacy, contact, login, admin

[maps]
@about : /about = Controller\About
@press : /press = Controller\Press
@news : /news = Controller\News
@terms : /terms = Controller\Terms
@privacy : /privacy = Controller\Privacy
@contact_add : /contact/add = Controller\Contact->addContact
@contact_alert : /contact/alert = Controller\Contact->alert
@image_download : /download/@image = Controller\Press->imageDownload
@login : /login = Controller\Auth->login
@admin : /admin = Controller\Dashboard->main

[MULTILANG]
global = terms, privacy, /admin/post, contact_add, contact_alert, image_download, login, admin

[MULTILANG.languages]
en = en-GB, en-US, en
tw = zh-TW, tw

[MULTILANG.rules.en]
admin_create = /admin/@module/create
contact_add = /contact/add
contact_alert = /contact/alert
image_download = /download/@image
admin = false
login = false

[MULTILANG.rules.tw]
admin_create = /admin/@module/create
contact_add = /contact/add
contact_alert = /contact/alert
image_download = /download/@image

OnReroute doesn't work with newest F3 update

I use f3-Multilang on most public sites and I exclude a few sites that are for logged in users like admins.

after the last update, I have the following problem. I use an ONREROUTE rule to ml reroute if needed.

$f3->set( 'ONREROUTE', function ($url, $permanent) use ($f3, $ml) {
        if (!$ml->isGlobal( $url ) && !in_array( $url, $f3->get( "excludedRedirects" ) )) {
            $f3->clear( 'ONREROUTE' );
            $ml->reroute( $url, $permanent );
        }
        else {
            return false;
        }
    } );

this way I can still use f3 reroute without sending the language.

After the latest F3 update the following problem happens:
whenever a reroute happens to a dynamic URL the URL in ONREROUTE doesn't have the "REAL" URL but the URL with the params.

so let's say I got the URL
/blog/@article
so normally if i would reroute to (or someone links links to)
/blog/1 it should be sent to /en/blog/1
but suddenly I get errors. I traced it down to f3-Multilang being active and ONREROUTE.
the $url Variable in the OnReroute Function will suddenly say:
/blog/@article
instead of /blog/1

Manually set language is ignored?

I'm trying to use F3 in an existing project, writing new modules with F3 and slowly migrating old ones.
We have a language switch on the website which sets the language in session, so I read that and pass it to F3 after reading my config.ini

// set language to the one user selected
if ($_SESSION['sprache'] == 'de') {
    $f3->set('LANGUAGE', 'de');
}
else {
    $f3->set('LANGUAGE', 'en');
}

I started using multilang today, so I might be wrong, but it seems like my manually set language is ignored by multilang and it always detects my german browser language.

Do you have any ideas what could have gone wrong in this case?

Kind regards
Frank

Redirect old url

Hi,
I am addind this plugin to an existing fatfree installation.
How can I redirect the old urls to the default language?

Example

domain.com/old/route/app should be redirected to
domain.com/en/old/route/app

but this should not be redirected because it works by default:

domain.com/es/old/route/app

Thanks

Skip $global routes when $migrate is false.

Hi, I assumed that setting $global as an array of aliases ['adm_login','adm_dash'] would skip the Multilang rewrite but no, I get 404 for /admlogin and 200 for /en/admlogin.
How would I accomplish this without manually rewritting routes ?
Thank you for your time.

Can't handle two different aliases with same route endpoint

Hey there,

Just was messing with this today and I had 2 routes that pointed to the same endpoint, but had different aliases. Multilang was not a fan haha.

GET @companies: /companies = Company->indexAction ; routes to /en/companies
GET @companies_create: /companies/create = Company->createAction; routes to /en/companies/create
POST @companies_handle_create: /companies/create = Company->handleCreateAction; ERROR routes to /companies/create

Without doing a ton of digging, I'm better the problem lies in the rewrite() method in Multilang. Don't know if this is a feature or a bug, please advise 😄

Ignoring folder on global

How can I ignore a whole folder on the global config?

I would like to keep all the routes under /api/* without using the language reroute.

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.