In SuiteCommerce Advance Vision release by default behavior to setup multi language is to add multiple domain under Setup-->Configuration-->Multi Domain Tab--> Hosts
Once you setup this you can able to see drop down in your web store which will have domain and language associate with that domain. We have already implemented this feature.
But, SCA also has feature to setup multi language with 'lang' parameter which will automatically change language withing single domain like below
We did this already-->(With multiple domain)
https://www.en.scasite.com/
https://www.fr.scasite.com/
We want below setup-->
https://www.scasite.com/?lang=en_US
https://www.scasite.com/?lang=fr_FR
So, Can any one have idea how we can pass language param in url?
Yes you are right SCA out of the box don't have this feature, but yes you can send 'lang' param in url and can able to change your language.
Remember if you are uding SCA vision release, do not add any language hosts in configuration under setup tab
To pass 'lang' param i.e www.domain.com/lang=it_IT etc
Override the Host selector file in your custom module to do this, you need to create below two files under global view
1.Modules/custom/GlobalViews#1.0.0/JavaScript/GlobalViews.HostSelector.View.js
Copy & paste below code in setLanguage: function (e){}
, setLanguage: function (e)
{
var language_code = jQuery(e.target).val()
, selected_language = _.find(SC.ENVIRONMENT.availableLanguages, function (language)
{
return language.locale === language_code;
});
// We use the param **"cur"** to pass this to the ssp environment
var current_search = Utils.parseUrlOptions(window.location.search);
// if we are in a facet result we will remove all facets and navigate to the default search
// TODO REVIEW THIS
if (window.location.hash !== '' && _.values(SC._applications)[0].getLayout().currentView instanceof BrowseView)
{
window.location.hash = Configuration.defaultSearchUrl || '';
}
current_search.lang = selected_language.locale;
window.location.search = _.reduce(current_search, function (memo, val, name)
{
return val ? memo + name + '=' + val + '&' : memo;
}, '?');
}
Use below code in getContext() to return some data to handlbar view
var available_languages = _.map(SC.ENVIRONMENT.availableLanguages, function(language)
{
// #class GlobalViews.CurrencySelector.View.Context.Currency
return {
// #property {String} code
code: language.locale
// #property {String} internalId
, internalId: language.internalid
// #property {String} isDefault
, isDefault: language.isdefault
// #property {String} symbol
, symbol: language.languagename
// #property {String} displayName
, displayName: language.locale|| language.locale
, isSelected: SC.ENVIRONMENT.currentLanguage.locale === language.locale
};
});
// #class GlobalViews.CurrencySelector.View.Context
return {
// #property {Boolean} showCurrencySelector
showLanguageSelector: !!(SC.ENVIRONMENT.availableLanguages && SC.ENVIRONMENT.availableLanguages.length > 1)
// #property {Array<GlobalViews.CurrencySelector.View.Context.Currency>} availableCurrencies
, availableLanguages: available_languages || []
// #property {String} currentCurrencyCode
// #property {String} currentCurrencySymbol
, currentLanguageSymbol: SC.getSessionInfo('language').languagename
};
}
Copy & Paste Below code in below file
Modules/custom/GlobalViews#1.0.0/Templates/global_views_host_selector.tpl
{{#if showLanguageSelector}}
<div class="global-views-currency-selector">
<span class="global-views-host-selector-addon">
{{currentLanguageSymbol}}
</span>
<select data-toggle="currency-selector" class="global-views-currency-selector-select">
{{#each availableLanguages}}
<option value="{{code}}" {{#if isSelected}}selected{{/if}}>
{{symbol}}
</option>
{{/each}}
</select>
</div>
{{/if}}
Done!! Now you can see 'lang' param is being added into url
Let me know if you want more help on this.
Related
I have a rails app running alongside with a rails API, there is a constant value for DAYS_LIMIT in config/initializers/constants.rb
DAYS_LIMIT = 40
DEFAULT_PRICE = 1.29
but now in the app i added an input field so that the user decide his DAYS_LIMIT.
So i want to fetch that value from the database from inside the API models.
I have placed breakpoints and can see that inside the API controller, the data is transfered from the app but not to the models.
edited as a question requested , it's a React-on-Rails app , here is the code where the new input field is save to the database (i have removed the other fields so the question look shorter)
export const saveChannel = (files) => {
return async (dispatch, getState) => {
const { channel } = getState();
const {rss_podcast_days} = channel;
const { image } = files;
const save = id ? updateChannel : createChannel;
const sub_required = subscription_required !== undefined ? subscription_required : false;
const formData = new FormData();
formData.append('channel[rss_podcast_days]', rss_podcast_days || '');
if (Object.keys(image).length) {
formData.append('channel[image]', image);
}
const channelId = await dispatch(save(formData, id));
dispatch(fetchChannel(id));
return id;
};
};
from the app controller
podcast_list = RestClient.get("#{ENV['URL_API']}/api/#{#channel.id.as_json}/podcast/list")
#podcasts = JSON.parse(podcast_list.body)
#podcasts = #podcasts.sort.reverse.to_h
this is from the API controller witch the data is transfered from the app
def index
podcasts = #channel.podcasts.published.list(params[:page], params[:items_per_page], params[:ordered_in])
render json: Podcasts::Normalizer.normalize(podcasts, #channel.station.default_podcast_price)
end
and here from the API model that i want to fetch data instead of the constants.
scope :by_days_limit, -> {with_tags.more_recent_than(Date.today - DAYS_LIMIT.days).ordered}
it should take today date minus the value (DAYS_LIMIT) from user input, but for now i get undefined local variable or method if i try to fetch directly
Bro if your class has constant like DAYS_LIMIT you can access it using that class itself for example,
class Demo
DAYS_LIMIT = 5
end
you can access that constant by Demo.DAYS_LIMIT in controller or else wherever you need it.
good luck!
ok , so i finally got it, i don't know if i should delete this thread or just tell how i did it. If it's inapropriate just tell me and i will delete this entire thread.
So here is how i did it, in the API controller i had to add my fetch so that the arguments (list) knows what i am talking about. #channel.days_limit
def index
podcasts = #channel.podcasts.published.list(params[:page], params[:items_per_page], params[:ordered_in], #channel.days_limit)
render json: Podcasts::Normalizer.normalize(podcasts, #channel.station.default_podcast_price)
end
then in the def list of the models, i added days_limit has argument
def list(page = nil, nb_items_per_page = 40, ordered_in = 'desc', days_limit)
ordered_in = ordered_in.in?(['asc', 'desc']) ? ordered_in : 'desc'
page.blank? ? by_days_limit(days_limit) : by_page(page, nb_items_per_page, ordered_in)
end
and finally in the scope of the models, i pass in the new argument
scope :by_days_limit, -> (days_limit) {with_tags.more_recent_than(Date.today - days_limit.days).ordered}
Now the user input from the app is passing to the models via the controller.
I am trying to create a combo box with a list of countries in SAP UI5.
I have created a combo box and have created dynamic list of some countries, but to create more than 100 countries, the only easy way is to create a JSON file of countries and then populate in Controller.js.
I tried to create a JSON file but I am unsure whether I have to store it under model folder or root.
What changes do I have to make in my XML view and controller, and where should I attach countries.json file?
You are looking at something called as "Aggregation Binding" Aggregation Binding in XML views
Here is an example to refer to which explains
How to create a model using data from json file
How to Bind model data to the XML view control(you have to bind comboBox instead of table)
How to bind json data model to an XML view
Let me know if this helps.
Maybe you don't need to create the countries.json file at all :)
As UI5 leverages Common Locale Data Repository (CLDR) internally and provides the data via sap.ui.core.LocaleDataAPI, which includes language names, country names, currency names, singular/plural modifications, and more..
A list of supported regions for the locale data are stored in a JSON format here. In one of those files, if you look at the property "territories", you'll see that the country names are listed among them. You can filter every irrelevant territory out that is not considered a country, and then bind the rest in the items aggregation of the combo box.
Demo
sap.ui.getCore().attachInit(() => sap.ui.require([
"sap/ui/core/Locale",
"sap/ui/core/LocaleData",
"sap/ui/model/json/JSONModel",
"sap/ui/core/mvc/XMLView",
], function(Locale, LocaleData, JSONModel, XMLView) {
"use strict";
XMLView.create({
definition: `<mvc:View xmlns:mvc="sap.ui.core" xmlns="sap.m"
height="100%"
displayBlock="true">
<ComboBox class="sapUiTinyMargin"
width="15rem"
placeholder="Select a country.."
filterSecondaryValues="true"
showSecondaryValues="true"
items="{
path: '/',
templateShareable: false,
key: 'code',
sorter: { path: 'name' }
}">
<core:ListItem xmlns:core="sap.ui.core"
key="{code}"
text="{name}"
additionalText="{code}" />
</ComboBox>
</mvc:View>`,
models: createCountryModel(getCountries()),
}).then(view => view.placeAt("content"));
function createCountryModel(countries, sizeLimit = 300) {
const model = new JSONModel(countries);
model.setSizeLimit(sizeLimit);
model.setDefaultBindingMode("OneWay");
return model;
}
function getCountries() {
const territories = getTerritories();
return extractCountriesFrom(territories, byCustomCheck());
}
function getTerritories(localeId) {
const currentConfig = sap.ui.getCore().getConfiguration();
const locale = localeId ? new Locale(localeId) : currentConfig.getLocale();
const localeData = new LocaleData(locale);
return localeData.getTerritories(); // includes country names
}
function extractCountriesFrom(territories, customCheck = () => true) {
const isValidCountry = createCountryCheck(customCheck);
const toObject = code => Object.freeze({
code: code,
name: territories[code],
});
const countryObjects = Object.keys(territories)
.filter(isValidCountry)
.map(toObject);
return Object.freeze(countryObjects);
}
function createCountryCheck(customCheck, obviouslyNotCountries = [
"EU", // "European Union"
"EZ", // "Eurozone"
"UN", // "United Nations"
"ZZ", // "Unknown Region"
]) {
return territoryCode => territoryCode.length == 2
&& !obviouslyNotCountries.includes(territoryCode)
&& customCheck(territoryCode);
}
function byCustomCheck() { // returns a function that returns boolean
// E.g.: list of sanctioned countries you want to exclude
const list = [
"AF",
"KP",
"IR",
// ...
];
return countryCode => !list.includes(countryCode);
}
}));
<script id="sap-ui-bootstrap" src="https://ui5.sap.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.ui.core, sap.m"
data-sap-ui-theme="sap_fiori_3"
data-sap-ui-async="true"
data-sap-ui-compatversion="edge"
data-sap-ui-xx-waitfortheme="init"
></script>
<body id="content" class="sapUiBody sapUiSizeCompact"></body>
As you can see in the example, the ComboBox is successfully populated with the countries. When a new LocaleData instance is created, a request is sent immediately (currently via sync XHR) to get the data which are translated in the language that UI5 detects from the client settings. If no language could be detected, the en.json file will be retrieved.src
The above approach has the following advantages:
No need to create and maintain a separate "country" list. ✔️
Multilingual support ✔️
Reusability ✔️ - When UI5 tries to fetch the same locale data file, which is the case when e.g. a Calendar is used, the browser can serve the file quickly from the cache since the same file was already fetched before.
Note
When creating a JSONModel to store more than 100 country names, keep in mind to increase the size limit as well. The current default limit is 100.
I'm receiving a list of elements from an API. All the elements are well formatted. When I dump one of them using twig, I get the following :
Leg {#2695 ▼
-id: null
#reservation: null
-airportStart: "AIX LES MILLES"
-airplaneType: "Cessna Citation Mustang"
-airportEnd: "ROBINSON"
-startDate: "2015-09-10 20:00:00"
-startHour: "2015-09-10 20:00:00"
-endHour: "2015-09-10 21:00:21"
-durationLeg: "01:21"
#nbPax: "4"
-price: null
-updatedPrice: null
-discountOnLeg: null
-tva: null
-status: null
}
My user must select one of these elements, So what I'm trying to do is to send the encoded json back to the controller, using
{{ element|json_encode }}
Unfortunately, the json is empty. When I try to dump the encoded json using
{{ dump(element|json_encode) }}
all I get is an empty array {};
Any idea why Is there another way to send the selected element datas to a controller function? (These elements are not persisted, each call on the API returns thousands of results)
I'm little late to the party (2 years of lateness), but for any one like me coming from a google research i say : i had the same problem too, and after googling around, i "solved" my problem with the Serializer Component. How? let me show you!
Installation
php composer.phar require symfony/serializer
Entity
<?php
namespace Your\Namespace\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
/**
* Leg
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Your\Namespace\Entity\LegRepository")
*/
class Leg {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
public function serializer()
{
$encoder = new JsonEncoder();
$normalizer = new ObjectNormalizer();
$normalizer->setIgnoredAttributes(array(
'whatever', 'attributes', 'you', 'want', 'to', 'ignore'
));
// The setCircularReferenceLimit() method of this normalizer sets the number
// of times it will serialize the same object
// before considering it a circular reference. Its default value is 1.
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getName();
});
$serializer = new Serializer(array($normalizer), array($encoder));
return $serializer->serialize($this, 'json');
}
}
Twig
{{ Leg.serializer|raw }}
N.B : this is tested under symfony 2.6
I have a simple form with the the following inputs:
type (radio) and parent (select)
Depending on the type selected, there should be different options for the select. So whenever type is selected, it should make the json request to get a list of parents based on the type.
So far what I have:
json request (in template page)
$.getJSON("{% url 'locations_by_type' %}", {type: type}, function(locations) {
var locations_select = $("select[name='parent']");
$.each(locations, function (index, value) {
locations_select.append($("<option/>", {
value: key,
text: value
}));
});
});
forms.py
class LocationForm(forms.Form):
LOCATION_TYPE_COICES = (
("country", "country"),
("state", "state"),
("county", "county"),
("town", "town"),
)
name = forms.CharField(max_length=45)
type = forms.ChoiceField(
choices=LOCATION_TYPE_COICES,
widget=RadioSelect(),
required=True,
)
parent = forms.ChoiceField(
label = 'Parent Location',
widget = Select(),
required = False,
help_text = "If you do not choose a parent location, this location will not appear on the locations page"
)
views.py
def locations_by_type(request):
locations = Location.objects.get(type=request.GET["type"])
return json.dumps(locations)
urls.py individual pattern
url(r'^/locations_by_type', views.locations_by_type, name='locations_by_type'),
And when I load the form, the form itself loads fine, however I get an error from the request in the javascript console:
GET http://locations_by_type/?type=state net::ERR_NAME_NOT_RESOLVED
Any ideas as to what I am doing wrong here?
UPDATE 1
I had a "/" in front of locations_by_type in the url regex previously. After fixing this, the javascript console reported a 500 Internal Server Error
The Django error that is logged in the manage.py runserver window is:
TypeError: <Location: SC> is not JSON serializable
UPDATE 2
I have managed to get rid of the 500 internal server error, however the data doesn't return as expected. I believe it has something to do with how I iterate over the returned locations list in the location.html script, but I'm not positive.
My forms.py remains the same as it was when this question was asked.
views.py
def locations_by_type(request):
locations = Location.objects.filter(type=request.GET["type"]).values('id', 'name')
data = json.dumps(list(locations), cls=DjangoJSONEncoder)
return HttpResponse(data, content_type='application/json')
location.html script
<script>
//Update Location Select box given a type
function updateLocations(type) {
$.getJSON("{% url 'locations_by_type' %}", {type: type}, function(locations) {
var locations_select = $("select[name='parent']");
$.each(locations, function (index, value) {
locations_select.append($("<option/>", {
value: index,
text: value
}));
});
});
}
//If there is a type selected at document load, make sure parent select
// has the appropriate values, otherwise hide it
$(document).ready(function() {
if ($("input[name='type']:checked").size() == 0) {
$("select[name='parent']").parent().parent().hide();
} else {
var type = $("input[name='type']:checked").val();
updateLocations(type);
}
});
//Whenever type is changed, make sure the parent select is shown and has
// the appropriate values
$("input[name='type']").change(function() {
$("select[name='parent']").parent().parent().show();
var type = $("input[name='type']:checked").val();
updateLocations(type);
})
</script>
Finally, what I get returned is:
<select id="id_parent" name="parent">
<option value="0">[object Object]</option>
<option value="1">[object Object]</option>
....
</select>
However, I would like:
<select id="id_parent" name="parent">
<option value="[location.id]">[location.name]</option>
<option value="[location.id]">[location.name]</option>
....
</select>
Context:
I Want to create a web application using CakePhp which should be translatable. I want to save multiple translations for the same field in one form.
Problem:
I've tried a dozen ways to get this to work and I did. But I ended up using two custom SQL queries which really doesn't feel like a cakePhp solution.
Question:
Does anybody know a better way to achieve the same result?
What I tried:
Giving the form fields a name like 'Model.fieldName.locale', which gives it the right format in the name attr of the input element but then my validation doesn't recognize the field name. But saving works.
Giving the form fields a name like 'modelLocale' and pass in a name attr 'data[Model][field][locale]', in this case the validation works exept for isUnique but saving to the database doesn't work.
More variations of this but not worth mentioning.
I'll add my view and model below: (if u want to see more code or need more info just ask)
/App/View/Category/add.ctp
<?php echo $this->Form->create(); ?>
<?php echo $this->Form->input('title|dut'); ?>
<?php echo $this->Form->input('title|eng'); ?>
<?php echo $this->Form->input('title|fre'); ?>
<?php echo $this->Form->input('description|dut', array('type'=>'textarea')); ?>
<?php echo $this->Form->input('description|eng', array('type'=>'textarea')); ?>
<?php echo $this->Form->input('description|fre', array('type'=>'textarea')); ?>
<?php echo $this->Form->end('add'); ?>
/App/Model/AppModel.php
<?php
App::uses('Model', 'Model');
class AppModel extends Model {
/**
* Check Unique
*
* Searches the i18n table to determine wetter a field is unique or not.
* Expects field name to be as following: "fieldname|locale".
*
* #param array $data The data of the field, automatically passed trough by cakePhp.
* #param string $field The name of the field, which should match the one in the view.
* #returns boolean
*/
public function checkUnique($data, $field) {
// Seperate the field key and locale which are seperated by "|".
$a = preg_split('/[|]/', $field, 2);
// If field key and locale are found...
if (is_array($a) || count($a) === 2) {
$q = sprintf("SELECT * FROM i18n WHERE i18n.locale = '%s' AND i18n.model = '%s' AND i18n.field = '%s' AND i18n.content = '%s' LIMIT 1",
Sanitize::escape($a[1]),
Sanitize::escape(strtolower($this->name)),
Sanitize::escape($a[0]),
Sanitize::escape($data[$field])
);
if ($this->query($q)) {
return false;
}
return true;
}
}
/**
* Setup Translation
*
* Loops trough the fields. If a field is translatable
* (which it will know by it's structure [fieldname]|[locale])
* and has the default locale. Then it's value will be stored
* in the array where cake expects it
* (data[Model][fieldname] instead of data[Model][fieldname|defaultLocale])
* so that cake will save it to the database.
*
* In the afterSave method the translations will be saved, for then we know
* the lastInsertId which is also the foreign_key of the i18n table.
*/
public function _setupTranslations() {
foreach($this->data[$this->name] as $key => $value) {
$a = preg_split('/[|]/', $key, 2);
if (is_array($a) && count($a) === 2) {
$languages = Configure::read('Config.languages');
if ($a[1] === $languages[Configure::read('Config.defaultLanguage')]['locale']) {
$this->data[$this->name][$a[0]] = $value;
}
}
}
}
/**
* Save Translations
*
* Saves the translations to the i18n database.
* Expects form fields with translations to have
* following structure: [fieldname]|[locale] (ex. title|eng, title|fre, ...).
*/
public function _saveTranslations() {
foreach($this->data[$this->name] as $key => $value) {
$a = preg_split('/[|]/', $key, 2);
if (is_array($a) && count($a) === 2) {
$q = sprintf("INSERT INTO i18n (locale, model, foreign_key, field, content) VALUES ('%s', '%s', '%s', '%s', '%s')",
Sanitize::escape($a[1]),
Sanitize::escape(strtolower($this->name)),
Sanitize::escape($this->id),
Sanitize::escape($a[0]),
Sanitize::escape($value)
);
$this->query($q);
}
}
}
/**
* Before Save
*/
public function beforeSave() {
$this->_setupTranslations();
return true;
}
/**
* After Save
*/
public function afterSave() {
$this->_saveTranslations();
return true;
}
}
/App/Model/Category.php
<?php
class Category extends AppModel {
public $name = 'Category';
public $hasMany = array(
'Item'=>array(
'className'=>'Item',
'foreignKey'=>'category_id',
'order'=>'Item.title ASC'
)
);
var $actsAs = array(
'Translate'=>array(
'title',
'description'
)
);
public $validate = array(
'title|dut'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|dut'),
'message'=>'Titel reeds in gebruik'
),
),
'title|eng'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|eng'),
'message'=>'Titel reeds in gebruik'
),
),
'title|fre'=>array(
'required'=>array(
'rule'=>'notEmpty',
'message'=>'Veld verplicht'
),
'unique'=>array(
'rule'=>array('checkUnique', 'title|fre'),
'message'=>'Titel reeds in gebruik'
),
),
);
}
?>
NOTE: There isn't that much information out there on this subject... I have a lot more questions about the translation behavior like getting the recursive results also in the correct locale, ... Anybody know a good tut or source of info (cookbook is quite limited)
Thanks for reading!!
It appears you may be building a CRM of sorts that allows the users to establish content that is read into the site based on the language they have set. I would use the built in i18n and l10n. It makes it really simple, but this is probably not a solution for dynamic content.
Having said that, the only other way I can think of doing this is very tedious. I would build a single screen with a language identifier drop down. So instead of trying to cram ALL languages in the same screen with a test box for each language, I would create one form and then use a drop down for the language.
Your model is using a column to define with language the row belongs to. The form you have created is expressing all languages in a single row. So if you were to view the Index page showing the records, of course you would see:
title 1 eng
title 1 dut
title 1 fre
title 2 eng
title 2 dut
title 2 fre
...
Further more, if you were ever to add a new language, you will have to modify the validation in the model and the form.
However, if you are set on doing it this way, change the | to _ and off you go. But then you will need to store all of the data in a single record. So when you look at the Index for the records, you will see:
title 1 end dut fre
title 2 end dut fre
...
My Advice:
1) Use the built in i18n / l10n using .po / .pot files.
2) If the content will be changing frequently and required to be stored in the database so it can be easily changed / updated frequently on the fly, then use a drop down.
Language: dropdown
Title: text_field