2004-08-11 11:26:20 +00:00
< ? php
// $Id$
2004-08-21 06:42:38 +00:00
2004-08-11 11:26:20 +00:00
/**
* @ file
2004-08-21 06:42:38 +00:00
* Admin - related functions for locale . module .
2004-08-11 11:26:20 +00:00
*/
// ---------------------------------------------------------------------------------
// Language addition functionality (administration only)
/**
* Helper function to add a language
*/
function _locale_add_language ( $code , $name , $onlylanguage = TRUE ) {
db_query ( " INSERT INTO { locales_meta} (locale, name) VALUES ('%s','%s') " , $code , $name );
$result = db_query ( " SELECT lid FROM { locales_source} " );
while ( $string = db_fetch_object ( $result )) {
2006-02-27 13:22:29 +00:00
db_query ( " INSERT INTO { locales_target} (lid, locale, translation) VALUES (%d,'%s', '') " , $string -> lid , $code );
2004-08-11 11:26:20 +00:00
}
2004-08-12 18:00:11 +00:00
2004-08-11 11:26:20 +00:00
// If only the language was added, and not a PO file import triggered
// the language addition, we need to inform the user on how to start
// a translation
if ( $onlylanguage ) {
2006-08-18 12:17:00 +00:00
drupal_set_message ( t ( 'The language %locale has been created and can now be used to import a translation. More information is available in the <a href="@locale-help">help screen</a>.' , array ( '%locale' => t ( $name ), '@locale-help' => url ( 'admin/help/locale' ))));
2004-08-11 11:26:20 +00:00
}
else {
2006-08-18 12:17:00 +00:00
drupal_set_message ( t ( 'The language %locale has been created.' , array ( '%locale' => t ( $name ))));
2004-08-11 11:26:20 +00:00
}
2004-08-12 18:00:11 +00:00
2006-08-18 12:17:00 +00:00
watchdog ( 'locale' , t ( 'The %language language (%locale) has been created.' , array ( '%language' => $name , '%locale' => $code )));
2004-08-11 11:26:20 +00:00
}
/**
2006-03-17 18:35:56 +00:00
* User interface for the language management screen .
2004-08-11 11:26:20 +00:00
*/
function _locale_admin_manage_screen () {
$languages = locale_supported_languages ( TRUE , TRUE );
2005-10-07 06:11:12 +00:00
$options = array ();
2005-10-11 19:44:35 +00:00
$form [ 'name' ] = array ( '#tree' => TRUE );
2004-08-11 11:26:20 +00:00
foreach ( $languages [ 'name' ] as $key => $lang ) {
2005-10-07 06:11:12 +00:00
$options [ $key ] = '' ;
2004-08-11 11:26:20 +00:00
$status = db_fetch_object ( db_query ( " SELECT isdefault, enabled FROM { locales_meta} WHERE locale = '%s' " , $key ));
2005-10-07 06:11:12 +00:00
if ( $status -> enabled ) {
$enabled [] = $key ;
}
if ( $status -> isdefault ) {
$isdefault = $key ;
}
2004-08-11 11:26:20 +00:00
if ( $key == 'en' ) {
2006-03-17 18:35:56 +00:00
$form [ 'name' ][ 'en' ] = array ( '#value' => check_plain ( $lang ));
2004-08-11 11:26:20 +00:00
}
else {
$original = db_fetch_object ( db_query ( " SELECT COUNT(*) AS strings FROM { locales_source} " ));
$translation = db_fetch_object ( db_query ( " SELECT COUNT(*) AS translation FROM { locales_target} WHERE locale = '%s' AND translation != '' " , $key ));
2004-08-12 18:00:11 +00:00
2004-08-11 11:26:20 +00:00
$ratio = ( $original -> strings > 0 && $translation -> translation > 0 ) ? round (( $translation -> translation / $original -> strings ) * 100. , 2 ) : 0 ;
2004-08-12 18:00:11 +00:00
2006-03-17 18:35:56 +00:00
$form [ 'name' ][ $key ] = array ( '#type' => 'textfield' ,
'#default_value' => $lang ,
'#size' => 15 ,
'#maxlength' => 64 ,
);
$form [ 'translation' ][ $key ] = array ( '#value' => " $translation->translation / $original->strings ( $ratio %) " );
2004-08-11 11:26:20 +00:00
}
}
2006-03-17 18:35:56 +00:00
$form [ 'enabled' ] = array ( '#type' => 'checkboxes' ,
'#options' => $options ,
'#default_value' => $enabled ,
);
$form [ 'site_default' ] = array ( '#type' => 'radios' ,
'#options' => $options ,
'#default_value' => $isdefault ,
);
2005-10-11 19:44:35 +00:00
$form [ 'submit' ] = array ( '#type' => 'submit' , '#value' => t ( 'Save configuration' ));
2006-08-18 18:58:47 +00:00
$form [ '#base' ] = 'locale_admin_manage_screen' ;
2005-10-07 06:51:43 +00:00
2006-08-18 18:58:47 +00:00
return $form ;
2005-10-07 06:11:12 +00:00
}
2004-08-12 18:00:11 +00:00
2006-03-17 18:35:56 +00:00
/**
* Theme the locale admin manager form .
*/
2006-02-23 10:30:03 +00:00
function theme_locale_admin_manage_screen ( $form ) {
2005-10-07 06:11:12 +00:00
foreach ( $form [ 'name' ] as $key => $element ) {
2006-03-17 18:35:56 +00:00
// Do not take form control structures.
2005-10-07 06:11:12 +00:00
if ( is_array ( $element ) && element_child ( $key )) {
2006-08-10 15:42:33 +00:00
$rows [] = array ( check_plain ( $key ), drupal_render ( $form [ 'name' ][ $key ]), drupal_render ( $form [ 'enabled' ][ $key ]), drupal_render ( $form [ 'site_default' ][ $key ]), ( $key != 'en' ? drupal_render ( $form [ 'translation' ][ $key ]) : t ( 'n/a' )), ( $key != 'en' ? l ( t ( 'delete' ), 'admin/settings/locale/language/delete/' . $key ) : '' ));
2005-10-07 06:11:12 +00:00
}
}
$header = array ( array ( 'data' => t ( 'Code' )), array ( 'data' => t ( 'English name' )), array ( 'data' => t ( 'Enabled' )), array ( 'data' => t ( 'Default' )), array ( 'data' => t ( 'Translated' )), array ( 'data' => t ( 'Operations' )));
$output = theme ( 'table' , $header , $rows );
2006-08-10 15:42:33 +00:00
$output .= drupal_render ( $form );
2006-04-17 20:48:26 +00:00
2005-10-07 06:11:12 +00:00
return $output ;
2004-08-11 11:26:20 +00:00
}
/**
2006-03-17 18:35:56 +00:00
* Process locale admin manager form submissions .
2004-08-11 11:26:20 +00:00
*/
2006-03-17 18:35:56 +00:00
function _locale_admin_manage_screen_submit ( $form_id , $form_values ) {
// Save changes to existing languages.
$languages = locale_supported_languages ( FALSE , TRUE );
foreach ( $languages [ 'name' ] as $key => $value ) {
if ( $form_values [ 'site_default' ] == $key ) {
$form_values [ 'enabled' ][ $key ] = 1 ; // autoenable the default language
}
$enabled = $form_values [ 'enabled' ][ $key ] ? 1 : 0 ;
if ( $key == 'en' ) {
// Disallow name change for English locale.
db_query ( " UPDATE { locales_meta} SET isdefault = %d, enabled = %d WHERE locale = 'en' " , ( $form_values [ 'site_default' ] == $key ), $enabled );
}
else {
db_query ( " UPDATE { locales_meta} SET name = '%s', isdefault = %d, enabled = %d WHERE locale = '%s' " , $form_values [ 'name' ][ $key ], ( $form_values [ 'site_default' ] == $key ), $enabled , $key );
}
}
drupal_set_message ( t ( 'Configuration saved.' ));
2006-04-17 20:48:26 +00:00
2006-03-17 18:35:56 +00:00
// Changing the locale settings impacts the interface:
2006-08-30 08:46:17 +00:00
cache_clear_all ( '*' , 'cache_menu' , TRUE );
cache_clear_all ( '*' , 'cache_page' , TRUE );
2004-08-12 18:00:11 +00:00
2006-07-31 11:25:55 +00:00
return 'admin/settings/locale/language/overview' ;
2006-03-17 18:35:56 +00:00
}
2006-08-18 18:58:47 +00:00
function locale_add_language_form () {
2004-08-11 11:26:20 +00:00
$isocodes = _locale_prepare_iso_list ();
2005-10-07 06:11:12 +00:00
$form = array ();
2006-03-17 18:35:56 +00:00
$form [ 'language list' ] = array ( '#type' => 'fieldset' ,
'#title' => t ( 'Language list' ),
'#collapsible' => TRUE ,
);
$form [ 'language list' ][ 'langcode' ] = array ( '#type' => 'select' ,
'#title' => t ( 'Language name' ),
'#default_value' => key ( $isocodes ),
'#options' => $isocodes ,
'#description' => t ( 'Select your language here, or add it below, if you are unable to find it.' ),
);
$form [ 'language list' ][ 'submit' ] = array ( '#type' => 'submit' , '#value' => t ( 'Add language' ));
2006-08-18 18:58:47 +00:00
return $form ;
}
2006-04-17 20:48:26 +00:00
2006-08-18 18:58:47 +00:00
function locale_custom_language_form () {
2005-10-07 06:11:12 +00:00
$form = array ();
2006-03-17 18:35:56 +00:00
$form [ 'custom language' ] = array ( '#type' => 'fieldset' ,
'#title' => t ( 'Custom language' ),
'#collapsible' => TRUE ,
);
$form [ 'custom language' ][ 'langcode' ] = array ( '#type' => 'textfield' ,
'#title' => t ( 'Language code' ),
'#size' => 12 ,
'#maxlength' => 60 ,
'#required' => TRUE ,
2006-08-18 12:17:00 +00:00
'#description' => t ( " Commonly this is an <a href= \" @iso-codes \" >ISO 639 language code</a> with an optional country code for regional variants. Examples include 'en', 'en-US' and 'zh-cn'. " , array ( '@iso-codes' => 'http://www.w3.org/WAI/ER/IG/ert/iso639.htm' )),
2006-03-17 18:35:56 +00:00
);
$form [ 'custom language' ][ 'langname' ] = array ( '#type' => 'textfield' ,
'#title' => t ( 'Language name in English' ),
'#maxlength' => 64 ,
'#required' => TRUE ,
'#description' => t ( 'Name of the language. Will be available for translation in all languages.' ),
);
$form [ 'custom language' ][ 'submit' ] = array ( '#type' => 'submit' , '#value' => t ( 'Add custom language' ));
// Use the validation and submit functions of the add language form.
2006-08-18 18:58:47 +00:00
$form [ '#base' ] = 'locale_add_language_form' ;
return $form ;
}
2004-08-11 11:26:20 +00:00
2006-08-18 18:58:47 +00:00
/**
* User interface for the language addition screen .
*/
function _locale_admin_manage_add_screen () {
$output = drupal_get_form ( 'locale_add_language_form' );
$output .= drupal_get_form ( 'locale_custom_language_form' );
2004-08-11 11:26:20 +00:00
return $output ;
}
/**
2006-03-17 18:35:56 +00:00
* Validate the language addition form .
*/
function locale_add_language_form_validate ( $form_id , $form_values ) {
if ( $duplicate = db_num_rows ( db_query ( " SELECT locale FROM { locales_meta} WHERE locale = '%s' " , $form_values [ 'langcode' ])) != 0 ) {
2006-08-18 12:17:00 +00:00
form_set_error ( t ( 'The language %language (%code) already exists.' , array ( '%language' => $form_values [ 'langname' ], '%code' => $form_values [ 'langcode' ])));
2006-03-17 18:35:56 +00:00
}
if ( ! isset ( $form_values [ 'langname' ])) {
$isocodes = _locale_get_iso639_list ();
if ( ! isset ( $isocodes [ $form_values [ 'langcode' ]])) {
form_set_error ( 'langcode' , t ( 'Invalid language code.' ));
}
}
}
/**
* Process the language addition form submission .
*/
function locale_add_language_form_submit ( $form_id , $form_values ) {
if ( isset ( $form_values [ 'langname' ])) {
// Custom language form.
_locale_add_language ( $form_values [ 'langcode' ], $form_values [ 'langname' ]);
}
else {
$isocodes = _locale_get_iso639_list ();
_locale_add_language ( $form_values [ 'langcode' ], $isocodes [ $form_values [ 'langcode' ]][ 0 ]);
}
2006-04-17 20:48:26 +00:00
2006-08-07 15:04:16 +00:00
return 'admin/settings/locale' ;
2006-03-17 18:35:56 +00:00
}
/**
* User interface for the translation import screen .
2004-08-11 11:26:20 +00:00
*/
2006-08-18 18:58:47 +00:00
function _locale_admin_import () {
2004-08-11 11:26:20 +00:00
$languages = locale_supported_languages ( FALSE , TRUE );
2005-10-07 06:11:12 +00:00
$languages = array_map ( 't' , $languages [ 'name' ]);
2004-08-11 11:26:20 +00:00
unset ( $languages [ 'en' ]);
2004-08-12 18:00:11 +00:00
2004-08-11 11:26:20 +00:00
if ( ! count ( $languages )) {
2004-08-21 21:13:29 +00:00
$languages = _locale_prepare_iso_list ();
2004-08-11 11:26:20 +00:00
}
else {
$languages = array (
t ( 'Already added languages' ) => $languages ,
t ( 'Languages not yet added' ) => _locale_prepare_iso_list ()
);
}
2004-08-21 21:13:29 +00:00
2005-10-07 06:11:12 +00:00
$form = array ();
2006-03-17 18:35:56 +00:00
$form [ 'import' ] = array ( '#type' => 'fieldset' ,
2006-04-30 00:28:53 +00:00
'#title' => t ( 'Import translation' ),
2006-03-17 18:35:56 +00:00
);
$form [ 'import' ][ 'file' ] = array ( '#type' => 'file' ,
'#title' => t ( 'Language file' ),
'#size' => 50 ,
'#description' => t ( 'A gettext Portable Object (.po) file.' ),
);
$form [ 'import' ][ 'langcode' ] = array ( '#type' => 'select' ,
'#title' => t ( 'Import into' ),
'#options' => $languages ,
'#description' => t ( 'Choose the language you want to add strings into. If you choose a language which is not yet set up, then it will be added.' ),
);
$form [ 'import' ][ 'mode' ] = array ( '#type' => 'radios' ,
'#title' => t ( 'Mode' ),
'#default_value' => 'overwrite' ,
'#options' => array ( 'overwrite' => t ( 'Strings in the uploaded file replace existing ones, new ones are added' ), 'keep' => t ( 'Existing strings are kept, only new strings are added' )),
);
$form [ 'import' ][ 'submit' ] = array ( '#type' => 'submit' , '#value' => t ( 'Import' ));
2005-10-11 19:44:35 +00:00
$form [ '#attributes' ][ 'enctype' ] = 'multipart/form-data' ;
2005-10-07 06:11:12 +00:00
2006-08-18 18:58:47 +00:00
return $form ;
2004-08-11 11:26:20 +00:00
}
2006-03-17 18:35:56 +00:00
/**
* Process the locale import form submission .
*/
function _locale_admin_import_submit ( $form_id , $form_values ) {
// Add language, if not yet supported
$languages = locale_supported_languages ( TRUE , TRUE );
if ( ! isset ( $languages [ 'name' ][ $form_values [ 'langcode' ]])) {
$isocodes = _locale_get_iso639_list ();
_locale_add_language ( $form_values [ 'langcode' ], $isocodes [ $form_values [ 'langcode' ]][ 0 ], FALSE );
}
// Now import strings into the language
$file = file_check_upload ( 'file' );
if ( $ret = _locale_import_po ( $file , $form_values [ 'langcode' ], $form_values [ 'mode' ]) == FALSE ) {
2006-08-18 12:17:00 +00:00
$message = t ( 'The translation import of %filename failed.' , array ( '%filename' => $file -> filename ));
2006-03-17 18:35:56 +00:00
drupal_set_message ( $message , 'error' );
watchdog ( 'locale' , $message , WATCHDOG_ERROR );
}
2006-08-07 15:04:16 +00:00
return 'admin/settings/locale' ;
2006-03-17 18:35:56 +00:00
}
2006-08-18 18:58:47 +00:00
function _locale_export_po_form () {
$form [ 'export' ] = array ( '#type' => 'fieldset' ,
'#title' => t ( 'Export translation' ),
'#collapsible' => TRUE ,
);
$form [ 'export' ][ 'langcode' ] = array ( '#type' => 'select' ,
'#title' => t ( 'Language name' ),
'#options' => $languages ,
'#description' => t ( 'Select the language you would like to export in gettext Portable Object (.po) format.' ),
);
$form [ 'export' ][ 'submit' ] = array ( '#type' => 'submit' , '#value' => t ( 'Export' ));
return $form ;
}
function _locale_export_pot_form () {
// Complete template export of the strings
$form [ 'export' ] = array ( '#type' => 'fieldset' ,
'#title' => t ( 'Export template' ),
'#collapsible' => TRUE ,
'#description' => t ( 'Generate a gettext Portable Object Template (.pot) file with all the interface strings from the Drupal locale database.' ),
);
$form [ 'export' ][ 'submit' ] = array ( '#type' => 'submit' , '#value' => t ( 'Export' ));
$form [ '#base' ] = '_locale_export_po_form' ;
return $form ;
}
2006-03-17 18:35:56 +00:00
/**
* User interface for the translation export screen
*/
function _locale_admin_export_screen () {
$languages = locale_supported_languages ( FALSE , TRUE );
$languages = array_map ( 't' , $languages [ 'name' ]);
unset ( $languages [ 'en' ]);
2006-08-18 18:58:47 +00:00
$output = '' ;
2006-03-17 18:35:56 +00:00
// Offer language specific export if any language is set up
if ( count ( $languages )) {
2006-08-18 18:58:47 +00:00
$output = drupal_get_form ( '_locale_export_po_form' );
2006-03-17 18:35:56 +00:00
}
2006-08-18 18:58:47 +00:00
$output .= drupal_get_form ( '_locale_export_pot_form' );
2006-03-17 18:35:56 +00:00
return $output ;
}
/**
* Process a locale export form submissions .
*/
2006-08-18 18:58:47 +00:00
function _locale_export_po_form_submit ( $form_id , $form_values ) {
2006-03-17 18:35:56 +00:00
_locale_export_po ( $form_values [ 'langcode' ]);
}
/**
* User interface for the string search screen
*/
function _locale_string_seek_form () {
// Get *all* languages set up
$languages = locale_supported_languages ( FALSE , TRUE );
asort ( $languages [ 'name' ]); unset ( $languages [ 'name' ][ 'en' ]);
$languages [ 'name' ] = array_map ( 'check_plain' , $languages [ 'name' ]);
// Present edit form preserving previous user settings
$query = _locale_string_seek_query ();
$form = array ();
$form [ 'search' ] = array ( '#type' => 'fieldset' ,
'#title' => t ( 'Search' ),
);
$form [ 'search' ][ 'string' ] = array ( '#type' => 'textfield' ,
'#title' => t ( 'Strings to search for' ),
'#default_value' => $query -> string ,
'#size' => 30 ,
'#maxlength' => 30 ,
'#description' => t ( 'Leave blank to show all strings. The search is case sensitive.' ),
);
$form [ 'search' ][ 'language' ] = array ( '#type' => 'radios' ,
'#title' => t ( 'Language' ),
'#default_value' => ( $query -> language ? $query -> language : 'all' ),
'#options' => array_merge ( array ( 'all' => t ( 'All languages' ), 'en' => t ( 'English (provided by Drupal)' )), $languages [ 'name' ]),
);
$form [ 'search' ][ 'searchin' ] = array ( '#type' => 'radios' ,
'#title' => t ( 'Search in' ),
'#default_value' => ( $query -> searchin ? $query -> searchin : 'all' ),
'#options' => array ( 'all' => t ( 'All strings in that language' ), 'translated' => t ( 'Only translated strings' ), 'untranslated' => t ( 'Only untranslated strings' )),
);
$form [ 'search' ][ 'submit' ] = array ( '#type' => 'submit' , '#value' => t ( 'Search' ));
$form [ '#redirect' ] = FALSE ;
2006-08-18 18:58:47 +00:00
return $form ;
2006-03-17 18:35:56 +00:00
}
/**
* User interface for string editing .
*/
function _locale_string_edit ( $lid ) {
$languages = locale_supported_languages ( FALSE , TRUE );
unset ( $languages [ 'name' ][ 'en' ]);
$result = db_query ( 'SELECT DISTINCT s.source, t.translation, t.locale FROM {locales_source} s INNER JOIN {locales_target} t ON s.lid = t.lid WHERE s.lid = %d' , $lid );
$form = array ();
2006-05-16 09:22:36 +00:00
$form [ 'translations' ] = array ( '#tree' => TRUE );
2006-03-17 18:35:56 +00:00
while ( $translation = db_fetch_object ( $result )) {
$orig = $translation -> source ;
// Approximate the number of rows in a textfield with a maximum of 10.
$rows = min ( ceil ( str_word_count ( $orig ) / 12 ), 10 );
2006-04-17 20:48:26 +00:00
2006-05-16 09:22:36 +00:00
$form [ 'translations' ][ $translation -> locale ] = array (
'#type' => 'textarea' ,
2006-03-17 18:35:56 +00:00
'#title' => $languages [ 'name' ][ $translation -> locale ],
'#default_value' => $translation -> translation ,
'#rows' => $rows ,
);
unset ( $languages [ 'name' ][ $translation -> locale ]);
}
// Handle erroneous lid.
if ( ! isset ( $orig )){
drupal_set_message ( t ( 'String not found.' ));
2006-07-31 11:25:55 +00:00
drupal_goto ( 'admin/settings/locale/string/search' );
2006-03-17 18:35:56 +00:00
}
// Add original text. Assign negative weight so that it floats to the top.
$form [ 'item' ] = array ( '#type' => 'item' ,
'#title' => t ( 'Original text' ),
'#value' => check_plain ( wordwrap ( $orig , 0 )),
'#weight' => - 1 ,
);
foreach ( $languages [ 'name' ] as $key => $lang ) {
2006-05-16 09:22:36 +00:00
$form [ 'translations' ][ $key ] = array (
'#type' => 'textarea' ,
2006-03-17 18:35:56 +00:00
'#title' => $lang ,
'#rows' => $rows ,
);
}
$form [ 'lid' ] = array ( '#type' => 'value' , '#value' => $lid );
$form [ 'submit' ] = array ( '#type' => 'submit' , '#value' => t ( 'Save translations' ));
2006-08-18 18:58:47 +00:00
return $form ;
2006-03-17 18:35:56 +00:00
}
/**
* Process string editing form submissions .
* Saves all translations of one string submitted from a form .
*/
function _locale_string_edit_submit ( $form_id , $form_values ) {
$lid = $form_values [ 'lid' ];
2006-05-16 09:22:36 +00:00
foreach ( $form_values [ 'translations' ] as $key => $value ) {
2006-04-14 13:43:18 +00:00
$value = filter_xss_admin ( $value );
2006-03-17 18:35:56 +00:00
$trans = db_fetch_object ( db_query ( " SELECT translation FROM { locales_target} WHERE lid = %d AND locale = '%s' " , $lid , $key ));
if ( isset ( $trans -> translation )) {
db_query ( " UPDATE { locales_target} SET translation = '%s' WHERE lid = %d AND locale = '%s' " , $value , $lid , $key );
}
else {
2006-05-16 09:22:36 +00:00
db_query ( " INSERT INTO { locales_target} (lid, translation, locale) VALUES (%d, '%s', '%s') " , $lid , $value , $key );
2006-03-17 18:35:56 +00:00
}
}
drupal_set_message ( t ( 'The string has been saved.' ));
// Refresh the locale cache.
locale_refresh_cache ();
// Rebuild the menu, strings may have changed.
menu_rebuild ();
2006-07-31 11:25:55 +00:00
return 'admin/settings/locale/string/search' ;
2006-03-17 18:35:56 +00:00
}
/**
* Delete a language string .
*/
function _locale_string_delete ( $lid ) {
db_query ( 'DELETE FROM {locales_source} WHERE lid = %d' , $lid );
db_query ( 'DELETE FROM {locales_target} WHERE lid = %d' , $lid );
locale_refresh_cache ();
drupal_set_message ( t ( 'The string has been removed.' ));
2006-04-17 20:48:26 +00:00
2006-07-31 11:25:55 +00:00
drupal_goto ( 'admin/settings/locale/string/search' );
2006-03-17 18:35:56 +00:00
}
2004-08-11 11:26:20 +00:00
/**
* Parses Gettext Portable Object file information and inserts into database
*
2006-09-01 05:38:40 +00:00
* @ param $file
* Drupal file object corresponding to the PO file to import
* @ param $lang
* Language code
* @ param $mode
* Should existing translations be replaced ( 'overwrite' or 'keep' )
2004-08-11 11:26:20 +00:00
*/
function _locale_import_po ( $file , $lang , $mode ) {
2004-08-21 21:13:29 +00:00
// If not in 'safe mode', increase the maximum execution time:
if ( ! ini_get ( 'safe_mode' )) {
set_time_limit ( 240 );
2004-09-08 15:38:26 +00:00
}
2004-08-21 21:13:29 +00:00
2004-08-11 11:26:20 +00:00
// Check if we have the language already in the database
if ( ! db_fetch_object ( db_query ( " SELECT locale FROM { locales_meta} WHERE locale = '%s' " , $lang ))) {
2005-05-05 22:22:46 +00:00
drupal_set_message ( t ( 'The language selected for import is not supported.' ), 'error' );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
2004-08-12 18:00:11 +00:00
2006-02-05 15:42:56 +00:00
// Get strings from file (returns on failure after a partial import, or on success)
2006-09-01 05:38:40 +00:00
$status = _locale_import_read_po ( 'db-store' , $file , $mode , $lang );
2006-06-22 12:23:39 +00:00
if ( $status === FALSE ) {
// error messages are set in _locale_import_read_po
return FALSE ;
}
2004-08-12 18:00:11 +00:00
2006-02-05 15:42:56 +00:00
// Get status information on import process
2006-09-01 05:38:40 +00:00
list ( $headerdone , $additions , $updates ) = _locale_import_one_string ( 'db-report' );
2004-08-12 18:00:11 +00:00
2006-02-05 15:42:56 +00:00
if ( ! $headerdone ) {
2006-08-18 12:17:00 +00:00
drupal_set_message ( t ( 'The translation file %filename appears to have a missing or malformed header.' , array ( '%filename' => $file -> filename )), 'error' );
2004-08-11 11:26:20 +00:00
}
2004-08-12 18:00:11 +00:00
2004-10-06 12:18:03 +00:00
// rebuild locale cache
2006-08-30 08:46:17 +00:00
cache_clear_all ( " locale: $lang " , 'cache' );
2004-10-06 12:18:03 +00:00
// rebuild the menu, strings may have changed
2004-10-05 20:09:47 +00:00
menu_rebuild ();
2004-10-06 12:18:03 +00:00
2005-05-05 22:22:46 +00:00
drupal_set_message ( t ( 'The translation was successfully imported. There are %number newly created translated strings and %update strings were updated.' , array ( '%number' => $additions , '%update' => $updates )));
2006-08-18 12:17:00 +00:00
watchdog ( 'locale' , t ( 'Imported %file into %locale: %number new strings added and %update updated.' , array ( '%file' => $file -> filename , '%locale' => $lang , '%number' => $additions , '%update' => $updates )));
2004-08-11 11:26:20 +00:00
return TRUE ;
}
/**
* Parses Gettext Portable Object file into an array
*
2006-09-01 05:38:40 +00:00
* @ param $op
* Storage operation type : db - store or mem - store
* @ param $file
* Drupal file object corresponding to the PO file to import
* @ param $mode
* Should existing translations be replaced ( 'overwrite' or 'keep' )
* @ param $lang
* Language code
2004-08-11 11:26:20 +00:00
* @ author Jacobo Tarrio
*/
2006-09-01 05:38:40 +00:00
function _locale_import_read_po ( $op , $file , $mode = NULL , $lang = NULL ) {
2004-08-11 11:26:20 +00:00
2006-02-05 15:42:56 +00:00
$fd = fopen ( $file -> filepath , " rb " ); // File will get closed by PHP on return
2004-08-11 11:26:20 +00:00
if ( ! $fd ) {
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation import failed, because the file %filename could not be read.' , $file );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
$context = " COMMENT " ; // Parser context: COMMENT, MSGID, MSGID_PLURAL, MSGSTR and MSGSTR_ARR
$current = array (); // Current entry being read
$plural = 0 ; // Current plural form
2006-02-05 15:42:56 +00:00
$lineno = 0 ; // Current line
2004-08-11 11:26:20 +00:00
2006-02-05 15:42:56 +00:00
while ( ! feof ( $fd )) {
$line = fgets ( $fd , 10 * 1024 ); // A line should not be this long
2004-08-11 11:26:20 +00:00
$lineno ++ ;
2006-02-05 15:42:56 +00:00
$line = trim ( strtr ( $line , array ( " \\ \n " => " " )));
2004-08-11 11:26:20 +00:00
if ( ! strncmp ( " # " , $line , 1 )) { // A comment
if ( $context == " COMMENT " ) { // Already in comment context: add
$current [ " # " ][] = substr ( $line , 1 );
}
elseif (( $context == " MSGSTR " ) || ( $context == " MSGSTR_ARR " )) { // End current entry, start a new one
2006-09-01 05:38:40 +00:00
_locale_import_one_string ( $op , $current , $mode , $lang );
2004-08-11 11:26:20 +00:00
$current = array ();
$current [ " # " ][] = substr ( $line , 1 );
$context = " COMMENT " ;
}
else { // Parse error
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename contains an error: "msgstr" was expected but not found on line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
}
elseif ( ! strncmp ( " msgid_plural " , $line , 12 )) {
if ( $context != " MSGID " ) { // Must be plural form for current entry
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename contains an error: "msgid_plural" was expected but not found on line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
$line = trim ( substr ( $line , 12 ));
$quoted = _locale_import_parse_quoted ( $line );
2006-07-05 11:45:51 +00:00
if ( $quoted === FALSE ) {
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename contains a syntax error on line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
$current [ " msgid " ] = $current [ " msgid " ] . " \0 " . $quoted ;
2004-08-12 18:00:11 +00:00
$context = " MSGID_PLURAL " ;
2004-08-11 11:26:20 +00:00
}
elseif ( ! strncmp ( " msgid " , $line , 5 )) {
if ( $context == " MSGSTR " ) { // End current entry, start a new one
2006-09-01 05:38:40 +00:00
_locale_import_one_string ( $op , $current , $mode , $lang );
2004-08-11 11:26:20 +00:00
$current = array ();
}
elseif ( $context == " MSGID " ) { // Already in this context? Parse error
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename contains an error: "msgid" is unexpected on line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
$line = trim ( substr ( $line , 5 ));
$quoted = _locale_import_parse_quoted ( $line );
2006-07-05 11:45:51 +00:00
if ( $quoted === FALSE ) {
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename contains a syntax error on line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
$current [ " msgid " ] = $quoted ;
$context = " MSGID " ;
}
elseif ( ! strncmp ( " msgstr[ " , $line , 7 )) {
if (( $context != " MSGID " ) && ( $context != " MSGID_PLURAL " ) && ( $context != " MSGSTR_ARR " )) { // Must come after msgid, msgid_plural, or msgstr[]
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename contains an error: "msgstr[]" is unexpected on line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
2006-07-05 11:45:51 +00:00
if ( strpos ( $line , " ] " ) === FALSE ) {
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename contains a syntax error on line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
$frombracket = strstr ( $line , " [ " );
$plural = substr ( $frombracket , 1 , strpos ( $frombracket , " ] " ) - 1 );
$line = trim ( strstr ( $line , " " ));
$quoted = _locale_import_parse_quoted ( $line );
2006-07-05 11:45:51 +00:00
if ( $quoted === FALSE ) {
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename contains a syntax error on line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
$current [ " msgstr " ][ $plural ] = $quoted ;
$context = " MSGSTR_ARR " ;
}
elseif ( ! strncmp ( " msgstr " , $line , 6 )) {
if ( $context != " MSGID " ) { // Should come just after a msgid block
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename contains an error: "msgstr" is unexpected on line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
$line = trim ( substr ( $line , 6 ));
$quoted = _locale_import_parse_quoted ( $line );
2006-07-05 11:45:51 +00:00
if ( $quoted === FALSE ) {
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename contains a syntax error on line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
$current [ " msgstr " ] = $quoted ;
$context = " MSGSTR " ;
}
elseif ( $line != " " ) {
$quoted = _locale_import_parse_quoted ( $line );
2006-07-05 11:45:51 +00:00
if ( $quoted === FALSE ) {
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename contains a syntax error on line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
if (( $context == " MSGID " ) || ( $context == " MSGID_PLURAL " )) {
$current [ " msgid " ] .= $quoted ;
}
elseif ( $context == " MSGSTR " ) {
$current [ " msgstr " ] .= $quoted ;
}
elseif ( $context == " MSGSTR_ARR " ) {
$current [ " msgstr " ][ $plural ] .= $quoted ;
}
else {
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename contains an error: there is an unexpected string on line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
}
}
// End of PO file, flush last entry
if (( $context == " MSGSTR " ) || ( $context == " MSGSTR_ARR " )) {
2006-09-01 05:38:40 +00:00
_locale_import_one_string ( $op , $current , $mode , $lang );
2004-08-11 11:26:20 +00:00
}
elseif ( $context != " COMMENT " ) {
2006-09-01 05:38:40 +00:00
_locale_import_message ( 'The translation file %filename ended unexpectedly at line %line.' , $file , $lineno );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
2006-02-05 15:42:56 +00:00
}
2006-09-01 05:38:40 +00:00
/**
* Sets an error message occured during locale file parsing .
*
* @ param $message
* The message to be translated
* @ param $file
* Drupal file object corresponding to the PO file to import
* @ param $lineno
* An optional line number argument
*/
function _locale_import_message ( $message , $file , $lineno = NULL ) {
$vars = array ( '%filename' => $file -> filename );
if ( isset ( $lineno )) {
$vars [ '%line' ] = $lineno ;
}
$t = function_exists ( 'installer_main' ) ? 'st' : 't' ;
drupal_set_message ( $t ( $message , $vars ), 'error' );
}
2006-02-05 15:42:56 +00:00
/**
* Imports a string into the database
*
2006-09-01 05:38:40 +00:00
* @ param $op
* Operation to perform : 'db-store' , 'db-report' , 'mem-store' or 'mem-report'
* @ param $value
* Details of the string stored
* @ param $mode
* Should existing translations be replaced ( 'overwrite' or 'keep' )
* @ param $lang
* Language to store the string in
2006-02-05 15:42:56 +00:00
* @ author Jacobo Tarrio
*/
2006-09-01 05:38:40 +00:00
function _locale_import_one_string ( $op , $value = NULL , $mode = NULL , $lang = NULL ) {
2006-02-05 15:42:56 +00:00
static $additions = 0 ;
static $updates = 0 ;
static $headerdone = FALSE ;
2006-09-01 05:38:40 +00:00
static $strings = array ();
switch ( $op ) {
// Return stored strings
case 'mem-report' :
return $strings ;
// Store string in memory (only supports single strings)
case 'mem-store' :
$strings [ $value [ 'msgid' ]] = $value [ 'msgstr' ];
return ;
// Called at end of import to inform the user
case 'db-report' :
return array ( $headerdone , $additions , $updates );
// Store the string we got in the database
case 'db-store' :
// We got header information
if ( $value [ 'msgid' ] == '' ) {
$hdr = _locale_import_parse_header ( $value [ 'msgstr' ]);
// Get the plural formula
if ( $hdr [ " Plural-Forms " ] && $p = _locale_import_parse_plural_forms ( $hdr [ " Plural-Forms " ], $file -> filename )) {
list ( $nplurals , $plural ) = $p ;
db_query ( " UPDATE { locales_meta} SET plurals = %d, formula = '%s' WHERE locale = '%s' " , $nplurals , $plural , $lang );
2006-02-05 15:42:56 +00:00
}
2006-09-01 05:38:40 +00:00
else {
db_query ( " UPDATE { locales_meta} SET plurals = %d, formula = '%s' WHERE locale = '%s' " , 0 , '' , $lang );
}
$headerdone = TRUE ;
}
// Some real string to import
else {
$comments = filter_xss_admin ( _locale_import_shorten_comments ( $value [ '#' ]));
// Handle a translation for some plural string
if ( strpos ( $value [ 'msgid' ], " \0 " )) {
$english = explode ( " \0 " , $value [ 'msgid' ], 2 );
$entries = array_keys ( $value [ 'msgstr' ]);
for ( $i = 3 ; $i <= count ( $entries ); $i ++ ) {
$english [] = $english [ 1 ];
}
$translation = array_map ( '_locale_import_append_plural' , $value [ 'msgstr' ], $entries );
$english = array_map ( '_locale_import_append_plural' , $english , $entries );
foreach ( $translation as $key => $trans ) {
if ( $key == 0 ) {
$plid = 0 ;
2006-02-05 15:42:56 +00:00
}
2006-09-01 05:38:40 +00:00
$loc = db_fetch_object ( db_query ( " SELECT lid FROM { locales_source} WHERE source = '%s' " , $english [ $key ]));
if ( $loc -> lid ) { // a string exists
$lid = $loc -> lid ;
// update location field
db_query ( " UPDATE { locales_source} SET location = '%s' WHERE lid = %d " , $comments , $lid );
$trans2 = db_fetch_object ( db_query ( " SELECT lid, translation, plid, plural FROM { locales_target} WHERE lid = %d AND locale = '%s' " , $lid , $lang ));
if ( ! $trans2 -> lid ) { // no translation in current language
db_query ( " INSERT INTO { locales_target} (lid, locale, translation, plid, plural) VALUES (%d, '%s', '%s', %d, %d) " , $lid , $lang , filter_xss_admin ( $trans ), $plid , $key );
$additions ++ ;
} // translation exists
else if ( $mode == 'overwrite' || $trans2 -> translation == '' ) {
db_query ( " UPDATE { locales_target} SET translation = '%s', plid = %d, plural = %d WHERE locale = '%s' AND lid = %d " , filter_xss_admin ( $trans ), $plid , $key , $lang , $lid );
if ( $trans2 -> translation == '' ) {
$additions ++ ;
}
else {
$updates ++ ;
}
}
2006-02-05 15:42:56 +00:00
}
2006-09-01 05:38:40 +00:00
else { // no string
db_query ( " INSERT INTO { locales_source} (location, source) VALUES ('%s', '%s') " , $comments , filter_xss_admin ( $english [ $key ]));
$loc = db_fetch_object ( db_query ( " SELECT lid FROM { locales_source} WHERE source = '%s' " , $english [ $key ]));
$lid = $loc -> lid ;
db_query ( " INSERT INTO { locales_target} (lid, locale, translation, plid, plural) VALUES (%d, '%s', '%s', %d, %d) " , $lid , $lang , filter_xss_admin ( $trans ), $plid , $key );
if ( $trans != '' ) {
$additions ++ ;
}
}
$plid = $lid ;
2006-02-05 15:42:56 +00:00
}
}
2006-09-01 05:38:40 +00:00
// A simple translation
else {
$english = $value [ 'msgid' ];
$translation = $value [ 'msgstr' ];
$loc = db_fetch_object ( db_query ( " SELECT lid FROM { locales_source} WHERE source = '%s' " , $english ));
if ( $loc -> lid ) { // a string exists
$lid = $loc -> lid ;
// update location field
db_query ( " UPDATE { locales_source} SET location = '%s' WHERE source = '%s' " , $comments , $english );
$trans = db_fetch_object ( db_query ( " SELECT lid, translation FROM { locales_target} WHERE lid = %d AND locale = '%s' " , $lid , $lang ));
if ( ! $trans -> lid ) { // no translation in current language
db_query ( " INSERT INTO { locales_target} (lid, locale, translation) VALUES (%d, '%s', '%s') " , $lid , $lang , filter_xss_admin ( $translation ));
$additions ++ ;
} // translation exists
else if ( $mode == 'overwrite' ) { //overwrite in any case
db_query ( " UPDATE { locales_target} SET translation = '%s' WHERE locale = '%s' AND lid = %d " , filter_xss_admin ( $translation ), $lang , $lid );
if ( $trans -> translation == '' ) {
$additions ++ ;
}
else {
$updates ++ ;
}
} // overwrite if empty string
else if ( $trans -> translation == '' ) {
db_query ( " UPDATE { locales_target} SET translation = '%s' WHERE locale = '%s' AND lid = %d " , $translation , $lang , $lid );
$additions ++ ;
}
2006-02-05 15:42:56 +00:00
}
2006-09-01 05:38:40 +00:00
else { // no string
db_query ( " INSERT INTO { locales_source} (location, source) VALUES ('%s', '%s') " , $comments , $english );
$loc = db_fetch_object ( db_query ( " SELECT lid FROM { locales_source} WHERE source = '%s' " , $english ));
$lid = $loc -> lid ;
db_query ( " INSERT INTO { locales_target} (lid, locale, translation) VALUES (%d, '%s', '%s') " , $lid , $lang , filter_xss_admin ( $translation ));
if ( $translation != '' ) {
$additions ++ ;
}
2006-02-05 15:42:56 +00:00
}
}
}
2006-09-01 05:38:40 +00:00
} // end of db-store operation
2004-08-11 11:26:20 +00:00
}
/**
* Parses a Gettext Portable Object file header
*
* @ param $header A string containing the complete header
* @ return An associative array of key - value pairs
* @ author Jacobo Tarrio
*/
function _locale_import_parse_header ( $header ) {
$hdr = array ();
$lines = explode ( " \n " , $header );
foreach ( $lines as $line ) {
$line = trim ( $line );
if ( $line ) {
list ( $tag , $contents ) = explode ( " : " , $line , 2 );
$hdr [ trim ( $tag )] = trim ( $contents );
}
}
return $hdr ;
}
/**
* Parses a Plural - Forms entry from a Gettext Portable Object file header
*
2006-09-01 05:38:40 +00:00
* @ param $pluralforms
* A string containing the Plural - Forms entry
* @ param $filename
* A string containing the filename
* @ return
* An array containing the number of plurals and a
* formula in PHP for computing the plural form
2004-08-11 11:26:20 +00:00
* @ author Jacobo Tarrio
*/
2004-10-26 17:20:58 +00:00
function _locale_import_parse_plural_forms ( $pluralforms , $filename ) {
2004-08-11 11:26:20 +00:00
// First, delete all whitespace
$pluralforms = strtr ( $pluralforms , array ( " " => " " , " \t " => " " ));
// Select the parts that define nplurals and plural
$nplurals = strstr ( $pluralforms , " nplurals= " );
if ( strpos ( $nplurals , " ; " )) {
$nplurals = substr ( $nplurals , 9 , strpos ( $nplurals , " ; " ) - 9 );
}
else {
return FALSE ;
}
$plural = strstr ( $pluralforms , " plural= " );
if ( strpos ( $plural , " ; " )) {
$plural = substr ( $plural , 7 , strpos ( $plural , " ; " ) - 7 );
}
else {
return FALSE ;
}
// Get PHP version of the plural formula
$plural = _locale_import_parse_arithmetic ( $plural );
2006-07-06 14:41:37 +00:00
if ( $plural !== FALSE ) {
2004-08-11 11:26:20 +00:00
return array ( $nplurals , $plural );
}
else {
2005-05-05 22:22:46 +00:00
drupal_set_message ( t ( 'The translation file %filename contains an error: the plural formula could not be parsed.' , array ( '%filename' => theme ( 'placeholder' , $filename ))), 'error' );
2004-08-11 11:26:20 +00:00
return FALSE ;
}
}
/**
* Parses and sanitizes an arithmetic formula into a PHP expression
*
* While parsing , we ensure , that the operators have the right
* precedence and associativity .
*
* @ param $string A string containing the arithmetic formula
* @ return The PHP version of the formula
* @ author Jacobo Tarrio
*/
function _locale_import_parse_arithmetic ( $string ) {
// Operator precedence table
$prec = array ( " ( " => - 1 , " ) " => - 1 , " ? " => 1 , " : " => 1 , " || " => 3 , " && " => 4 , " == " => 5 , " != " => 5 , " < " => 6 , " > " => 6 , " <= " => 6 , " >= " => 6 , " + " => 7 , " - " => 7 , " * " => 8 , " / " => 8 , " % " => 8 );
// Right associativity
$rasc = array ( " ? " => 1 , " : " => 1 );
$tokens = _locale_import_tokenize_formula ( $string );
// Parse by converting into infix notation then back into postfix
$opstk = array ();
$elstk = array ();
foreach ( $tokens as $token ) {
$ctok = $token ;
// Numbers and the $n variable are simply pushed into $elarr
if ( is_numeric ( $token )) {
$elstk [] = $ctok ;
}
elseif ( $ctok == " n " ) {
$elstk [] = '$n' ;
}
elseif ( $ctok == " ( " ) {
$opstk [] = $ctok ;
}
elseif ( $ctok == " ) " ) {
$topop = array_pop ( $opstk );
while (( $topop != NULL ) && ( $topop != " ( " )) {
$elstk [] = $topop ;
$topop = array_pop ( $opstk );
}
}
elseif ( $prec [ $ctok ]) {
// If it's an operator, then pop from $oparr into $elarr until the
// precedence in $oparr is less than current, then push into $oparr
$topop = array_pop ( $opstk );
while (( $topop != NULL ) && ( $prec [ $topop ] >= $prec [ $ctok ]) && ! (( $prec [ $topop ] == $prec [ $ctok ]) && $rasc [ $topop ] && $rasc [ $ctok ])) {
$elstk [] = $topop ;
$topop = array_pop ( $opstk );
}
if ( $topop ) {
$opstk [] = $topop ; // Return element to top
}
$opstk [] = $ctok ; // Parentheses are not needed
}
else {
2006-07-05 11:45:51 +00:00
return FALSE ;
2004-08-11 11:26:20 +00:00
}
}
// Flush operator stack
$topop = array_pop ( $opstk );
while ( $topop != NULL ) {
$elstk [] = $topop ;
$topop = array_pop ( $opstk );
}
// Now extract formula from stack
$prevsize = count ( $elstk ) + 1 ;
while ( count ( $elstk ) < $prevsize ) {
$prevsize = count ( $elstk );
for ( $i = 2 ; $i < count ( $elstk ); $i ++ ) {
$op = $elstk [ $i ];
if ( $prec [ $op ]) {
$f = " " ;
if ( $op == " : " ) {
$f = $elstk [ $i - 2 ] . " ): " . $elstk [ $i - 1 ] . " ) " ;
}
elseif ( $op == " ? " ) {
$f = " ( " . $elstk [ $i - 2 ] . " ?( " . $elstk [ $i - 1 ];
}
else {
$f = " ( " . $elstk [ $i - 2 ] . $op . $elstk [ $i - 1 ] . " ) " ;
}
array_splice ( $elstk , $i - 2 , 3 , $f );
break ;
}
}
}
// If only one element is left, the number of operators is appropriate
if ( count ( $elstk ) == 1 ) {
return $elstk [ 0 ];
}
else {
return FALSE ;
}
}
/**
* Backward compatible implementation of token_get_all () for formula parsing
*
* @ param $string A string containing the arithmetic formula
* @ return The PHP version of the formula
* @ author Gerhard Killesreiter
*/
function _locale_import_tokenize_formula ( $formula ) {
$formula = str_replace ( " " , " " , $formula );
$tokens = array ();
for ( $i = 0 ; $i < strlen ( $formula ); $i ++ ) {
2006-01-15 07:14:14 +00:00
if ( is_numeric ( $formula [ $i ])) {
$num = $formula [ $i ];
2004-08-11 11:26:20 +00:00
$j = $i + 1 ;
2006-01-15 07:14:14 +00:00
while ( $j < strlen ( $formula ) && is_numeric ( $formula [ $j ])) {
$num .= $formula [ $j ];
2004-08-11 11:26:20 +00:00
$j ++ ;
}
$i = $j - 1 ;
$tokens [] = $num ;
}
2006-01-15 07:14:14 +00:00
elseif ( $pos = strpos ( " =<>!&| " , $formula [ $i ])) { // We won't have a space
$next = $formula [ $i + 1 ];
2004-08-11 11:26:20 +00:00
switch ( $pos ) {
case 1 :
case 2 :
case 3 :
case 4 :
if ( $next == '=' ) {
2006-01-15 07:14:14 +00:00
$tokens [] = $formula [ $i ] . '=' ;
2004-08-11 11:26:20 +00:00
$i ++ ;
}
else {
2006-01-15 07:14:14 +00:00
$tokens [] = $formula [ $i ];
2004-08-11 11:26:20 +00:00
}
break ;
case 5 :
if ( $next == '&' ) {
$tokens [] = '&&' ;
$i ++ ;
}
else {
2006-01-15 07:14:14 +00:00
$tokens [] = $formula [ $i ];
2004-08-11 11:26:20 +00:00
}
break ;
case 6 :
if ( $next == '|' ) {
$tokens [] = '||' ;
$i ++ ;
}
else {
2006-01-15 07:14:14 +00:00
$tokens [] = $formula [ $i ];
2004-08-11 11:26:20 +00:00
}
break ;
}
}
else {
2006-01-15 07:14:14 +00:00
$tokens [] = $formula [ $i ];
2004-08-11 11:26:20 +00:00
}
}
return $tokens ;
}
/**
* Modify a string to contain proper count indices
*
* This is a callback function used via array_map ()
*
* @ param $entry An array element
* @ param $key Index of the array element
*/
function _locale_import_append_plural ( $entry , $key ) {
2004-08-12 18:00:11 +00:00
// No modifications for 0, 1
2004-08-11 11:26:20 +00:00
if ( $key == 0 || $key == 1 ) {
return $entry ;
}
// First remove any possibly false indices, then add new ones
2006-08-18 12:17:00 +00:00
$entry = preg_replace ( '/(@count)\[[0-9]\]/' , '\\1' , $entry );
return preg_replace ( '/(@count)/' , " \\ 1[ $key ] " , $entry );
2004-08-11 11:26:20 +00:00
}
/**
* Generate a short , one string version of the passed comment array
*
* @ param $comment An array of strings containing a comment
* @ return Short one string version of the comment
*/
function _locale_import_shorten_comments ( $comment ) {
$comm = '' ;
2005-07-29 18:56:58 +00:00
while ( count ( $comment )) {
$test = $comm . substr ( array_shift ( $comment ), 1 ) . ', ' ;
if ( strlen ( $comm ) < 130 ) {
$comm = $test ;
}
else {
break ;
}
2004-08-11 11:26:20 +00:00
}
return substr ( $comm , 0 , - 2 );
}
/**
* Parses a string in quotes
*
* @ param $string A string specified with enclosing quotes
* @ return The string parsed from inside the quotes
*/
function _locale_import_parse_quoted ( $string ) {
if ( substr ( $string , 0 , 1 ) != substr ( $string , - 1 , 1 )) {
return FALSE ; // Start and end quotes must be the same
}
$quote = substr ( $string , 0 , 1 );
$string = substr ( $string , 1 , - 1 );
if ( $quote == '"' ) { // Double quotes: strip slashes
return stripcslashes ( $string );
}
elseif ( $quote == " ' " ) { // Simple quote: return as-is
return $string ;
}
else {
return FALSE ; // Unrecognized quote
}
}
/**
* Exports a Portable Object ( Template ) file for a language
*
* @ param $language Selects a language to generate the output for
*/
function _locale_export_po ( $language ) {
global $user ;
2004-08-12 18:00:11 +00:00
2004-08-11 11:26:20 +00:00
// Get language specific strings, or all strings
if ( $language ) {
$meta = db_fetch_object ( db_query ( " SELECT * FROM { locales_meta} WHERE locale = '%s' " , $language ));
$result = db_query ( " SELECT s.lid, s.source, s.location, t.translation, t.plid, t.plural FROM { locales_source} s INNER JOIN { locales_target} t ON s.lid = t.lid WHERE t.locale = '%s' ORDER BY t.plid, t.plural " , $language );
}
else {
2005-04-25 17:57:03 +00:00
$result = db_query ( " SELECT s.lid, s.source, s.location, t.plid, t.plural FROM { locales_source} s INNER JOIN { locales_target} t ON s.lid = t.lid ORDER BY t.plid, t.plural " );
2004-08-11 11:26:20 +00:00
}
2004-08-12 18:00:11 +00:00
2004-08-11 11:26:20 +00:00
// Build array out of the database results
$parent = array ();
while ( $child = db_fetch_object ( $result )) {
2004-10-11 19:57:42 +00:00
if ( $child -> source != '' ) {
$parent [ $child -> lid ][ 'comment' ] = $child -> location ;
$parent [ $child -> lid ][ 'msgid' ] = $child -> source ;
if ( $child -> plid ) {
$parent [ $child -> lid ][ $child -> plid ][ 'plural' ] = $child -> lid ;
$parent [ $child -> lid ][ $child -> plid ][ 'translation' ] = $child -> translation ;
$parent [ $child -> lid ][ $child -> plid ][ 'msgid' ] = $child -> source ;
}
else {
$parent [ $child -> lid ][ 'translation' ] = $child -> translation ;
}
2004-08-11 11:26:20 +00:00
}
}
2004-08-12 18:00:11 +00:00
2004-08-11 11:26:20 +00:00
// Generating Portable Object file for a language
if ( $language ) {
$filename = $language . '.po' ;
$header .= " # $meta->name translation of " . variable_get ( 'site_name' , 'Drupal' ) . " \n " ;
$header .= '# Copyright (c) ' . date ( 'Y' ) . ' ' . $user -> name . ' <' . $user -> mail . " > \n " ;
$header .= " # \n " ;
$header .= " msgid \" \" \n " ;
$header .= " msgstr \" \" \n " ;
$header .= " \" Project-Id-Version: PROJECT VERSION \\ n \" \n " ;
$header .= " \" POT-Creation-Date: " . date ( " Y-m-d H:iO " ) . " \\ n \" \n " ;
$header .= " \" PO-Revision-Date: " . date ( " Y-m-d H:iO " ) . " \\ n \" \n " ;
$header .= " \" Last-Translator: " . $user -> name . ' <' . $user -> mail . " > \\ n \" \n " ;
$header .= " \" Language-Team: " . $meta -> name . ' <' . $user -> mail . " > \\ n \" \n " ;
$header .= " \" MIME-Version: 1.0 \\ n \" \n " ;
$header .= " \" Content-Type: text/plain; charset=utf-8 \\ n \" \n " ;
$header .= " \" Content-Transfer-Encoding: 8bit \\ n \" \n " ;
if ( $meta -> formula && $meta -> plurals ) {
$header .= " \" Plural-Forms: nplurals= " . $meta -> plurals . " ; plural= " . strtr ( $meta -> formula , '$' , '' ) . " ; \\ n \" \n " ;
}
$header .= " \n " ;
2006-08-18 12:17:00 +00:00
watchdog ( 'locale' , t ( 'Exported %locale translation file: %filename.' , array ( '%locale' => $meta -> name , '%filename' => $filename )));
2004-08-11 11:26:20 +00:00
}
2004-08-12 18:00:11 +00:00
2004-08-11 11:26:20 +00:00
// Generating Portable Object Template
else {
2006-06-01 12:32:52 +00:00
$filename = 'drupal.pot' ;
2004-08-11 11:26:20 +00:00
$header .= " # LANGUAGE translation of PROJECT \n " ;
$header .= " # Copyright (c) YEAR NAME <EMAIL@ADDRESS> \n " ;
$header .= " # \n " ;
$header .= " msgid \" \" \n " ;
$header .= " msgstr \" \" \n " ;
$header .= " \" Project-Id-Version: PROJECT VERSION \\ n \" \n " ;
$header .= " \" POT-Creation-Date: " . date ( " Y-m-d H:iO " ) . " \\ n \" \n " ;
$header .= " \" PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ \\ n \" \n " ;
$header .= " \" Last-Translator: NAME <EMAIL@ADDRESS> \\ n \" \n " ;
$header .= " \" Language-Team: LANGUAGE <EMAIL@ADDRESS> \\ n \" \n " ;
$header .= " \" MIME-Version: 1.0 \\ n \" \n " ;
$header .= " \" Content-Type: text/plain; charset=utf-8 \\ n \" \n " ;
$header .= " \" Content-Transfer-Encoding: 8bit \\ n \" \n " ;
$header .= " \" Plural-Forms: nplurals=INTEGER; plural=EXPRESSION; \\ n \" \n " ;
$header .= " \n " ;
2006-08-18 12:17:00 +00:00
watchdog ( 'locale' , t ( 'Exported translation file: %filename.' , array ( '%filename' => $filename )));
2004-08-11 11:26:20 +00:00
}
// Start download process
header ( " Content-Disposition: attachment; filename= $filename " );
header ( " Content-Type: text/plain; charset=utf-8 " );
print $header ;
foreach ( $parent as $lid => $message ) {
if ( ! isset ( $done [ $lid ])) {
if ( $message [ 'comment' ]) {
print '#: ' . $message [ 'comment' ] . " \n " ;
}
print 'msgid ' . _locale_export_print ( $message [ 'msgid' ]);
if ( isset ( $message [ 1 ][ 'plural' ])) {
print 'msgid_plural ' . _locale_export_print ( $message [ 1 ][ 'msgid' ]);
if ( $language ) {
for ( $i = 0 ; $i < $meta -> plurals ; $i ++ ) {
print 'msgstr[' . $i . '] ' . _locale_export_print ( _locale_export_remove_plural ( $message [ $ { i }][ 'translation' ]));
$done [ $message [ $ { i }][ 'plural' ]] = 1 ;
}
}
else {
print 'msgstr[0] ""' . " \n " ;
print 'msgstr[1] ""' . " \n " ;
$done [ $message [ 0 ][ 'plural' ]] = 1 ;
$done [ $message [ 1 ][ 'plural' ]] = 1 ;
}
}
else {
if ( $language ) {
print 'msgstr ' . _locale_export_print ( $message [ 'translation' ]);
}
else {
print 'msgstr ""' . " \n " ;
}
}
print " \n " ;
}
}
die ();
}
/**
* Print out a string on multiple lines
*/
function _locale_export_print ( $str ) {
$stri = addcslashes ( $str , " \0 .. \37 \\ \" " );
$parts = array ();
// Cut text into several lines
while ( $stri != " " ) {
$i = strpos ( $stri , " \\ n " );
if ( $i === FALSE ) {
$curstr = $stri ;
$stri = " " ;
}
else {
$curstr = substr ( $stri , 0 , $i + 2 );
$stri = substr ( $stri , $i + 2 );
}
$curparts = explode ( " \n " , _locale_export_wrap ( $curstr , 70 ));
$parts = array_merge ( $parts , $curparts );
}
if ( count ( $parts ) > 1 ) {
return " \" \" \n \" " . implode ( " \" \n \" " , $parts ) . " \" \n " ;
}
else {
return " \" $parts[0] \" \n " ;
}
}
/**
* Custom word wrapping for Portable Object ( Template ) files .
*
* @ author Jacobo Tarrio
*/
function _locale_export_wrap ( $str , $len ) {
2006-01-13 14:38:38 +00:00
$words = explode ( ' ' , $str );
2004-08-11 11:26:20 +00:00
$ret = array ();
$cur = " " ;
$nstr = 1 ;
while ( count ( $words )) {
$word = array_shift ( $words );
if ( $nstr ) {
$cur = $word ;
$nstr = 0 ;
}
elseif ( strlen ( " $cur $word " ) > $len ) {
$ret [] = $cur . " " ;
$cur = $word ;
}
else {
$cur = " $cur $word " ;
}
}
$ret [] = $cur ;
return implode ( " \n " , $ret );
}
/**
* Removes plural index information from a string
*/
function _locale_export_remove_plural ( $entry ) {
2006-08-18 12:17:00 +00:00
return preg_replace ( '/(@count)\[[0-9]\]/' , '\\1' , $entry );
2004-08-11 11:26:20 +00:00
}
/**
* List languages in search result table
*/
function _locale_string_language_list ( $translation ) {
$languages = locale_supported_languages ( FALSE , TRUE );
unset ( $languages [ 'name' ][ 'en' ]);
$output = '' ;
foreach ( $languages [ 'name' ] as $key => $value ) {
if ( isset ( $translation [ $key ])) {
2005-07-29 06:50:10 +00:00
$output .= ( $translation [ $key ] != '' ) ? $key . ' ' : " <em class= \" locale-untranslated \" > $key </em> " ;
2004-08-12 18:00:11 +00:00
}
2004-08-11 11:26:20 +00:00
}
return $output ;
}
/**
* Build object out of search criteria specified in request variables
*/
function _locale_string_seek_query () {
2006-08-18 18:58:47 +00:00
static $query ;
2004-08-11 11:26:20 +00:00
2006-08-18 18:58:47 +00:00
if ( ! isset ( $query )) {
2004-08-11 11:26:20 +00:00
$fields = array ( 'string' , 'language' , 'searchin' );
$query = new StdClass ;
if ( is_array ( $_REQUEST [ 'edit' ])) {
foreach ( $_REQUEST [ 'edit' ] as $key => $value ) {
if ( ! empty ( $value ) && in_array ( $key , $fields )) {
$query -> $key = $value ;
}
}
}
else {
foreach ( $_REQUEST as $key => $value ) {
if ( ! empty ( $value ) && in_array ( $key , $fields )) {
$query -> $key = strpos ( ',' , $value ) ? explode ( ',' , $value ) : $value ;
}
}
}
}
return $query ;
}
/**
* Perform a string search and display results in a table
*/
function _locale_string_seek () {
2005-03-31 21:18:08 +00:00
// We have at least one criterion to match
2004-08-11 11:26:20 +00:00
if ( $query = _locale_string_seek_query ()) {
$join = " SELECT s.source, s.location, s.lid, t.translation, t.locale FROM { locales_source} s INNER JOIN { locales_target} t ON s.lid = t.lid " ;
2006-01-05 23:35:34 +00:00
$arguments = array ();
2004-08-11 11:26:20 +00:00
// Compute LIKE section
switch ( $query -> searchin ) {
case 'translated' :
2006-01-05 23:35:34 +00:00
$where = " WHERE (t.translation LIKE '%%%s%%' AND t.translation != '') " ;
2004-08-11 11:26:20 +00:00
$orderby = " ORDER BY t.translation " ;
2006-01-05 23:35:34 +00:00
$arguments [] = $query -> string ;
2004-08-11 11:26:20 +00:00
break ;
case 'untranslated' :
2006-01-05 23:35:34 +00:00
$where = " WHERE (s.source LIKE '%%%s%%' AND t.translation = '') " ;
2004-08-11 11:26:20 +00:00
$orderby = " ORDER BY s.source " ;
2006-01-05 23:35:34 +00:00
$arguments [] = $query -> string ;
2004-08-11 11:26:20 +00:00
break ;
case 'all' :
default :
2006-01-05 23:35:34 +00:00
$where = " WHERE (s.source LIKE '%%%s%%' OR t.translation LIKE '%%%s%%') " ;
2004-08-11 11:26:20 +00:00
$orderby = '' ;
2006-01-05 23:35:34 +00:00
$arguments [] = $query -> string ;
$arguments [] = $query -> string ;
2004-08-11 11:26:20 +00:00
break ;
}
switch ( $query -> language ) {
// Force search in source strings
case " en " :
2006-01-05 23:35:34 +00:00
$sql = $join . " WHERE s.source LIKE '%%%s%%' ORDER BY s.source " ;
$arguments = array ( $query -> string ); // $where is not used, discard its arguments
2004-08-11 11:26:20 +00:00
break ;
// Search in all languages
case " all " :
$sql = " $join $where $orderby " ;
break ;
// Some different language
default :
2006-01-05 23:35:34 +00:00
$sql = " $join $where AND t.locale = '%s' $orderby " ;
$arguments [] = $query -> language ;
2004-08-11 11:26:20 +00:00
}
2006-01-05 23:35:34 +00:00
$result = pager_query ( $sql , 50 , 0 , NULL , $arguments );
2004-08-11 11:26:20 +00:00
2004-08-19 15:41:57 +00:00
$header = array ( t ( 'String' ), t ( 'Locales' ), array ( 'data' => t ( 'Operations' ), 'colspan' => '2' ));
2004-08-11 11:26:20 +00:00
$arr = array ();
while ( $locale = db_fetch_object ( $result )) {
$arr [ $locale -> lid ][ 'locales' ][ $locale -> locale ] = $locale -> translation ;
$arr [ $locale -> lid ][ 'location' ] = $locale -> location ;
$arr [ $locale -> lid ][ 'source' ] = $locale -> source ;
}
foreach ( $arr as $lid => $value ) {
2006-07-31 11:25:55 +00:00
$rows [] = array ( array ( 'data' => check_plain ( truncate_utf8 ( $value [ 'source' ], 150 , FALSE , TRUE )) . '<br /><small>' . $value [ 'location' ] . '</small>' ), array ( 'data' => _locale_string_language_list ( $value [ 'locales' ]), 'align' => 'center' ), array ( 'data' => l ( t ( 'edit' ), " admin/settings/locale/string/edit/ $lid " ), 'class' => 'nowrap' ), array ( 'data' => l ( t ( 'delete' ), " admin/settings/locale/string/delete/ $lid " ), 'class' => 'nowrap' ));
2004-08-11 11:26:20 +00:00
}
$request = array ();
if ( count ( $query )) {
foreach ( $query as $key => $value ) {
$request [ $key ] = ( is_array ( $value )) ? implode ( ',' , $value ) : $value ;
}
}
2005-10-07 06:11:12 +00:00
if ( count ( $rows )) {
$output .= theme ( 'table' , $header , $rows );
}
if ( $pager = theme ( 'pager' , NULL , 50 , 0 , $request )) {
$output .= $pager ;
}
2004-08-11 11:26:20 +00:00
}
return $output ;
}
// ---------------------------------------------------------------------------------
// List of some of the most common languages (administration only)
/**
* Prepares the language code list for a select form item with only the unsupported ones
*/
function _locale_prepare_iso_list () {
$languages = locale_supported_languages ( FALSE , TRUE );
$isocodes = _locale_get_iso639_list ();
foreach ( $isocodes as $key => $value ) {
if ( isset ( $languages [ 'name' ][ $key ])) {
unset ( $isocodes [ $key ]);
continue ;
}
if ( count ( $value ) == 2 ) {
$tname = t ( $value [ 0 ]);
$isocodes [ $key ] = ( $tname == $value [ 1 ]) ? $tname : " $tname ( $value[1] ) " ;
}
else {
$isocodes [ $key ] = t ( $value [ 0 ]);
}
}
asort ( $isocodes );
return $isocodes ;
}
/**
* Some of the common languages with their English and native names
*
* Based on ISO 639 and http :// people . w3 . org / rishida / names / languages . html
*/
function _locale_get_iso639_list () {
return array (
" aa " => array ( " Afar " ),
" ab " => array ( " Abkhazian " , " аҧсуа бызшәа " ),
" ae " => array ( " Avestan " ),
" af " => array ( " Afrikaans " ),
" ak " => array ( " Akan " ),
" am " => array ( " Amharic " , " አማርኛ " ),
" ar " => array ( " Arabic " , " العربية " ),
" as " => array ( " Assamese " ),
" av " => array ( " Avar " ),
" ay " => array ( " Aymara " ),
" az " => array ( " Azerbaijani " , " azərbaycan " ),
" ba " => array ( " Bashkir " ),
" be " => array ( " Belarusian " , " Беларуская " ),
" bg " => array ( " Bulgarian " , " Български " ),
" bh " => array ( " Bihari " ),
" bi " => array ( " Bislama " ),
" bm " => array ( " Bambara " , " Bamanankan " ),
" bn " => array ( " Bengali " ),
" bo " => array ( " Tibetan " ),
" br " => array ( " Breton " ),
" bs " => array ( " Bosnian " , " Bosanski " ),
" ca " => array ( " Catalan " , " Català " ),
" ce " => array ( " Chechen " ),
" ch " => array ( " Chamorro " ),
" co " => array ( " Corsican " ),
" cr " => array ( " Cree " ),
" cs " => array ( " Czech " , " Čeština " ),
" cu " => array ( " Old Slavonic " ),
2006-08-11 17:51:20 +00:00
" cv " => array ( " Chuvash " ),
" cy " => array ( " Welsh " , " Cymraeg " ),
2006-04-19 13:22:12 +00:00
" da " => array ( " Danish " , " Dansk " ),
2004-08-11 11:26:20 +00:00
" de " => array ( " German " , " Deutsch " ),
" dv " => array ( " Maldivian " ),
" dz " => array ( " Bhutani " ),
" ee " => array ( " Ewe " , " Ɛʋɛ " ),
" el " => array ( " Greek " , " Ελληνικά " ),
" en " => array ( " English " ),
" eo " => array ( " Esperanto " ),
" es " => array ( " Spanish " , " Español " ),
" et " => array ( " Estonian " , " Eesti " ),
" eu " => array ( " Basque " , " Euskera " ),
" fa " => array ( " Persian " , " فارسی " ),
" ff " => array ( " Fulah " , " Fulfulde " ),
" fi " => array ( " Finnish " , " Suomi " ),
" fj " => array ( " Fiji " ),
" fo " => array ( " Faeroese " ),
" fr " => array ( " French " , " Français " ),
" fy " => array ( " Frisian " , " Frysk " ),
" ga " => array ( " Irish " , " Gaeilge " ),
" gd " => array ( " Scots Gaelic " ),
" gl " => array ( " Galician " , " Galego " ),
" gn " => array ( " Guarani " ),
" gu " => array ( " Gujarati " ),
" gv " => array ( " Manx " ),
" ha " => array ( " Hausa " ),
" he " => array ( " Hebrew " , " עברית " ),
" hi " => array ( " Hindi " , " हिन्दी " ),
" ho " => array ( " Hiri Motu " ),
" hr " => array ( " Croatian " , " Hrvatski " ),
" hu " => array ( " Hungarian " , " Magyar " ),
" hy " => array ( " Armenian " , " Հայերեն " ),
" hz " => array ( " Herero " ),
" ia " => array ( " Interlingua " ),
" id " => array ( " Indonesian " , " Bahasa Indonesia " ),
" ie " => array ( " Interlingue " ),
" ig " => array ( " Igbo " ),
" ik " => array ( " Inupiak " ),
" is " => array ( " Icelandic " , " Íslenska " ),
" it " => array ( " Italian " , " Italiano " ),
" iu " => array ( " Inuktitut " ),
" ja " => array ( " Japanese " , " 日本語 " ),
" jv " => array ( " Javanese " ),
" ka " => array ( " Georgian " ),
" kg " => array ( " Kongo " ),
" ki " => array ( " Kikuyu " ),
" kj " => array ( " Kwanyama " ),
" kk " => array ( " Kazakh " , " Қазақ " ),
" kl " => array ( " Greenlandic " ),
" km " => array ( " Cambodian " ),
" kn " => array ( " Kannada " , " ಕನ್ನಡ " ),
" ko " => array ( " Korean " , " 한국어 " ),
" kr " => array ( " Kanuri " ),
" ks " => array ( " Kashmiri " ),
" ku " => array ( " Kurdish " , " Kurdî " ),
" kv " => array ( " Komi " ),
" kw " => array ( " Cornish " ),
" ky " => array ( " Kirghiz " , " Кыргыз " ),
" la " => array ( " Latin " , " Latina " ),
" lb " => array ( " Luxembourgish " ),
" lg " => array ( " Luganda " ),
" ln " => array ( " Lingala " ),
" lo " => array ( " Laothian " ),
" lt " => array ( " Lithuanian " , " Lietuviškai " ),
" lv " => array ( " Latvian " , " Latviešu " ),
" mg " => array ( " Malagasy " ),
" mh " => array ( " Marshallese " ),
" mi " => array ( " Maori " ),
" mk " => array ( " Macedonian " , " Македонски " ),
" ml " => array ( " Malayalam " , " മലയാളം " ),
" mn " => array ( " Mongolian " ),
" mo " => array ( " Moldavian " ),
" mr " => array ( " Marathi " ),
" ms " => array ( " Malay " , " Bahasa Melayu " ),
" mt " => array ( " Maltese " , " Malti " ),
" my " => array ( " Burmese " ),
" na " => array ( " Nauru " ),
" nd " => array ( " North Ndebele " ),
" ne " => array ( " Nepali " ),
" ng " => array ( " Ndonga " ),
" nl " => array ( " Dutch " , " Nederlands " ),
" no " => array ( " Norwegian " , " Norsk " ),
" nr " => array ( " South Ndebele " ),
" nv " => array ( " Navajo " ),
" ny " => array ( " Chichewa " ),
" oc " => array ( " Occitan " ),
" om " => array ( " Oromo " ),
" or " => array ( " Oriya " ),
" os " => array ( " Ossetian " ),
" pa " => array ( " Punjabi " ),
" pi " => array ( " Pali " ),
" pl " => array ( " Polish " , " Polski " ),
" ps " => array ( " Pashto " , " پښتو " ),
" pt " => array ( " Portuguese " , " Português " ),
" qu " => array ( " Quechua " ),
" rm " => array ( " Rhaeto-Romance " ),
" rn " => array ( " Kirundi " ),
" ro " => array ( " Romanian " , " Română " ),
" ru " => array ( " Russian " , " Русский " ),
" rw " => array ( " Kinyarwanda " ),
" sa " => array ( " Sanskrit " ),
" sc " => array ( " Sardinian " ),
" sd " => array ( " Sindhi " ),
" se " => array ( " Northern Sami " ),
" sg " => array ( " Sango " ),
" sh " => array ( " Serbo-Croatian " ),
" si " => array ( " Singhalese " ),
" sk " => array ( " Slovak " , " Slovenčina " ),
" sl " => array ( " Slovenian " , " Slovenščina " ),
" sm " => array ( " Samoan " ),
" sn " => array ( " Shona " ),
" so " => array ( " Somali " ),
" sq " => array ( " Albanian " , " Shqip " ),
" sr " => array ( " Serbian " , " Српски " ),
" ss " => array ( " Siswati " ),
" st " => array ( " Sesotho " ),
" su " => array ( " Sudanese " ),
" sv " => array ( " Swedish " , " Svenska " ),
" sw " => array ( " Swahili " , " Kiswahili " ),
" ta " => array ( " Tamil " , " தமிழ் " ),
" te " => array ( " Telugu " , " తెలుగు " ),
" tg " => array ( " Tajik " ),
" th " => array ( " Thai " , " ภาษาไทย " ),
" ti " => array ( " Tigrinya " ),
" tk " => array ( " Turkmen " ),
" tl " => array ( " Tagalog " ),
" tn " => array ( " Setswana " ),
" to " => array ( " Tonga " ),
" tr " => array ( " Turkish " , " Türkçe " ),
" ts " => array ( " Tsonga " ),
" tt " => array ( " Tatar " , " Tatarça " ),
" tw " => array ( " Twi " ),
" ty " => array ( " Tahitian " ),
" ug " => array ( " Uighur " ),
" uk " => array ( " Ukrainian " , " Українська " ),
" ur " => array ( " Urdu " , " اردو " ),
" uz " => array ( " Uzbek " , " o'zbek " ),
" ve " => array ( " Venda " ),
" vi " => array ( " Vietnamese " , " Tiếng Việt " ),
" wo " => array ( " Wolof " ),
" xh " => array ( " Xhosa " , " isiXhosa " ),
" yi " => array ( " Yiddish " ),
" yo " => array ( " Yoruba " , " Yorùbá " ),
" za " => array ( " Zhuang " ),
2004-09-09 11:53:56 +00:00
" zh-hans " => array ( " Chinese, Simplified " , " 简体中文 " ),
" zh-hant " => array ( " Chinese, Traditional " , " 繁體中文 " ),
2004-08-11 11:26:20 +00:00
" zu " => array ( " Zulu " , " isiZulu " ),
);
}