I am trying to add multi language support for my website, like this:
Language.php:
class lang {
private $lang = null;
function __construct($lang) {
global $module;
$module['lang'] = true;
$this->lang = parse_ini_file("includes/languages/{$lang}.ini");
}
public function xlate($str) {
$arg_count = func_num_args();
if ($arg_count > 1) {
$params = func_get_args();
// strip first arg
array_shift($params);
} else {
$params = array();
}
$out_str = isset($this->lang[$str]) ? $this->lang[$str] : null;
if (!$out_str) {
throw new exception("Lang String Not Found: $str");
$this->lang = parse_ini_file("includes/languages/en_gb.ini");
}
return vsprintf($out_str, $params);
}
}
I then have my language variables stored in a ini file:
en_gb.ini:
Support = Support
Quick Menu = Quick Menu
HOME_MAIN_TITLE = You will Get Paid
Then I use it like:
echo $lang->xlate("Support");
echo $lang->xlate("Quick Menu");
echo $lang->xlate("HOME_MAIN_TITLE");
Which works. However, whenever I pass a ' character in the language, it doesn't work:
HOME_MAIN_TITLE = You'll Get Paid
It echoes the text until the ' character:
You
And then it stops.
How can I fix this?
In general, all strings in .ini file should be wrapped into "double quotes". This will fix issue.
May I suggest you to look at something generic and community approved. Like https://github.com/symfony/translation (it has .ini loader inside)
Related
I have a webpage that I want to be show in different language when changing the query string, ie; mypage.php?langue=en_en
I'm able to do it with the code I found from bumperbox:
https://codereview.stackexchange.com/questions/39787/multi-language-website-management
I change the code to be able to make the language selection with a query string:
Original code
class lang {
private $lang = null;
function __construct($lang) {
$this->lang = parse_ini_file("{$lang}.ini");
}
public function xlate($str) {
$arg_count = func_num_args();
if ($arg_count > 1) {
$params = func_get_args();
// strip first arg
array_shift($params);
} else {
$params = array();
}
$out_str = isset($this->lang[$str]) ? $this->lang[$str] : null;
// if you string doesn't exist or is mistyped, then blow up, so we know about it
// or you could even go away to google translate and perform the translation for
// any missing strings
if (!$out_str) {
throw new exception("Lang String Not Found: $str");
}
return vsprintf($out_str, $params);
}
}
$lang = new lang('fr_fr');
I have changed the last line:
$lang = new lang('fr_fr');
for :
$lang = new lang(mysql_real_escape_string($_GET['langue']));
So I can select the language (fr_fr or en_en) from the url with mypage.php/langue=fr_fr
This works well.
My issue is that I would like to show a different column in my sqlquery depending of that query string.
while($row = mysql_fetch_array($rs)) {
if($lang == "fr_fr"){
$tmpTarget="code_produit";
}
else {
$tmpTarget="product_code";
}
echo "<tr><td>$row[$tmpTarget]</td></tr>"
This doesn't work, it always bring me back the else result even if my language selection is french.
I tried several things but nothing worked so far. I really don't know what else to do. I simply need that if $lang = the selection made in the query string, then my tmpTarget would be a different value so I can show the french name of the product code which is in a different column in my table.
Thank you in advance for your help!!! Much appreciated!
$lang = new lang('fr_fr');
sets $lang to an object, not a string. You need to add code to your lang class that returns the name of the language.
class lang {
private $lang = null;
public $name;
function __construct($lang) {
$this->lang = parse_ini_file("{$lang}.ini");
$this->name = $lang;
}
public function xlate($str) {
$arg_count = func_num_args();
if ($arg_count > 1) {
$params = func_get_args();
// strip first arg
array_shift($params);
} else {
$params = array();
}
$out_str = isset($this->lang[$str]) ? $this->lang[$str] : null;
// if you string doesn't exist or is mistyped, then blow up, so we know about it
// or you could even go away to google translate and perform the translation for
// any missing strings
if (!$out_str) {
throw new exception("Lang String Not Found: $str");
}
return vsprintf($out_str, $params);
}
}
Then you can do:
if ($lang->name == "fr_fr") {
$tmpTarget = "code_produit";
} else {
$tmpTarget = "product_code";
}
BTW, I would put that if statement before the while loop, since the condition doesn't change for each row.
I'm trying to follow the instructions on this docs page, but I seem to be missing something:
https://docs.joomla.org/Supporting_SEF_URLs_in_your_component
The URI that needs to be corrected is: index.php?com_component&view=legal&page=customermasteragreement
It seems like the routing function should be simple, but the page is just displaying default instead of the sub-view.
Here's my current code:
function ComponentBuildRoute(&$query)
{
$segments = array();
if (isset($query['view'])) {
$segments[] = $query['view'];
unset($query['view']);
}
if (isset($query['page'])) {
$segments[] = $query['page'];
unset($query['page']);
}
return $segments;
}
function ComponentParseRoute($segments)
{
$vars = array();
switch($segments[0])
{
case 'legal':
$vars['view'] = 'legal';
break;
case 'customermasteragreement':
$vars['page'] = 'customermasteragreement';
break;
}
return $vars;
}
Update
This code works to display the subpage, but it gives me a URI like: legal-agreements/legal?page=customermasteragreement
class ComponentRouter extends JComponentRouterBase {
public function build(&$query) {
$segments = array();
$view = null;
if (isset($query['view'])) {
$segments[] = $query['view'];
$view = $query['view'];
unset($query['view']);
}
if (isset($query['id'])) {
if ($view !== null) {
$segments[] = $query['id'];
} else {
$segments[] = $query['id'];
}
unset($query['id']);
}
return $segments;
}
public function parse(&$segments) {
$vars = array();
// View is always the first element of the array
$vars['view'] = array_shift($segments);
return $vars;
}
}
EDIT 2
If it helps, here's my model and views
models/legal.php
// import Joomla modelitem library
jimport('joomla.application.component.modelitem');
class ComponentModelLegal extends JModelItem {
public function __construct($config = array())
{
JLoader::register('ComponentHelper', JPATH_COMPONENT_ADMINISTRATOR . '/helpers/component.php');
parent::__construct($config);
}
/**
*
* #return string
*/
public function getLegal() {
$app = JFactory::getApplication();
$page = $app->input->get('page', '', 'STRING');
if ($page) {
ComponentHelper::add('type', $page); //This is an API request to an external service, returning JSON formatted data
$legal = ComponentHelper::getData('commons/legal-agreements.json', TRUE);
if (isset($legal[0]['status'])) {
JError::raiseError(400, $legal[0]['ERROR']);
return false;
} else {
if (!isset($this->legal)) {
$this->legal = $legal;
}
return $this->legal;
}
}
}
}
views/legal/view.html.php
class ComponentViewLegal extends JViewLegacy {
function display($tpl = null) {
// Assign data to the view
$this->legal = $this->get('legal');
// Check for errors.
if (count($errors = $this->get('Errors'))) {
JLog::add(implode('<br />', $errors), JLog::WARNING, 'jerror');
return false;
}
// Display the view
parent::display($tpl);
}
}
views/legal/tmpl/default.php
$page = JRequest::getVar('page');
$pages = array(
'resellermasteragreement',
'customermasteragreement',
'resellerdomainagreement',
'customerdomainagreement',
'resellerwebserviceagreement',
'customerwebserviceagreement',
'resellerdigicertagreement',
'customerdigicertagreement',
'registraragreement',
'customerhostingproductagreement',
'resellerhostingproductagreement'
);
?>
<div class="col-lg-12">
<?php
echo in_array($page, $pages) ? $this->loadTemplate('legal') : $this->loadTemplate('home');
?>
</div>
views/legal/tmpl/default_legal.php
$page = JRequest::getVar('page');
echo nl2br(htmlspecialchars($this->legal[$page]['defaultagreement'], ENT_NOQUOTES, "UTF-8"));
?>
<a type="button" class="btn btn-primary" href="<?php echo JROUTE::_("index.php?option=com_component&view=legal"); ?>">Back</a>
EDIT 3
This works because I wasn't navigating directly to the page from a menu item. There's a top level menu, then a page a links to the agreements. I have a hidden menu to each item, but that wasn't the link I followed.
$app = JFactory::getApplication()->getMenu();
$customermaster = $app->getItems( 'link', 'index.php?option=com_component&view=legal&page=customermasteragreement', true );
JRoute::_('index.php?Itemid='.$customermaster->id);
You have one view but you're not actually set the page argument correctly when you catch a view argument, but you treat page as some king of another view. Can you please try this and check if it works:
function [componentname]ParseRoute($segments)
{
$vars = array();
switch($segments[0])
{
case 'legal':
$vars['view'] = 'legal';
$vars['page'] = $segments[1];
break;
}
return $vars;
}
Also the functions ComponentBuildRoute, ComponentParseRoute must have your components name instead of Component at the beginning.
EDIT
[componentname]BuildRoute and [componentname]ParseRoute are deprecated, you should stick the the class that extends JComponentRouterBase as you have it in your updated second example. Try this one here and see if it works:
class ComponentRouter extends JComponentRouterBase {
public function build(&$query) {
$segments = array();
$view = null;
if (isset($query['view'])) {
$segments[] = $query['view'];
$view = $query['view'];
unset($query['view']);
}
if (isset($query['page'])) {
$segments[] = $query['page'];
unset($query['page']);
}
return $segments;
}
public function parse(&$segments) {
$vars = array();
// View is always the first element of the array
$vars['view'] = array_shift($segments);
if(count($segments) > 0)
$vars['page'] = array_shift($segments);
return $vars;
}
}
EDIT 2
I have a test Joomla 3 site with a simple component named Ola.
Non SEO component URL: http://j3.dev.lytrax.net/index.php?option=com_ola&page=test_page&view=ola
URL generated by JRoute before Router class added to router.php: http://j3.dev.lytrax.net/index.php/component/ola/?page=test_page&view=ola
SEO URL returned by JRoute::_('index.php?option=com_ola&page=test_page&view=ola'); after creating router.php and used the Router class above: http://j3.dev.lytrax.net/index.php/component/ola/ola/test_page
If you visit the links, you'll see that all are working and you can even see the page, view and JRoute results of the calling component Ola.
Unless I've completely misunderstood Joomla (I've been developing with it for four five years now), there is no "sub-view" concept implemented. Trying to use the idea is what's getting you into trouble I think.
I'm astounded that the idea of using a visual debugger is so unpopular.
Get yourself something like Netbeans (there are others too), set up a local web and database server, and watch the code run. Very (well, relatively very) quickly you'll start to understand how the structure hangs together.
You may put code in your view that picks up the extra params you've set and displays or hides things accordingly, but there is no "sub-view".
Until yesterday I was burning my brain trying to switch from a procedural thinking to a OOP thinking; this morning I gave up. I said to my self I wasn't probably ready yet to understand it.
I started then coding in the usual way, writing a function to check if there's the cookie "logged" or not
function chkCookieLogin() {
if(isset($_COOKIE["logged"])) {
$logged = 'true';
$cookieValue = $_COOKIE["logged"];
return $logged;
return $cookieValue;
}
else {
$logged = 'false';
return $logged;
}
}
$result = chkCookieLogin();
if($result == 'true'){
echo $cookieValue;
}
else {
echo 'NO COOKIE';
}
since I run across a problem: I wanted to return two variables ($logged and $cookieValue) instead of just one. I google it and I found this answer where Jasper explains a method using an OOP point of view (or this is what I can see).
That answer opened me a new vision on the OOP so I tried to rewrite what I was trying to achieve this way:
class chkCookie {
public $logged;
public $cookieValue;
public function __construct($logged, $cookieValue) {
$this->logged = $logged;
$this->cookieValue = $cookieValue;
}
function chkCookieLogin() {
$out = new chkCookie();
if(isset($_COOKIE["logged"])) {
$out->logged = 'true';
$out->cookieValue = $_COOKIE["logged"];
return $out;
}
else {
$out->logged = 'false';
return $out;
}
}
}
$vars = chkCookieLogin();
$logged = $vars->logged;
$cookieValue = $vars->cookieValue;
echo $logged; echo $cookieValue;
Obviously it didn't work at the first attempt...and neither at the second and the third. But for the first time I feel I'm at one step to "really touch" the OOP (or this is what I think!).
My questions are:
is this attempt correctly written from the OOP point of view?
If yes, what are the problems? ('cause I guess there's more than one)
Thank you so much!
Credit to #NiettheDarkAbsol for the idea of returning an Array data-type.
Using dependency injection, you can set-up an object like this:
class Factory {
private $Data = [];
public function set($index, $data) {
$this->Data[$index] = $data;
}
public function get($index) {
return $this->Data[$index];
}
}
Then to use the DI module, you can set methods like so (using anonymous functions):
$f = new Factory();
$f->set('Cookies', $_SESSION);
$f->set('Check-Cookie', function() use ($f) {
return $f->get('Cookies')['logged'] ? [true, $f->get('Cookies')['logged']] : [false, null];
});
Using error checks, we can then call the method when and as we need it:
$cookieArr = is_callable($f->get('Check-Cookie')) ? call_user_func($f->get('Check-Cookie')) : [];
echo $cookieArr[0] ? $cookieArr[1] : 'Logged is not set';
I'd also consider adding constants to your DI class, allowing more dynamic approaches rather than doing error checks each time. IE, on set() include a constant like Factory::FUNC_ARRAY so your get() method can return the closure already executed.
You can look into using ternary operators if you're confused.
See it working over at 3v4l.org.
If it means anything, here is an OOP styled approach.
I'm currently a beginner developer and have just started my first big project whilst I have spare time, What I'm trying to do is basically write variables to a html/tpl document, Which I have currently got working, Here is my code:
private function index(){
$username = 'MyUsername';
$onlineTime = 'MyOnlineTime';
$this->setParams('Username', $username); // $username Will be replaced by database queried results once completed.
}
And here is the setParams function.
function setParams($item1, $item2){
ob_start();
$theme = 'default';
include_once T . '/'.$theme.'/index.php'; // T . is defined at the beginning of the document.
if ((($html = ob_get_clean()) !== false) && (ob_start() === true))
{
echo preg_replace('~{(['.$item1.']*)}~i', ''.$item2.'', $html, 1);
}
}
And here is the coding inside the html/tpl document.
{username} has been online for {onlineTime} Hours
This is probably a very simple code for some of you but as this is my first attempt this is all I can do.
What I would like to do is have it so you can setParams as many times as you want without changing the $variable names like so:
private function index(){
$username = 'MyUsername';
$onlineTime = 'MyOnlineTime';
$this->setParams('Username',$username);
$this->setParams('OnlineTime', $onlineTime);
}
whilst keeping the setParams($item1, $item2)
But as you can imagine this just cuts the code completely. Does anyone know a solution to this problem? I've been searching all day without any real luck.
Thanks In Advance,
Ralph
I think what you need is a class with a static method;
<?php
class Params {
public static $params = array();
public static function setParam($key, $value) {
self::$params[$key] = $value;
}
public static function getParam($key) {
if (isset(self::$params[$key])) {
return self::$params[$key];
}
}
}
// Usage
// Set Username
Params::setParam("username", "JohnDoe");
Params::setParam("password", "12345");
echo Params::getParam("username");
echo Params::getParam("password");
I'm trying to create an Autoloader class, so that I'll be able to load all modules automatically. But the problem is, I want to set a global from configuration file, and later, just call all of them by using:
Autoloader::GetGlobals();
So far, I have these 3 files:
Configuration.php
<?php
global $Configuration;
$Configuration['Globals'] = "Core Database Templates Directory Debugger";
?>
Autoloader.Class.php
<?php
require_once('Configuration.php');
private static $Globals = "";
private static $MakeGlobal = "global ";
public static function GetGlobals()
{
$ParsedGlobals = "";
$Globals2String = explode(" ", Autoloader::$Globals);
foreach($Globals2String as $Global)
$Globals[] = "$".$Global;
$DefinedGlobals = count($Globals);
for ($i = 0; $i < $DefinedGlobals; $i++)
{
$LastElement = $DefinedGlobals - 1;
if($i != $LastElement)
$ParsedGlobals .= $Globals[$i].", ";
else
$ParsedGlobals .= $Globals[$i].";";
}
return Autoloader::$MakeGlobal.$ParsedGlobals;
}
?>
I'm getting the right output:
global Core, Database, Templates, Directory, Debugger;
The next thing is that I want to interpret this as PHP code and not as a string, and I don't want to use eval() (because I've read many articles that says that this is the last function to be used).
So the question is, is it possible to run this string from return as PHP code by simply calling it as Autoloader::GetGlobals();?
It's almost as bad as using eval(), but there's variable variables, if you choose to go down this path of madness and chaos:
function foo($arg) {
global $$arg; // note the $$
echo "$arg / $$arg";
}
$a = 'bar';
foo('a');
output:
a / bar