I have yii language localization turned ON, so I use in config 'language'=>'en',
and write things like Yii::t('layouts_main','Home') and store translations of Home in php file.
But apart of this, I have tables, and there are data inside in different languages.
For example I have a list of countries which must be stored in mysql table in different languages.
Table structure is very simple: id, name_en,name_de,name_es etc...
I did it that way so if language change those 2 letters must be controlling from which sell to read the name.
In my controller I get my data from table to array
$tableCountry = Country::model()->findAll();
Then I'm making such variable which will containt "name+" language variable which is 'en' in config
$name_lang ="name_".Yii::app()->Language;
So now I made variable $name_lang which contains name_en
Then I choose the right sells using $name_lang variable
$list=CHtml::listData($modelCountry,'id',$name_lang);
Using $name_lang only name_en data will go to List. So later I can switch settings of language in config file to "de" and only name_de data will fgo to List.
So everything is fine here.
But what If I have complex table which contains country_name_en, city_name_en,region_name_en etc...
To put this data into list I have to make 3 or more variables
$country_name_lang ="country_name_".Yii::app()->Language;
$city_name_lang ="city_name_".Yii::app()->Language;
$region_name_lang ="region_name_".Yii::app()->Language;
So I wonder is there any other better way to do such things ?
Option 1: You can define in the model another variable called country_name (without any suffix). You can do this just in the model, you do not have to create a field in the db too. No need for validation or anything for this variable.
Then in your after find method for the model you can do
public function afterFind()
{
$this->country_name = $this->{"country_name_".Yii::app()->Language};
}
Feel free to add everything here.
Now everywhere you should be able to use $model->country_name without any care what the language is.
Option 2 would be to use magic methods. you can read more from them here: http://www.php.net/manual/en/language.oop5.magic.php
Define for your model such a method:
public function __call($name, $arguments)
{
if(isset($this->{$name. "_".Yii::app()->Language}))
return $this->{$name. "_".Yii::app()->Language}
elseif(isset($this->$name))
return $this->{$name}
else
throw new Exception(xxx, 'We couldn\'t find the variable');
}
Now you should be able to use $model->country_name() and you would again get the variable in the proper language. You can use also $model->id() and you would get the proper id too as it has a fallback if there is no id_en field.
Related
If possible..how would this be done? Laravel 5.5
route('section.category.subcategory',$subcategory->id)
must output (routes/web.php has the get:: set as this too)
/section/{parent_slug}/{subcategory_slug}
I could easily do
route('section.category.subcategory',[
'subcategory_slug' => $subcategory->slug,
'parent_slug'=>$parent->slug
]
);
but I'm trying to avoid having to declare those things all the time.
I thought getRouteKeyName in model would be first place to go to, but it binds to only one variable as far as I could find.
RouteHandler isn't the place to do anything either because it reads the url, not outputs it right?
I'm assuming in some file that I don't know about I will have to set this sort of logic.
if(requested_route is section.category.subcategory)){
// get the parent_id of ID provided,
// get parent's slug
// build the url.
}
Even better, I think I could do a left join when pulling the list of subcategories, so I have $subcategory->parent_slug instead of going for $parent->slug. This way
route('section.category.subcategory',[$subcategory])
has all the variables it needs to work with.
I think for this helper would be a good choice so you could create url like this:
route('section.category.subcategory',build_category_parameters($subcategory))
and you can create function like this:
function build_category_parameters($subcategory)
{
// here any logic that is needed
// here return array something like this
return [ 'subcategory_slug' => $subcategory->slug,
'parent_slug'=> $subcategory->parent->slug
];
}
If you don't have helper file already you can create one using this way Laravel - require php script in a service provider
I'm using ActiveForm with Yii2 and by default it seems to generate default id's for fields if you don't set one, in the format of:
{action-name}-{field-name}
So for example if I had a field with the name of foo_bar used in an action of actionSettings then the id of this field would be generated as:
settings-foo_bar
I would prefer this to just be foo_bar.
Is this possible to change on a form by form basis?
Based on the answer provided by #Bizley I was investigating how the method calculated the name and found out there is another way to achieve this as well.
You can simply override the formName method of your respective model to return a blank value, such as:
public function formName() {
return '';
}
Whilst this has less overheads as you don't need to create a new class, it will also affect other things within your form such as the field names and also should not be used for forms which contain multiple different models as explained here.
Lastly, because this question was about changing how Yii formats the id, #Bizleys answer is the correct one; my solution is just another option of possibly achieving it another way.
ActiveField id is by default created based on the form's model name and field's name.
If you want to change it for the whole form override the method that does it:
protected function getInputId()
{
return $this->_inputId ?: Html::getInputId($this->model, $this->attribute);
}
and use this modified class in your form.
I am doing a multi language website for which i use a route:group with a prefix in my route.php.
Now, i am trying to grab the variables of language to know which language i am viewing the website, and then use it to query database.
I have found a problem, how to get more than 1 variables from the url?
Route::get('/{vedi}/{fuffa}', function($vedi, $fuffa)
{
$arrays=array(['campo'=>$vedi,
'lingua'=>$fuffa]);
return View::make('abc')->with('data',$arrays);
});
then in my 'abc' view i would ask for a
{{$data->campo}}
but he tell me it's not a n object.
1) how can i do?
2)is this a corret approach to build a website multilanguage?
For 1)
You are trying to access your data via a $data object that does not exist. You try to access your values like if $data was an object (by using ->campo).
What actually happens that you have an array. In your case, you can access your variables like this:
{{ $data['campo'] }}
What you also can do:
$arrays=array('campo'=>$vedi,
'lingua'=>$fuffa);
return View::make('abc')->with($arrays);
});
And then, in your view, you will have the variables directly available, without being in one $data array:
{{ $campo }}
For 2)
You can make use of laravels Localization functionality. Basically, you make one or many files for each language where you have the same structure of messages. In your views, you acceess the same messages, but based on the viewers chosen language the correct message is shown.
See Laravel Localization here
Suppose I have 2 identical table having same structure(Call it 'tableA' & 'tableB').
I want to save certain data on table 'A' and certain data on table 'B'.
NOW I want to use the same MODEL for both the table.
I want to change the table linked with the Model(say 'ModelM') to change dynamically based on condition at the controller.
e.g.
In controller:- //sample code
function saveProduct(){
$this->loadModel('ModelM');
if(condition){
$this->ModelM->useTable = 'A';
}else{
$this->ModelM->useTable = 'B';
}
$this->ModelM->save($this->data);
}
ADDITION ON 14th JANUARY 2011
Following is the copy/paste of code I am working on:
function experiment(){
$tableName = 'temp_table'.'1234';
mysql_query('CREATE TABLE '.$tableName.' LIKE temp_home_masters');
$sql = $this->createInsertQuery($new_arr,$tableName);
$status = mysql_query($sql);
if($status){
echo "saved successfully";
}else{
echo "error";
}
$this->NewHomeMaster->setSource($tableName);//NewHomeMaster was previously attached to a different table , here I want to change the tableName the model linked with dynamically.Model 'NewHomeMaster' already exists and uses a table ...Here I am willing to link this model to the newly created tempory table.//
$home_details=$this->paginate('NewHomeMaster',array($new_conditions));
mysql_query('DROP table '.$tableName);
}
UNFORTUNATELY THIS DOES NOT WORK...
I originally set out to figure a solution to your problem, but the more I think about it, I believe your logic is flawed.
I can see how the fact that the tables are similar or identical can lead you to the decision of using a single model to interact with both tables. However, when you look at a what a model is supposed to be (In CakePHP basically an interface to a table), it doesn't make sense to switch back and forth.
The CakePHP docs explain models like this:
In object-oriented programming a data model is an object that represents a "thing", like a car, a person, or a house.
In your example, you really have two separate "things" that look exactly the same. Therefore, they should have their own models.
If your models are really going to have the exact same methods, then "the CakePHP Way" would be to define a custom Behavior that encapsulates your shared methods. Then attach the behavior to both models.
Then you can load the model you need in the Controller condition:
private $DynamicModel;
public function saveProduct() {
if (condition) {
App::import('Model', 'ModelZ');
$this->DynamicModel = new ModelZ;
} else {
App::import('Model', 'ModelY');
$this->DynamicModel = new ModelY;
}
$this->DynamicModel->save($this->data);
}
Do this on your controllers before filter function:
$this->CakePHPModelName->setSource('table_name');
This will use different table.
Source: http://www.mainelydesign.com/blog/view/changing-cakephps-model-usetable-on-fly
The situation is tricky, as Stephen describes it, because your approach somewhat violates the MVC conventions.
However, if you're willing to go to the dark side of custom hacks, you could consider creating you own customized Datasource in CakePHP that handles this kind of logic for you. An option is to extend a given Datasource (presumably the MySQL one) with you own custom logic that aims to perform some prelimary filtering/conditioning before interacting with the database. Not that clean because the logic is placed in the wrong scope, but could work. Have a look here for a start: http://book.cakephp.org/view/1075/DataSources
Alternatively, you could create two different models and make them share the same logic using a behavior. This kinda limits you to take the choice of model earlier in the flow (and thus doesn't only affect the location of data storage), but might be a possibility.
I'm building a website that needs to support different language translations. I have strings in PHP, JavaScript and Smarty Template files that need to translated.
I want to use something like PHP's gettext() function and have a single language file for each locale.
This is easy when the translatable strings are in the PHP files but I also have text in the Smarty Templates and JavaScript files that also need to be translated.
I really want one single file that holds all the translatable strings.
Update:
I'm wanting to use gettext() but the real issue I have is that it only works with PHP and not JavaScript as the JavaScript is client-side.
You can store your strings in CSV files like so:
"Welcome","Welcome"
"Total Price","Total Price"
Make one file for each language. The first value is the key you'll be using to fetch the string and the second the translation. You can then build a class with functions that load the appropriate file, stores the variables in an array and translate text on request. For example:
protected function loadTranslation($locale) {
$this->_data=array();
$localeFilePath="locale/" . $locale . "/whatever.csv";
if(file_exists($localeFilePath)){
$localeFileHandle = fopen($localeFilePath, "r");
while (($line = fgetcsv($localeFileHandle, 1000)) !== FALSE) {
$this->_data[$line[0]]=$line[1];
}
fclose($localeFileHandle);
}
return $this;
}
for loading the values and
public function __($label) {
$output=$this->_data[$label];
if(empty($output)) $output=$label;
return $output;
}
for translating.
You can then, for example, use the following to get the proper string:
echo $this->__('Total Price')
The advantage of using CSVs is that you don't need to bother with the database and of course they can be edited via Excel which will come in real handy when you have to tell some data-entry drone to do a translation.
Furthermore you can write a small script to find strings used by the __() function in your code and then write the appropriate strings in the CSV files so you don't have to waste your time appending strings to each and every CSV file for every string you use in your code.
As for Smarty templates and Javascript files, you'll also have to take your translated strings from PHP. Smarty values come from PHP so use your translator before you assign a value to Smarty. As for javascript, it'll have to be dynamically created by PHP so you can set the translated strings in the appropriate places.
All the web apps I've built support 3 languages, and are designed for more. I store all the texts in the database, and call them Constants.
create table constants (
id integer not null,
fk_constant_group integer not null
)
create table constants_group (
id integer not null,
name varchar(32)
)
create table languages (
id integer not null,
name varchar(32),
accronym varchar(3),
/*other fields if necessary*/
)
create table constants_value (
id integer not null,
fk_constant integer,
fk_language integer,
value text
)
The constant group is used to group them by module/page. If you are going to display a page you are going to need all the constants, so you use a single query to get all the data in the needed language for one page. An index on the group name would be appropriate.
In PHP I wrote something like this:
public static function SetNames()
{
$info=debug_backtrace(true);
$result=self::GetNames($info);
Factory::getSmarty()->assign("names",$result);
return $result;
}
public static function GetNames($info=false) /*$info is the debug_backtrace result*/
{
if(!$info)
$info=debug_backtrace(true);
$class=$info[1]['class']; /*It's the page name basically*/
if(isset(self::$m_names[$class]))
return self::$m_names[$class]; /*Using a static member for caching*/
global $application;
$langId=$application->langId;
$constants=AppFac::getAdo()->GetAll("SELECT
X.name as constant_name,
XV.value as constant_value
FROM
constants X
LEFT JOIN constants_values XV ON XV.fk_constant=X.id
LEFT JOIN constants_groups XG ON X.fk_constant_group=XG.id
WHERE
XG.name=?
AND XV.fk_language=?",array($class,$langId)); /*Parametrized query*/
$result=array();
foreach($constants as $constant) /*Make constants easily accessible*/
$result[$constant['constant_name']]=$constant['constant_value'];
self::$m_names[$class]=$result;
return $result;
}
From the PHP module, before fetching or displaying the template, call the SetNames() method, which will automatically determine the name of the class you are calling from, and will use this name to find the constant group and fill in the constants in the language set in the session. After that use the constants from the template like this {$names.label_name} for example, if you are looking for label_name constants for your page.
This way is good because:
1. You can add multiple languages right from the Web interface you build for your app.
2. You can easily organize your texts when they are stored in the database.
3. You can easily add and edit texts right from the interface you build for your app.
If you are looking to translate whole pages instead of constants, you might use Smarty Resources, when you just include a resource in the template, and in PHP you define how to handle that resource.
http://www.smarty.net/manual/en/template.resources.php