Translating FreePBX
FreePBX uses the gettext package for i18n. This makes translating to your language not so easy as you think.
You need to extract all text from the current system, traversing all module subdirectories. And even before that you need to download all modules so that you can translate all text. Sound simple. Well it is not.
Every time there is a new release with added text you need to to extract the text again, compare that against you prevoius work, add the new text, compile and then upload the corrected text in a ticket. If you find a spelling error the development team need to bump the revision of the module containing the corrected text. And as I have discovered, if the text in a module is coded like this:
echo "<h2>" . _("Module Administration") . "</h2>";
The text is extracted, but if it is like this:
echo "<h2>" . _('Module Administration') . "</h2>";
It will not be extracted and in the end, never translated.
- Note - I believe if you use "-L PHP" instead of "-C" in xgettext you will pull the single quoted strings
The proposal that I have is this:
Instead of having the English text in the module you replace that with a label. Today it is like _("Some text"). Replace that with a label _DEFINEDLABEL. In a separate file called lang-en_US.php you define the text like this:
<?php
// English text
define("_CORE_MOD_ADMIN","Module Administration");
define("_CORE_DELETED","deleted");
.
.
?>
Code example from page.modules.php, original code
</script>
<?php
echo "<h2>" . _("Module Administration") . "</h2>";
When the new language system is in use it will look like this:
</script> <?php echo "<h2>" . _CORE_MOD_ADMIN . "</h2>";
When a new language is added to FreePBX the translator copy the file lang-en_US.php to for example lang-sv_SE.php and change the english text to Swedish. If we use the above example the file will look like this:
<?php
// English text
define("_CORE_MOD_ADMIN","Moduladministration");
define("_CORE_DELETED","raderad");
.
.
?>
Advantages
- Easy to translate, no need to extract text as it is already extracted.
- If there are spelling errors, only the language file will be changed. No need to bump revisions
- Easy to see text that is not translated, it will turn up as _SOME_LABEL if the define is missing.
Change in code
I have done the coding in the following files:
- header.php
- config.php
header.php
In header.php there is this code doing the language checking:
// setup locale
function set_language() {
if (extension_loaded('gettext')) {
if (isset($_COOKIE['lang'])) {
setlocale(LC_ALL, $_COOKIE['lang']);
putenv("LANGUAGE=".$_COOKIE['lang']);
} else {
setlocale(LC_ALL, 'en_US');
}
bindtextdomain('amp','./i18n');
bind_textdomain_codeset('amp', 'utf8');
textdomain('amp');
}
}
Replace this code with my proposal:
// setup locale
function set_language() {
global $currentlang; // new global variable
if (isset($_COOKIE['lang'])) { // check for cookie
$currentlang = $_COOKIE['lang']; //if it is set, put that in the global variable
} else {
$currentlang = 'en_US'; // else set the language to english
}
if (file_exists("language/lang-$currentlang.php")) { // if the language exists
include_once("language/lang-$currentlang.php"); // include it
} else {
include_once("language/lang-en_US.php"); // else use the english which MUST exist!!!
}
}
config.php
Original code:
} else if (file_exists($module_file)) {
// load language info if available
if (extension_loaded('gettext')) {
if (is_dir("modules/{$module_name}/i18n")) {
bindtextdomain($module_name,"modules/{$module_name}/i18n");
bind_textdomain_codeset($module_name, 'utf8');
textdomain($module_name);
}
}
include($module_file);
} else {
// TODO: make this a showview()
echo "404 Not found";
}
My proposal:
} else if (file_exists($module_file)) {
// load language info if available
// new language structure, use it if it the language file exist
// first we test for a language directory, if it exist, try to load
// the defined language, if that fails, load english (which MUST exist!!!)
if (is_dir("modules/{$module_name}/language")) {
if (file_exists("modules/{$module_name}/language/lang-$currentlang.php")) {
include_once("modules/{$module_name}/language/lang-$currentlang.php");
} else {
include_once("modules/{$module_name}/language/lang-en_US.php");
}
}
// else use the old language style, so that we can change each module without doing a big-bang
else if (extension_loaded('gettext')) {
if (is_dir("modules/{$module_name}/i18n")) {
bindtextdomain($module_name,"modules/{$module_name}/i18n");
bind_textdomain_codeset($module_name, 'utf8');
textdomain($module_name);
}
}
include($module_file);
} else {
// TODO: make this a showview()
echo "404 Not found";
FreePBX version
The above code is from FreePBX 2.4.0beta2.2
Test the proposal
If you want to test the proposal, just follow this steps:
- Create a language folder under the admin directory, put the file lang-en_US.php here.
- Replace config.php and page.modules.php in admin folder.
- Create a folder called language in the dashboard directory, put the file lang-dash-en_US.php here, rename the file to lang-en_US.php.
- Replace the file page.index.php in the dashboard directory.
- Copy the lang-en_US.php to for example lang-de_DE.php and change some text, change language in FreePBX to German and see that it works.
Things to do
- Collect all text that is the same, enter them as _GLOBAL_XXXXX
- Create a name standard for the labels
COMMENTS
- My concern is the amount of change. The current setup does have limitations because of the gettext subsystem unable to handle multiple translation files on one page (which breaks some of the modular nature.)
- I'm also concerned about yet something new. How do other successful modular projects handle this, it would be nice to go with something proven and that some translators may be familiar with? (Any thoughts, Drupal, others?)
- One thought may be to just provide the .po files in a parallel language_pack structure to the modules. This could be maintained by translators. We could have an equivalent of module_admin for translations. I would think we could create a utility script to eliminate much of the manual comparisons from previous translation files.
- If sticking with xgettext, there is still the issue of a single .mo filed required. If we go with a language_pack_admin, we could have FreePBX rebuild the mo file by consolidating all po files, on the local system, anytime a new language pack is installed/updated. Then FreePBX could use a single translation file which removes the issues? (Again though - how are other successful projects handling this)
Attachments
- config.php (12.1 kB) -
Proposal file, put it in admin directory
, added by mickecarlsson on 01/16/08 12:05:30. - header.php (5.9 kB) -
Proposal file, put it in admin directory
, added by mickecarlsson on 01/16/08 12:06:35. - lang-en_US.php (10.8 kB) -
Proposal file, create a folder called language, put it there
, added by mickecarlsson on 01/16/08 12:07:08. - page.modules.php (33.9 kB) -
Proposal file, put it in admin directory
, added by mickecarlsson on 01/16/08 12:07:38. - page.index.php (21.5 kB) -
Proposal file, put it in modules/dashboard directory
, added by mickecarlsson on 01/16/08 12:08:06. - lang-dash-en_US.php (4.5 kB) -
Proposal file, put it in modules/dashboardlanguage directory, rename it to lang-en_US.php
, added by mickecarlsson on 01/16/08 12:08:32.
