Closed samdark closed 6 years ago
Just an idea: http://i18next.com/
Let me share with my thoughts on possible architecture of it with future discussion participants:
1. Translation messages may be stored in ordinary JS file which could be loaded from the server side with the <script src="/js/en.ru.main.js" type="text/javascript"></script>
tag declaration. Proposed structure of the JS translation file name:
en.ru.main.js:
1. en - source language name (other variants are ru-ua, zh-hans-cn).
2. ru - target language name (other variants are pl-pl, de-at).
3. main - category name.
4. js - file extension.
Other possible variants:
1. zh-hans-cn.de-at.sign.up.js - translations from simplified han chinese to austrian german, sign.up category.
2. ru-ua.en-us.backEnd.js - translations from ukrainian russian to american english, backEnd category.
What benefits could give us standard files:
2. Files described above could be built with the console controller which would be supplied with the Yii distribution. Usage samples:
./yiic i18n build --outputAlias=@frontend/www/js
./yiic i18n build --outputAlias=@backend/public/js --targetLocale=ru_RU
./yiic i18n build --outputAlias=@backend/public/js --sourceLocale=uk_UA
3. Internal translation JS file structure:
// global yii variable is reserved
if (!'yii' in window) {
window.yii = {};
}
// i18n key is reserved for translations
if (!'i18n' in window.yii) {
window.yii.i18n = {};
}
// categories
if (!'managerCabinet' in window.yii.i18n) {
window.yii.i18n['managerCabinet'] = {};
}
// source language
if (!'uk-UA' in window.yii.i18n['managerCabinet']) {
window.yii.i18n['managerCabinet']['uk-UA'] = {};
}
// target language
if (!'ru-RU' in window.yii.i18n['managerCabinet']['uk-UA']) {
window.yii.i18n['managerCabinet']['uk-UA']['ru-RU'] = {};
}
// messages
window.yii.i18n['managerCabinet']['uk-UA']['ru-RU'] = {
'Вхід у кабінет': 'Вход в кабинет',
'Оплата послуг': 'Оплата услуг',
'Додавання покупця': 'Добавление покупателя',
'Додавання платіжної картки': 'Добавление платёжной карточки',
// ...
};
4. Client side translation utilities API:
console.log(window.yiit('managerCabinet', 'Вхід у кабінет', [])); // would log 'Вход в кабинет'
console.log(yiit('managerCabinet', 'Вхід у кабінет')); // same as above
console.log(yiit('managerCabinet', 'Видалення клієнта')); // would log 'Видалення клієнта', missing translation
This is just a cursory glance at the overall architecture. Please share your thoughts on this, maybe i missed something.
Should be as close as possible to the server side translation mechanism. First, because of the message extraction command and second - more importantly imo - because of usability.
Don't know i18next or what it provides, but it might indeed be a good idea not only to provide translation support on the client side, but the whole l18n app component (which currently does not do much more than translation, but I guess there are still things missing? Finding localized views, localizing dates and times, maybe currencies?).
I think clientside can reuse PHP file so translators will not do double job translating the same strings.
I think clientside can reuse PHP file so translators will not do double job translating the same strings.
Sure and JS files would be built from PHP translations by standard Yii console command.
More generally from the app's message source. Might as well be .po files or DB.
More generally from the app's message source. Might as well be .po files or DB.
:v: That's what i meant.
I wonder if this could be incorporated with the assets management. Converting a .po file to a .js file containing the same information in another format doesn't look that much different from converting .less to .css
Does this mean the robots won't be able to read the translated strings?
2013/5/15 bwoester notifications@github.com
I wonder if this could be incorporated with the assets management. Converting a .po file to a .js file containing the same information in another format doesn't look that much different from converting .less to .css
— Reply to this email directly or view it on GitHubhttps://github.com/yiisoft/yii2/issues/274#issuecomment-17931419 .
@lucianobaraglia yes. Some robots will not be able to read it. That is for data rendered with JavaScript.
@samdark just wanted to be sure we were talking about the same thing... Then I can't see the benefits being greater than the issues...
@lucianobaraglia you mean in rendering content using JS overall or in having translations in JS?
@samdark sorry, you're right... I meant rendering content using javascript...
@lucianobaraglia that's another question not really related to the issue.
Such feature make sense only if its implementation will use runtime *.js files requests and evaluations.
Working on prototype.
I don't think this should be at framework core part since it's not such a trivial task. There are a lot of existing libraries for this purpose (i18next, Jed, etc.), Yii should probably only give an ability for retrieving all (or only a part) of messages for further convertion to appropriate format.
If anyone still interested at implementing this as a part of framework, please consider the need of AMD support and so on.
Hi, there is any ready code?)
I think it may be:
Yii.t('message', params);
current language we can get from <html lang>
or <html data-lang>
When message use plural forms, it generate possible cases, eval with plural params, ex:
Yii.t('We have a tomato|We have {n} tomatoes');
will generate three cases (ru-RU.json from English source language):
{
'We have a tomato|We have {n} tomatoes': {
one: 'У нас есть помидор',
few: 'У нас есть {n} помидора',
many: 'У нас есть {n} помидоров'
}
}
Like this.
Also we can use https://github.com/twitter/twitter-cldr-js for ICU data in JavaScript. I realized some code for that (all points from above) and now will try to implement the types from ICU as in Yii. Later I'll create github repository for this.
Great. Post about your research here. It may help us when we'll get to this feature.
I put gist here: https://gist.github.com/stepanselyuk/d76e2d4a162fd1628ffc also some functions (from other files): https://gist.github.com/stepanselyuk/8903181f7e74b0a267dd
Other files:
TwitterCldr AssetBundle: https://gist.github.com/stepanselyuk/0809585a16fa68c15548
Console controller for collecting messages from *.js files, and generating Json-files with resources: https://gist.github.com/stepanselyuk/39f167b973648bbf1fd2
Component for generating Js-files for include to pages: https://gist.github.com/stepanselyuk/79a5532b1c946dd7c0f5
That component should be included to an AssetBundle as
(string) new JavaScriptMessages( [ 'language' => \Yii::$app->language ] ),
Algorithm:
alert( MyApp.tt( 'Необходимо выбрать {a} строку.', {'a': 'супер'} ) );
alert( MyApp.tt( 'There {n, plural, =0{are no cats} =1{is one cat} few{are few cats} other{are # cats}}', {n: 2} ) );
alert( MyApp.tt( 'У меня есть {n, number, currency} в кармане!', [{value: 2134, currency: 'RUB'}] ) );
There is examples for generated JSON (with messages and other info) and JS (for include to pages): https://gist.github.com/stepanselyuk/7686e79b248e158b1cdc
I use the Minify library, so it way to me to generate JS file and include it to unified archive with other JS-files.
./yii-system js-translations/collect
./yii-system js-translations/generate-json
I use special copy of ./yii file with changed commands path (also I use \Yii::$app->getThread() for save application thread in logs (see https://gist.github.com/stepanselyuk/fed9bb4e66d29f641482 )):
#!/usr/bin/env php
<?php
/**
* Yii console bootstrap file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
require_once 'settings.inc.php';
defined( 'YII_DEBUG' ) or define( 'YII_DEBUG', true );
// fcgi doesn't have STDIN and STDOUT defined by default
defined( 'STDIN' ) or define( 'STDIN', fopen( 'php://stdin', 'r' ) );
defined( 'STDOUT' ) or define( 'STDOUT', fopen( 'php://stdout', 'w' ) );
require( __DIR__ . '/vendor/autoload.php' );
require( __DIR__ . '/vendor/yiisoft/yii2/Yii.php' );
$config = require( __DIR__ . '/config/console.php' );
require( __DIR__ . '/components/yiiExt/ConsoleApplication.php' );
//$application = new yii\console\Application( $config );
$application = new app\components\yiiExt\ConsoleApplication( $config );
$application->controllerNamespace = 'app\commands\system';
$exitCode = $application->run();
exit( $exitCode );
plural number (integer, float, shortInteger, longInteger, currency, percent) datetime (short, medium, long, full, additional) date (short, medium, long, full, additional) time (short, medium, long, full, additional) timespan (relative time with possible force use specified unit like year, week, etc)
I hope it help to implement a version integrated to Yii2.
Any news on this?
It's planned for 2.1.x so we aren't going to work on it for 2.0 unless a pull request provided.
@samdark I might work on this one. What/how do you think this should work? Here is what I think:
The client side should be very simple, it shouldn't not know anything about string-templating, nor translations, nor anything else. The only thing it should know is what the developer wants to translate and where that text belongs to.
As I see it, there are to possible use-cases. The first one is a string that the developer would like to place somewhere in the DOM. The second one is a string that the developer would like to translate and then use in his own code.
Based on those two assumptions, a possible prototype could look like this:
yii.t("This is a %s", var_s_test, '#my_span');
yii.t("Another %s with multiple %s", var_s_test, var_s_args, function(err, t){
console.log(t);
});
The first line would:
#my_span
element in the DOMThe second line would:
err
status and the t
string. If there was an error, err
would be true
and the t
variable would contain the original text. If there was no error, err
will be false
and t
will contain the translated text.Now, the server part would be even easier. It could consist of a controller with a single method, which would then contain a single line (a call to Yii::t
with the received arguments).
@samdark Oh, I forgot one really important thing.
The yii.t
method should use something like memoize
, so further calls containing the exact same string and arguments will not result in another ajax call.
I'm not sure any AJAX calls are required. message command could generate json file containing translations and the file could be used then. I was thinking about generating these files from PHP ones and using something like https://github.com/twitter/twitter-cldr-js.
I added that functionality to one of my bigger projects and you need a couple things more:
To modify the CLI MessageController
to also parse .js files (and possible others).
IMO also a category. In the backend this allows you to split translations over various sources. I have a number of modules and every module takes care of its own translations. IMO we should keep things as consistent as possible between front- and backend as well, which means that you should also use the category in the javascript version.
Having a category also allows something else: To return the entire block of strings and their translation at once (perhaps on request only?) so that we don't even need an ajax call per translation. Don't forget that you'll have to work with Deferred
's to correctly update whatever you need, so it might save a lot of headaches to fetch the entire translated block at once.
On top of that you probably also want to translate tags that aren't always javascript. I implemented a support for a "data-i18n"-attribute for that purpose (which will replace the html content of whatever tag this is used in. I also have support for a "data-i18n-target", for when you want to replace one of the tags attributes instead of the html content.
Obviously there are a ton of ways to tackle this problem, just wanted to chip in :)
@samdark @Blizzke So your idea is to actually parse the JS files and search for calls to yii.t
(or whatever the method's name is)? If so, I'm not really sure how would that work with compressed JS files (see, var x = yii.t
).
Imo it should match the existing backend system as closely as possible, so yes. My yii.t implementation also allows for the same {token} syntax etc.
As for compressed js, that is (imo) a deployment step and the message command should run against the regular files. I doubt you'll be writing your original source compressed, no?
I don't think one will ever run it against compressed files.
Ok, makes sense, indeed. Let's see if I can get some spare time and work on this one.
If needed I can help out or provide some of the things I already added. Let me know
@Blizzke Yes! Totally! The more, the better! Actually, as I'm currently kind of short of time you can take the issue if you want to. It sounds like you already got something working :)
My way (which described above, in August 2014) working good in my application.
I don't mind doing it but perhaps we should iron out first what features people expects of the yii.t function? I doubt my demands are what everyone is looking for.
What do you guys think about:
MessageController modification to parse *.js files
Probably yes. One thing I can say in advance is that it's not reliable to do it via regexp so one should take care of implementing tokenizer for JavaScript like I did for PHP to fix tons of regexp issues.
"format" for storing in a json file (on top of the existing ones)
Yes.
yii.t functionality that allows you to work with {token}-parts, perhaps some formatting? (do we do categories like the backend or not, imo we should)
Yes. I'd expect to be able to reuse my PHP messages in JS. So full blown ICU (I've provided a link to twitter's one).
extract html data attributes for html translation (data-i18n, ...)?
Probably data-t? What are these for? Any examples?
Automatic Loading translations on client-side (assetbundle, ajax?) Or have the user loading via whatever method and registering it with the translation system (yii.translations('category', object))?
Check how it's done for JUI datapicker currently.
Take a look at globalize. It has a similar message formatting functionality.
Indeed. And it's now adopted by jQuery foundation so it's definitely best possible choice for now.
Is anyone still working on this?
Have same question
I actually implemented most of this for a project, but since there was never any activity on this ticket I haven't bothered with making it releasable.
I can release the modified MessageController
(which should work pretty okay as is), but the related javascript is coupled with that project. If I were to release that it would require implementing a couple things yourself. Let me know if there is any interest. I'm not going to continue working on it but I can setup a quick repository for it
@Blizzke if you could release it, I'm interested in the code.
@Blizzke release please, im going to work on it too
Yes, that could be interesting to check. If it will be released as standalone extension, it could be referenced from a guide.
And how it'll be compressed via yii asset
command?
As I said, I am not going to release this as a fully working extension, I don't have the time nor the enthusiasm for that. I'll setup a repository with all the relevant files and some high level instructions on how to get started with it. Just give me a day or 2 to see if I can scrape together a bit of time.
If put the "repository" here It provides a controller to parse javascript (and HTML) and deliver the found messages to the regular Message functionality so you can have them written in files / added to the database like regular messages.
The way I developed it is that it has a jQuery plugin that calls an endpoint in your application (example included) to retrieve the messages as json
.
When I developed this I decided I wanted the messages of back- and frontent combined and not just part of the javascript.
This is offered without any guarantees and probably won't work out of the box. The message parsing code and the translation function do work and were tested back then. The part where you fetch them from the backend was just thrown in. The original code worked with a bus that did the processing and did not have any ajax requests.
I will try to answer any issues created for the project but please understand that it has been a while for me :) If it is decided you want to turn the repository into an actual yii2 plugin, I will be accepting pull requests, doing releases and adding it to packagist. Otherwise I'd appreciate a shout-out, as requested in the README
I don't know, what did you do. But I have an idea. We can use as you said "yii.t(category,message)", if we import the all translate data! My idea, we don't need all translate data, developer can use different category for javascript. for example;
in layout:
Yii::$app->getI18n()->register($this,'javascript',['en-EN','ru-RU']);
Yii::$app->getI18n()->register($this,'alert',['en-EN','tr-TR']);
// maybe there could be have big data for each language. when the language changes by dynamic, we can get the data with ajax? Just an idea.
than it sends to javascript like that data:
window.yiiTranslateSource = {
javascript: [
{
'bla bla bla': {
'en-EN': 'bila bila bila',
'ru-RU': 'bila bila bila2'
}
}
],
alert: [
{
'Success': {
'en-EN': 'Success 2',
'tr-TR': 'Başarılı'
}
}
]
};
and we can use in javascript;
yii.t('javascript','Success');
/* we can change language by dynamic */
yii.changeLanguage('en-EN');
What do you think? @samdark
I was expecting the solution would be to:
<?= Yii::t() ?>
to get translations<?= $this->renderFile(__DIR__.'/_script.js.php',['foo' => 'bar']) ?>
I have this for a CSS file. The code would be printed on the page source, but it would include all the translated messages. However the above solutions seem better since the source would remain without the clatter.
Another solution would be to add the translated messages in hidden fields
e.g. <input type="hidden" id="contact-thanks" value="<?= Yii::t('contact','Thanks!') ?>">
on the page and have your script use the messages
e.g. $('#contact .msg-title').html($('#contact-thanks').val());
I think translations at clientside are quite a common feature. I think it will be useful if Yii will provide it out of the box.