0

I always get a PHP notice in my error log because of a little language function I am using. Somehow I can't get it right...

My languages array looks like this (EDITED):

    $dict = [
        'title' => [
            'en' => 'Welcome',
            'de' => 'Willkommen'
        ],
        'description' => [
            'en' => 'Hello',
            'de' => 'Hallo'
        ],
    ];

And this is my language function (EDITED):

function lang($phrase){
    global $l, $dict;

    return $dict[$phrase][$l];
}

$l is defined before the $dict array (as "de" or "en").

In my document I am doing:

<?php echo lang('title');?>

And the PHP notice refers to the "return $dict[$phrase][$l];" part. I don't really understand why. I tried different things and nothing works.

EDIT:

Oh, damn. After posting I finally realized I where using a phrase which is not in my language array anymore. Somehow I was blind. Nevertheless the post was good to clean up my function. Thanks!

2
  • 1
    You have a missing closing bracket at the end of your array, and that in itself should result in syntax error. I'm assuming that's a copy-paste goof. When I run your code after the fix, it seems to work as expected: 3v4l.org/1HZqV ... Otherwise, your sample code is unrelated to <?php echo $title; ?> -- where is $title defined, and where/how do you actually call the function? Commented Oct 17, 2020 at 14:11
  • If you get an error notice along the lines of "undefined index" (please add the exact error to your post!), it means that whatever you pass into lang() as $phrase isn't defined in your dictionary. You may want to do <?php $lang = 'en'; echo lang('title'); ?> instead. Better, at least pass the language as an argument to the function, lang($phrase, $language), and call lang('title', 'en'), or lang('title', $l)` in your code to avoid the globals you can easily avoid. Commented Oct 17, 2020 at 14:15

2 Answers 2

3

As noted my comments on the original post, after fixing the missing bracket (assuming typo) in your array declaration, the function works correctly, if called correctly as echo lang('title'). If you get a notice on Undefined index, it means the $phrase you're looking for doesn't exist in the $dict array. (If you only get the notice in your error logs, you may want to turn on on-screen error reporting when you're developing to catch these in real time!)

Basic debugging aside, let me help you tidy up this code a bit. First, $l or language can be passed in as a function argument. Second, the $dict doesn't need to be a variable, assuming you're not modifying it on the fly. You can declare it as a constant and avoid globals. Third, your array mapping is unnecessary overhead, when you can simply call the relevant indexes in the array. A minimal clean-up would then look something like this:

const DICT = [
    'title' => [
        'en' => 'Welcome',
        'de' => 'Willkommen'
    ],
    'description' => [
        'en' => 'Hello',
        'de' => 'Hallo'
    ]
];

function lang($phrase, $lang) {
    // If you're using PHP 7, the null-coalescing operator is handy here:
    return DICT[$phrase][$lang] ?? "Phrase {$phrase} Unknown!";
}

You'd use it like this:

// Sample usage: Output title in English and description in German:

echo lang('title', 'en') . ' ' . lang('description', 'de');

// Or, if the language variable is pre-defined, then e.g.:

$l = 'en';
echo lang('title', $l) . ' ' . lang('description', $l);

Here, the use of a constant for your dictionary is a basic but very efficient way to handle a global need for pre-defined data. Constants are parsed at compile-time, while variable declarations are parsed at runtime. They are available in all contexts. And you definitely don't want to have the whole $dict inside your function just to avoid a global declaration!

A more elegant way to handle this would be to create a class, which will allow you to avoid the language function parameter. (Class constants and properties are like "local globals", available to all methods in the class.) Here's a sample application of your case as a class, where the dictionary is a class constant, and the language is a class property:

class Dict {

    const DICT = [
        'title' => [
            'en' => 'Welcome',
            'de' => 'Willkommen'
        ],
        'description' => [
            'en' => 'Hello',
            'de' => 'Hallo'
        ]
    ];
    
    public static $lang = 'en'; // for a default language
    
    public static function phrase($phrase)
    {
        return self::DICT[$phrase][self::$lang] ?? "Phrase {$phrase} Unknown!";
    }

}   

Dict::$lang = 'de'; // Define language

echo Dict::phrase('title') . ' ' . Dict::phrase('description');
// : Willkommen Hallo

If you expect to have a large dictionary that needs to be accessed in other contexts, or simply wish to centralize your language reference, then you may want to define it as a regular constant outside the class, as in the first example. I've used a class constant here mostly for educational purposes. Hope this helps you forward and towards cleaner code.

Sign up to request clarification or add additional context in comments.

3 Comments

Sorry for the typo and copy/paste errors. Thanks your you answer! Creating a class is what I will do later on. I don't even remember, why I made the function so complicated. Edited my post, because I found the mistake. Thanks again for your help and the class.
Glad it helped. Redoing the array_map at each function call would add up significant overhead when either your dictionary size or function calls scale up. No harm going with a class even if it's just one method for now, chances are in time you will have related functions requiring shared assets that you don't want in the global scope.
Just implemented the class, too. Works perfect and no more globals flying around. Thanks! :)
0

The notice means that you are trying to access an index of your array that doesn't exist. In your case, it seems that you mixed up the order of language and phrase: You try to access $dict[en][title] instead of $dict[title][en].

More generally, always check if an index exists before you return it:

if (isset($dict[$l])) {
    return ...
} else {
    throw new Exception... // or whatever
}

As you move further on with PHP, you'll find that using globals will cause more harm than it's worth.

1 Comment

The array mapping he does seems to be correct. Presumably the error is simply caused by a call to a phrase that doesn't exist to begin with.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.