Skip to content
Browse files

Branching from 6.x-1.7

0 parents commit 1dd800192dd6ffe6fe9259e4248b6021ef635761 Jose Reyero committed
Showing with 9,892 additions and 0 deletions.
  1. +415 −0 CHANGELOG.txt
  2. +40 −0 INSTALL.txt
  3. +30 −0 README.txt
  4. +63 −0 i18n.admin.inc
  5. +7 −0 i18n.info
  6. +121 −0 i18n.install
  7. +17 −0 i18n.js
  8. +966 −0 i18n.module
  9. +277 −0 i18n.pages.inc
  10. +6 −0 i18nblocks/i18nblocks.info
  11. +152 −0 i18nblocks/i18nblocks.install
  12. +293 −0 i18nblocks/i18nblocks.module
  13. +107 −0 i18nblocks/translations/i18nblocks.de.po
  14. +82 −0 i18nblocks/translations/i18nblocks.pot
  15. +124 −0 i18nblocks/translations/i18nblocks.uk.po
  16. +8 −0 i18ncck/i18ncck.info
  17. +17 −0 i18ncck/i18ncck.install
  18. +103 −0 i18ncck/i18ncck.module
  19. +8 −0 i18ncontent/i18ncontent.info
  20. +67 −0 i18ncontent/i18ncontent.install
  21. +219 −0 i18ncontent/i18ncontent.module
  22. +58 −0 i18ncontent/translations/i18ncontent.de.po
  23. +57 −0 i18ncontent/translations/i18ncontent.pot
  24. +67 −0 i18ncontent/translations/i18ncontent.uk.po
  25. +9 −0 i18nmenu/i18nmenu.info
  26. +17 −0 i18nmenu/i18nmenu.install
  27. +456 −0 i18nmenu/i18nmenu.module
  28. +54 −0 i18nmenu/translations/i18nmenu.de.po
  29. +53 −0 i18nmenu/translations/i18nmenu.pot
  30. +71 −0 i18nmenu/translations/i18nmenu.uk.po
  31. +9 −0 i18npoll/i18npoll.info
  32. +170 −0 i18npoll/i18npoll.module
  33. +58 −0 i18npoll/translations/i18npoll.de.po
  34. +57 −0 i18npoll/translations/i18npoll.pot
  35. +7 −0 i18nprofile/i18nprofile.info
  36. +69 −0 i18nprofile/i18nprofile.install
  37. +256 −0 i18nprofile/i18nprofile.module
  38. +42 −0 i18nprofile/translations/i18nprofile.de.po
  39. +41 −0 i18nprofile/translations/i18nprofile.pot
  40. +149 −0 i18nstrings/i18nstrings.admin.inc
  41. +6 −0 i18nstrings/i18nstrings.info
  42. +203 −0 i18nstrings/i18nstrings.install
  43. +1,219 −0 i18nstrings/i18nstrings.module
  44. +150 −0 i18nstrings/translations/i18nstrings.de.po
  45. +116 −0 i18nstrings/translations/i18nstrings.pot
  46. +156 −0 i18nstrings/translations/i18nstrings.uk.po
  47. +23 −0 i18nsync/README.txt
  48. +6 −0 i18nsync/i18nsync.info
  49. +17 −0 i18nsync/i18nsync.install
  50. +588 −0 i18nsync/i18nsync.module
  51. +152 −0 i18nsync/translations/i18nsync.de.po
  52. +147 −0 i18nsync/translations/i18nsync.pot
  53. +159 −0 i18nsync/translations/i18nsync.uk.po
  54. +182 −0 i18ntaxonomy/i18ntaxonomy.admin.inc
  55. +8 −0 i18ntaxonomy/i18ntaxonomy.info
  56. +95 −0 i18ntaxonomy/i18ntaxonomy.install
  57. +16 −0 i18ntaxonomy/i18ntaxonomy.js
  58. +1,100 −0 i18ntaxonomy/i18ntaxonomy.module
  59. +151 −0 i18ntaxonomy/i18ntaxonomy.pages.inc
  60. +190 −0 i18ntaxonomy/translations/i18ntaxonomy.de.po
  61. +180 −0 i18ntaxonomy/translations/i18ntaxonomy.pot
  62. +206 −0 i18ntaxonomy/translations/i18ntaxonomy.uk.po
Sorry, we could not display the entire diff because it was too big.
415 CHANGELOG.txt
@@ -0,0 +1,415 @@
+// $Id: CHANGELOG.txt,v 1.15.4.100 2010/09/09 14:47:16 jareyero Exp $
+
+6.x-1.6 to 6.x-1.7
+------------------
+- Fixed: Missing function i18n_taxonomy_translate_term_name(), by srobert72, #870366
+- Fixed: Parameter 1 to menu_nodeapi() expected to be a reference (php5.3), by setvik, #886700
+- Fixed: Multilingual variables cache being rebuilt every page load, by patrick2000, #905428
+- Improved variable api, added i18n_variable(), extended i18n_variable_del()
+- Improved variable settings, variables for default language are now set to main table
+
+6.x-1.5 to 6.x-1.6
+------------------
+- Fixed: issues with i18nvariables, do not check anymore i18n_variables on initialization, by Jose, #850708
+- Fixed: Menu item's language is lost after node update, by neochief, #514630
+- Fixed: PHP notices, by eMPee584, #769318
+- Delete description on menu item delete, #844208 by plach
+- Fixed: Node type help not translated, by NaX, #685788
+- Some improvements to i18nmenu API, by plach, #386372
+- Renamed theme_taxonomy_term_page() to theme_i18ntaxonomy_term_page() to avoid namespace conflicts
+- Added some taxonomy API functions: i18ntaxonomy_translate_term_name()....
+- Fixed: Access denied to database query (LOCK TABLES) on i18n_variable_set(), #769260
+- Reworked variable initialization to fix multiple issues (front page, etc), #826774
+- Some changes to variables API, added new function: i18n_variable_get()
+- Fixed: Node type help not translated, by NaX, #685788
+
+6.x-1.4 to 6.x-1.5
+------------------
+- Fixed: taxonomy name/help not translated, by miro_dietiker, #686896
+- Disabled language selection when editing menu items, by swentel, #694680
+- Reworked variable initialization to fox static cache issues with Drupal 6.17 (drupal_is_front_page), #817164
+- Fixed l10n_client support, added missing strings for translation, by miro_dietiker, khaled.zaidan, #304434
+- Fixed duplicates in locales source, by Dippers, #782036
+- Fixed warning during update script (possibly ids are the same), #783978
+- Fixed maxlength, for node translation autocomplete, by cedrid, #802418
+- Fixed i18n_init not run early enough, inconsistent menu items, #614548
+- Fixed wrong data type in query for i18n_blocks (pgsql), by jmpoure, #709272
+- Added i18nstrings_string() for 'plain strings', don't do unneeded filtering.
+- Fixed: Make menu title translatable (blocks), by moritzz, #356375
+- Moved i18nviews module to new project,http://drupal.org/project/i18nviews, #788290
+- Fixed: errors when taxonomy not set, #743492
+- Fixed update issues, for i18nstrings, #79644
+- New feature: Optionally translate default language in tt(), by miro_dietiker and markus_petrux, #676002
+- Moved i18n_test module to "Testing" package, #799730
+- Fixed: The "All languages" option can't save back on blocks, #766678
+- Marked obsolete functions tt() and to(). Replaced by i18nstrings() and i18nstrings_update()
+- Restore back function tt() to prevent "Fatal error: Call to undefined function tt()" in other modules, by markus_petrux, #358839
+- Added support for optional default language translation, by Miro Dietiker, #676002
+
+6.x-1.3 to 6.x-1.4
+------------------
+- Added some unit tests for strings, blocks and taxonomy
+- Fixed: Block translations deleted on refresh, #732680
+- Fixed: warning in node form causes PHP notice, by swentel, #664944
+- Fixed: i18nblocks conflicts with modules and themes, by JohnAlbin, #457512
+- Some clean up to i18nstrings API: Dropped update option from i18nstrings_translate()
+- Disable content selection during synchronization, by Pisco, #648348
+- Add CSS class to body tag based on language, by Garrett Albright, #661448
+- Added compatibility with ctools page manager, by Kars-T, #698942
+- Added support for taxonomy_override_selector, by hefox, #619726
+- Fixed: Configure string translation tab not showing up, #732368
+- Fixed: Views content negotiation filter not working, by markus_petrux, #650552
+- PHP 5.3 compatibiliby issue, by Berdir, #623138
+
+6.x-1.2 to 6.x-1.3
+-------------------
+- Security update: Reworked string translation (i18nstrings) API, see #731632
+- Fixed: Multiple issues with file synchronization, new files, other modules, #363065
+- Usability: Added proper validation to vocabulary form, by andrewlevine #577066
+- Fixed: Translated primary and secondary links appear even if they are disabled, by yrocq #631388
+- Reworked i18nstrings API using proper namespaces, kept tt() for compatibility
+- Added: New plugin for views translation, by nedjo, joostvdl, #360024
+
+6.x-1.1 to 6.x-1.2
+------------------
+- Fixed: location fields not showing up for synchronization options, #426294
+- Fixed: pgsql compatibility in upgrade scripts, by jandd, #383728
+- Fixed: Translate all custom block titles (not only custom blocks), by brucepearson, #541048
+- Added: Primary and secondary links' menus translation using preprocess, by neochief, #514108
+- Changed: hook_locale(), new 'info' op, obsoleted 'refresh'
+- Fixed: Some text groups losing strings when refreshed, #442428
+- Fixed: Checkboxes fields are not translating in profile view pages, by neochief, #520692
+- Added: Translate tags vocablary name on node form, by dboulet, #494576
+- Fixed i18n Filter in Views results in SQL Error, by markus_petrux, #522162
+- Added tag support for taxonomy synchronization, #605600
+- Fixed buggy taxonomy synchronization, #365606, #365606, #379306
+- Cleaning some module dependencies and old code.
+- Fixed: Select translations autocomplete wrong filtering by hass, #557980
+- Added: Translated taxonomy tokens by Hugo Wetterberg, googletorp, #344794
+- Fixed: Global variable corruption by etzel, #580280
+- New i18ncck module by neochief
+- Got started with some unit tests and base test clase.
+- Added support for image synchronization, #329825
+- Added filefield/imagefield synchronization, by dasjo, #278426
+- Fixed: Unset variables set to be multilingual don't show default values, by Island Usurper, #527534
+- Reworked content type settings, now in own fieldset
+- Added node language options:
+ - Default to current language
+ - No language neutral
+ - Lock language once created
+
+6.x-1.0 to 6.x-1.1
+--------------------
+- Fixed pgsql issue in i18nblocks query rewriting, by plj, #383772
+- Changed i18ntaxonomy module weight for it to run before views, by Owen Barton, #386136
+- Inprovements to i18nsync_node_available_fields(): cached, alter hook, ...
+- Fixed node author field in i18nsync (author -> name), by mfb, #399216
+- Fixed synchronization for nodereference fields, by dasjo, #242601
+- Fixed: Unterminated entities in taxonomy module break page validation, by ao2, #478856
+- Fixed multilingual variables issues, by rfay, #451120
+- Fixed i18nstrings, wrong hook_schema_alter() implementation, by rsvelko, #466876
+- Added location to i18nsync fields, by zedzed, #426294
+- Added new dependence (i18ntaxonomy) and some taxonomy handling to i18nviews
+- Fixed missing brackets around table name in query, #371429
+- Fixed incompatible table locking, using proper API, #416912
+- Fixed typos, by ao2, #429850, dboulet, #396402
+- New feature: allow to select existing node as translation, #295682
+- Fixed: translation relationship broken with i18nsync, by smh67, Beban, #367118
+- Fixed: translated node forgets its menu settings, by brucepearson, #364375
+- Fixed: missing node languages for LANGUAGE_SUPPORT_EXTENDED_NOT_DISPLAYED, by Roger Lopez, #365574
+
+6.x-beta6 to 6.x-1.0
+----------------------
+- Added localization for content types and taxonomy in advanced search form, #363384
+- Fixed multiple translation issues for node/add pages, included #353912
+- Fixed Undefined function i18n_taxonomy_localize_terms(), by roborn, #362559
+- Fixed: Unknown column status during i18ncontent update, #361228
+- Fixed missing table wrapper in i18nblocks, by mrfelton, #362050
+- Added: Display hierarchy when translating taxonomy, by Afief, #288086
+- Added option for switching language when translating
+- Moved admin settings to higher level tab
+- Deleting profile strings when deleting fields, #356538
+- Fixed: attached nodes synchronization not working, by doq, #359987
+- Fixed: locale table growing on each request, issue with i18nstring_textgroup(), by drifter, #359249
+- Fixed: missing i18nstrings module when enabling other modules module, #358839
+- Fixed: taxonomy strings not properly synchronized (removed), by stella, #356542
+- Removed obsoleted (was commented out) i18nblocks_blocks(), #356519
+- Applied i18nblocks implementation of i18nstrings/locale_refresh(), by catch, #356520
+- Applied i18ncontent tt/hook_locale() review, by catch, #356535
+- Reworked taxonomy language logic when saving terms, set node/current language for tags.
+- Added some more help text and links to the taxonomy admin pages.
+- Mark translations a outdated when source string changes, by nedjo, #355366
+- Changed default behavior: content types with multilingual support disabled are now language neutral, #270655
+- Added i18n menu handling to node form, by Roger López, Stella, #348444
+- Added additional language support level, by stella, Roger Lopez, #349193
+- Fixed duplicated 'ts' function (CiviCRM), renamed to 'i18nstrings_ts', by mfb, #298154
+- Move all admin settings to one place under Language, by stella, #343557
+- CCK file fields not removed anymore when one of the translations is deleted, #313613
+- Fixed: uploaded files removed when one of the translations is deleted, #313613
+- Fixed issue with file sync list option, by nkoponen, #298327
+- Added clean up feature to i18nstrings refresh
+- Fixed multiple issues with content type strings translations, #343335
+- Added some more parameter checking for i18nviews module, #294278
+- Fixed issues with i18nstrings and menu items, by smk-ka, catch, sun, nedjo, #339057
+- Fixed i18ntaxonomy schema mismatch (missing schema_alter), #288994
+- Added index for locale table's location field, by sun, #349488
+- Removed deprecated i18n_get_links().
+- Tentative fix for: Story shows multiple times on front page, #334670
+- Simplified mixed content selection logic
+- Added content selection for views, by Jax, #283845
+- Implemented workaround for race condition, #277711
+
+6.x-beta4 to 6.x-beta6 (Rolled out beta5 with some errors so did a new release)
+------------------------------------------------------------------------------
+- Fixed multilingual blocks not translating, #305868
+- Fixed notices, undefined variables, by eMPee584, #276161
+- Fixed some issues with strings and menu install/updates
+- Fixed typo in i18nblocks_update_2(), #317042
+- Fixed warning illegal offset type, #294278
+- Removed wrong version from info files, by Roger López, #319249
+- Fixed help string, by Hass, #316784
+- Uncountable code style issues have been fixed for coder module compliance and core rules.
+- Fixed: Translation issues like missing periods, typos, etc.
+- Fixed: Synchronization not happening when creating a translation, #313605
+- Added some help texts for i18nstrings and warnings about the site default language, #314035
+- Fixed: Column name conflict (oid) on PostgreSQL, #314126
+- Fixed: Added English language to import/export page, #314035
+- Fixed: Code tries to access object as array, by Skiller, #293275
+- Fixed: Sub-menu items not localized, by Anselm Heaton, #298612
+- Some clean up in i18ntaxonomy module, coding standards, cvs tags, etc..
+- Fixed some race condition when creating term translations
+- Multiple bug fixes and code improvements, by stella, #314846, #314880, #314853
+
+6.x-beta3 to 6.x-beta4
+----------------------
+- Fixed issue with language dependent front page, that couldn't be used before
+- Fixed postgresql compatibility issue, by miopa, #308554
+- Fixed profile category string issues, #304451
+- Renamed some modules, updated descriptions
+- Fixed / improved some module help texts.
+- Fixed multiple localization issues, by hass, #310852
+- Fixed error with i18ncontent update, #295263
+- Fixed warning in sync module, by vjacquin, #299064 and #294511
+- Fixed issue with i18nblocks and system module weights, #299642
+- Fixed warning in i18ntaxonomy, by mvc , #304003
+
+6.x-beta2 to 6.x-beta3
+----------------------
+- Partial fix for node view forcing selection mode, #283839
+- Fixed multiple taxonomy translation issues, by japanitrat, #292232
+- Added some basic functionality to i18nviews module, wip
+- Some clean up of update scripts, removed obsoleted functions.
+- Moved selection mode initialization out of hook init, to first time it's needed.
+- Fixed some issues with taxonomy and query rewriting rewriting, by vingbord, #260156 and #260151
+- Fixed wrong data type in schema, by polarbear128, #289027
+- Fixed issue with synchronization and empty fields as array (files)
+- Added back CCK syncronization, by drewish, #293019
+- Added new 'mixed' language selection option, by ablivio, #203798
+- Moved language icons to a new project, http://drupal.org/project/languageicons
+
+6.x-beta1 to 6.x-beta2
+--------------------
+- Better icon for Arabic, by Afief, #278140
+- Added back i18nviews module for views specific features
+- Fixed languageicons occassional warning, by eMPee584, #276161
+- Commented out views hooks on i18ntaxonomy (not for the first version), #285660
+- Fixed wrong duplicated translations (context/caching issue), #281471
+- Added preliminary support for l10n client (Patch pending)
+- Fixed missing dependency on i18nblocks, #279769
+- Fixed i18npoll_nodeapi returning $node, breaking feeds and cron calls, #254022
+- Fixed notice and possible i18n blocks not found, #278588
+- Fixed node upgrade issue, #260626
+- Removed references to obsolteted function i18n_get_normal_path(), #259603
+- Wrong link and typos in taxonomy module, by AaronCollier, #259440
+- Added Thai flag, by kengggg, #257387
+- Fixed wrong link on help page, by steinmb, #257289
+- Patch to prevent query rewriting for views, by Frank Steiner, #272432
+- Taxonomies not syncing, by drewish, #256357
+- Menu not refreshing locales, by Frank Steiner, #263124
+- User profiles not translated on registration, by gustavlarson, #248089
+
+6.x-dev to 6.x-beta1
+--------------------
+- Security update, http://drupal.org/node/250344
+- Disabled CCK integration in i18nsync till there's a stable cck version
+- Fixed multiple issues with selection modes and some old function clean up
+- Fixed issues with menu administration pages, now showing all items for nodes
+- Improved block management, no need for 'Translated' duplicated blocks anymore.
+- Added new dependency for i18nmenu, now it works with i18nblocks
+- Fixed: i18nstrings_to() not changing location, http://drupal.org/node/230750
+- Fixed: taxonomy vocabulary setting not saved when set to 'none'
+- Updated README.txt and INSTALL.txt files
+- Added string refreshing for all the modules using i18nstrings.
+- Fixed installation issues doing operations on enable hook instead of install.
+- Fixed: wrong function name on i18ntaxonomy producing errors/warnings
+- Fixed: blocks not saving language properly.
+- Improved taxonomy vocabulary form, re-grouping the fields and with some help text.
+- Fixed Call-time-pass-by reference, http://drupal.org/node/231856
+- New refresh and update feature for i18nstrings (implemented for taxonomy and content)
+
+5.x to 6.x
+----------
+07/03/2008
+- Fixed file synchronization for translations
+- Added field population for synchronized fields
+- Translating taxonomy for new node translations
+- Taxonomy synchronization is now a per content type option
+23/02/2008
+- Added aggregated polls: i18npoll.module
+- Options for language icons: show/don't show for nodes and block
+
+21/02/2008
+- Upgraded i18ncontent, fully reworked, much cleaner now
+- Improved i18nstrings, translation of strings without source, debug facility
+- Replaced taxonomy term pages with localized version
+
+19/02/2008 Initial dev release for Drupal 6
+- Fully reworked block translation, language for blocks now, dropped old metablocks
+- Fully reworked menu translation, uses Drupal 6 features and extended blocks
+- Fully reworked taxonomy translation, all options in the same module now
+- Better code documentation and help texts everywhere
+- Reorganized package, no more experimental modules, all modules in its folder now
+- Language icons in an independent module now
+- Reworked strings handling (extended localization)
+- Trimmed down parts now done by Drupal core
+- Upgraded most of the modules, other are waiting on some dependencies (cck, views, nodeasblock, etc...)
+
+4.7.x to 5.x
+------------
+- Upgraded all modules
+- Added lots of features and new modules to the collection
+
+4-7-4 to ....
+--------------
+- Added support for RTL languages
+- Added native names for languages
+- Cleaned up multilingual variable management
+- Handling of attachments as file revisions. No need to duplicate anymore
+
+4-6-0 to 4-7-4
+--------------
+30/11/2005
+- Added basic workflow support for translations
+- Lost of bug fixes
+- Content administration tab for translations
+
+20/08/2005
+- Updated for HEAD
+- Simplified patches
+
+13/06/2005
+- Fixed some minor bugs
+- Created HOWTO.txt for misc documentation
+- Added Russian flag
+- Updated patches for 4.6.1
+
+16/05/2005
+- Updated README.txt, taxonomy translations
+- Fixed i18n_includes.patch
+
+4-5-0 to 4-6-0
+--------------
+24/04/2005
+- Created 4-6 branch
+- Updated docs
+
+11/04/2005
+- Fixed bug http://drupal.org/node/20305 (pgsql typo)
+- Updated generated links to latest changes in l()
+- Some code cleanup
+- Update script from 4.5 to CVS
+
+29/03/2005
+- Japanese flag
+- Updated contributors
+
+26/03/2005
+- Added some logic in query rewriting functions to get all terms & vocabs when administering categories and the right ones when editing a node.
+- Reworked configuration options for flags. Now language appears in the alt text for icons. Accesibility++
+- Added support for long locale names (http://drupal.org/node/14814). Still needs some fallback mechanism.
+- Merging all the code in only one include file: i18n.inc
+- Not running anymore the language name through localization system: http://drupal.org/node/19179
+- Added & updated PostgreSQL schema
+- Changed block HTML from using tables to HTML list, for consistency with the rest of Drupal blocks
+- Added some contributed language flags
+- Some cleaning up, WIP, preparing for 4.6 release
+
+04/02/2005
+- Applied patch http://drupal.org/node/15966 (Switch to translated nodes and taxonomy listings)
+- Moved the file i18n.mysql and fixed bug
+- Cleaned up the patches (NK)
+
+02/02/2005
+- Term translations in a new table
+- Implemented db_rewrite_sql hook
+- Improved taxonomy forms
+- Fixed bug with update.php
+- Moved most of the hooks to i18n.inc, to fix some problems and speed up cached pages
+
+23/01/2005
+- Reworked init system, module.inc patch not needed anymore
+- Implemented hook_node_query_sql, node.module patch not needed anymore
+- Using redirects from the main page depending on browser language
+- Removed translations from taxonomy terms
+- Added language to vocabularies
+
+04/01/2004
+- Now language icons are not displayed with teasers
+- Fixed http://drupal.org/node/15062 (Inconsistent i18n_front_page vs. i18n_frontpage)
+- Fixed http://drupal.org/node/14640 (Page not found)
+- Fixed http://drupal.org/node/14475 (all init hooks disabled by cvs patch)
+
+07/12/2004
+- Fixed http://drupal.org/node/13982 (setting languages in config file fails)
+- Changed configurable path to language icons, to configurable html tag. Reconfiguration of the module needed.
+- Changed 'flags' to 'language icons', and the images to 16x12px png
+- Fixed http://drupal.org/node/13319 (some issues with cached pages)
+- Reworked initialization system. New patches for bootstrap.inc, module.inc
+
+30/11/2004
+
+- Updated to current DRUPAL-4-5 branch, won't work with 4.5.0 anymore
+- Cleaned the code as suggested by Dries
+- Renamed some database fields: 'lang' to 'language' in 'node' and 'taxonomy_term'
+
+23/11/2004 [Full rewrite]
+- Updated INSTALL.txt and README.txt
+- Basic translation interface for nodes and taxonomy terms
+- Simplified configuration, removed many options
+- Language code is now kept only in url
+- No need for include file anymore (patched common.inc::url)
+- Moving ahead from multi-table approach
+- Drop all the multi-table-synchronization logic
+- Simple database patches for 'node' and 'taxonomy_term' tables
+
+4-4-0 to 4-5-0
+--------------
+- Removed the common.inc patch 01, as it already got into the core
+- Added language dependent variables
+- Refined node and taxonomy synchronization
+- Rearranged configuration interface
+- Option to get supported languages from locale module
+- Replaced some global variables with functions.
+- Fixed an issue with outgoing links and url prefixing
+- Some integration with locale module
+- $languages changed to i18n_languages in the config file
+- Added patch for locale system to get the language from i18n
+- Partial rewriting and cleaning up of the code
+- Now, any two letter prefix in url will be taken as language
+- Fixed bug with url rewriting
+
+13/07/2004
+- Updated according to patches and description on http://drupal.org/node/view/8977
+
+06/04/2004
+- Changed options in conf file
+- Added taxonomy synchronization
+- Added node synchronization
+- Added *real* content translation with language-dependant tables
+- Repackaging: placed all the files under modules/i18n
+- Fix Call to undefined function: drupal_get_path_map()
+- Fixed small problem with drupal_goto
+
+4-4-0
+------------
+- Initial release
40 INSTALL.txt
@@ -0,0 +1,40 @@
+// $Id: INSTALL.txt,v 1.21.2.6 2009/08/29 21:08:24 hass Exp $
+********************************************************************
+ D R U P A L M O D U L E
+********************************************************************
+Name: i18n module and translation module
+Author: Jose A. Reyero
+
+********************************************************************
+ This is the 6.x version of i18n module, and works with Drupal 6.x
+********************************************************************
+
+********************************************************************
+Updated documentation will be kept on-line at http://drupal.org/node/133977
+********************************************************************
+
+INSTALLATION:
+============
+
+1. Create folder 'sites/all/modules/i18n' and copy all the modules files, keeping directory structure, to this folder.
+2. If updating, run the update.php script following the standard procedure for Drupal updates.
+
+POST-INSTALLATION/CONFIGURATION:
+============
+- First of all review Drupal language settings and make sure you have chosen the right default language.
+- Enable the needed modules grouped under "Internationalization" package
+- Read the on-line handbook on
+
+IMPORTANT:
+==========
+- This module requires a complex set up, make sure you read the handbook and understand the different options
+- Before creating a support request, do read the handbook: http://drupal.org/node/133977
+
+Additional Support
+==================
+For support, please create a support request for this module's project: http://drupal.org/project/i18n
+
+Support questions by email to the module maintainer will be simply ignored. Use the issue tracker.
+
+====================================================================
+Jose A. Reyero, freelance at reyero dot net, http://www.reyero.net
30 README.txt
@@ -0,0 +1,30 @@
+// $Id: README.txt,v 1.18.2.5 2009/11/24 15:53:44 jareyero Exp $
+
+README.txt
+==========
+
+********************************************************************
+This is i18n package 6.x, and works with Drupal 6.x
+********************************************************************
+WARNING: DO READ THE INSTALL FILE AND the ON-LINE HANDBOOK
+********************************************************************
+
+This is a collection of modules providing multilingual features.
+These modules will build onto Drupal 6 core features enabling a full multilingual site
+
+Up to date documentation will be kept on-line at http://drupal.org/node/133977
+
+SimpleTest:
+-----------
+Tests for this module will run on SimpleTest 6.x-2.8 (old version).
+About this see http://drupal.org/node/584596
+
+Additional Support
+=================
+For support, please create a support request for this module's project:
+ http://drupal.org/project/i18n
+
+Support questions by email to the module maintainer will be simply ignored. Use the issue tracker.
+
+====================================================================
+Jose A. Reyero, drupal at reyero dot net, http://www.reyero.net
63 i18n.admin.inc
@@ -0,0 +1,63 @@
+<?php
+// $Id: i18n.admin.inc,v 1.2.2.7 2009/01/21 13:08:35 jareyero Exp $
+
+/**
+ * @file
+ * Extended multilanguage administration and module settings UI.
+ */
+
+/**
+ * Form builder function.
+ *
+ * TO DO: Add exclude paths for content selection
+ * Some options have been removed from previous versions:
+ * - Languages are now taken from locale module unless defined in settings file.
+ * - Language dependent tables are authomatically used if defined in settings file.
+ */
+function i18n_admin_settings() {
+ // Content selection options.
+ $form['selection'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Content selection'),
+ //'#collapsible' => TRUE,
+ //'#collapsed' => TRUE,
+ );
+ $form['selection']['i18n_selection_mode'] = array(
+ '#type' => 'radios',
+ '#title' => t('Content selection mode'),
+ '#default_value' => variable_get('i18n_selection_mode', 'simple'),
+ '#options' => _i18n_selection_mode(),
+ '#description' => t('Determines which content to show depending on the current page language and the default language of the site.'),
+ );
+
+ // Node translation links setting.
+ $form['links'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Content translation links'),
+ );
+ $form['links']['i18n_hide_translation_links'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Hide content translation links'),
+ '#description' => t('Hide the links to translations in content body and teasers. If you choose this option, switching language will only be available from the language switcher block.'),
+ '#default_value' => variable_get('i18n_hide_translation_links', 0),
+ );
+ $form['links']['i18n_translation_switch'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Switch interface for translating'),
+ '#default_value' => variable_get('i18n_translation_switch', 0),
+ '#description' => t('Switch interface language to fit node language when creating or editing a translation. If not checked the interface language will be independent from node language.'),
+ );
+ return system_settings_form($form);
+}
+
+// List of selection modes
+function _i18n_selection_mode() {
+ return array(
+ 'simple' => t('Current language and language neutral.'),
+ 'mixed' => t('Mixed current language (if available) or default language (if not) and language neutral.'),
+ 'default' => t('Only default language and language neutral.'),
+ 'strict' => t('Only current language.'),
+ 'off' => t('All content. No language conditions apply.'),
+ );
+}
+
7 i18n.info
@@ -0,0 +1,7 @@
+; $Id: i18n.info,v 1.5.2.5 2008/11/16 11:05:19 jareyero Exp $
+name = Internationalization
+description = Extends Drupal support for multilingual features.
+dependencies[] = locale
+dependencies[] = translation
+package = Multilanguage
+core = 6.x
121 i18n.install
@@ -0,0 +1,121 @@
+<?php
+// $Id: i18n.install,v 1.15.2.9 2009/11/05 18:41:20 jareyero Exp $
+
+/**
+ * @file
+ * Installation file for Internationalization (i18n) module.
+ */
+
+/**
+ * Implementation of hook_schema().
+ */
+function i18n_schema() {
+ $schema['i18n_variable'] = array(
+ 'description' => 'Multilingual variables.',
+ 'fields' => array(
+ 'name' => array(
+ 'description' => 'The name of the variable.',
+ 'type' => 'varchar',
+ 'length' => 128,
+ 'not null' => TRUE,
+ 'default' => ''),
+ 'language' => array(
+ 'description' => 'The language of the variable.',
+ 'type' => 'varchar',
+ 'length' => 12,
+ 'not null' => TRUE,
+ 'default' => ''),
+ 'value' => array(
+ 'description' => 'The value of the variable.',
+ 'type' => 'text',
+ 'not null' => TRUE,
+ 'size' => 'big'),
+ ),
+ 'primary key' => array('name', 'language'),
+ );
+ return $schema;
+}
+
+/**
+ * Set language field in its own table
+ * Do not drop node.language now, just in case
+ * TO-DO: Drop old tables, fields
+ */
+function i18n_install() {
+ // Create database tables
+ drupal_install_schema('i18n');
+ // Set module weight for it to run after core modules
+ db_query("UPDATE {system} SET weight = 10 WHERE name = 'i18n' AND type = 'module'");
+}
+
+function i18n_uninstall() {
+ drupal_uninstall_schema('i18n');
+
+ variable_del('i18n_hide_translation_links');
+ variable_del('i18n_selection_mode');
+ foreach (array_keys(node_get_types()) as $type) {
+ variable_del('i18n_node_'. $type);
+ }
+}
+
+/**
+ * Drupal 6 upgrade. I have started with the wrong numbering, cannot change it now.
+ */
+function i18n_update_9() {
+ // Update content type settings
+ foreach (array_keys(node_get_types()) as $type) {
+ if (variable_get('i18n_node_'. $type, 0)) {
+ variable_set('language_content_type_'. $type, TRANSLATION_ENABLED);
+ }
+ }
+ // General language settings
+ if (variable_get('i18n_browser', 0)) {
+ variable_set('language_negotiation', LANGUAGE_NEGOTIATION_PATH);
+ }
+ else {
+ variable_set('language_negotiation', LANGUAGE_NEGOTIATION_PATH_DEFAULT);
+ }
+ // Set module weight for it to run after core modules
+ $items[] = update_sql("UPDATE {system} SET weight = 10 WHERE name = 'i18n' AND type = 'module'");
+
+ switch ($GLOBALS['db_type']) {
+ case 'mysql':
+ case 'mysqli':
+ // Move node language and trid into node table
+ $items[] = update_sql("UPDATE {node} n INNER JOIN {i18n_node} i ON n.nid = i.nid SET n.language = i.language, n.tnid = i.trid");
+ // Upgrade tnid's so they match one of the nodes nid's to avoid
+ // future conflicts when translating existing nodes
+ $items[] = update_sql("UPDATE {node} n SET n.tnid = (SELECT MIN(i.nid) FROM {i18n_node} i WHERE i.trid = n.tnid) WHERE n.tnid > 0");
+ break;
+ case 'pgsql':
+ // Move node language and trid into node table
+ $items[] = update_sql("UPDATE {node} SET language = {i18n_node}.language, tnid = {i18n_node}.trid FROM {i18n_node} WHERE {node}.nid = {i18n_node}.nid");
+ // Upgrade tnid's so they match one of the nodes nid's to avoid
+ // future conflicts when translating existing nodes
+ $items[] = update_sql("UPDATE {node} SET tnid = (SELECT MIN(i.nid) FROM {i18n_node} i WHERE i.trid = {node}.tnid) WHERE tnid > 0");
+ }
+
+ return $items;
+}
+
+/**
+ * Drupal 6 clean up. To uncomment after making sure all previous updates work
+ */
+/*
+function i18n_update_10() {
+ // Drop old tables
+ $items[] = update_sql("DROP TABLE {i18n_node}");
+
+ // Delete variables. Most settings will be now handled by Drupal core.
+ variable_del('i18n_allow');
+ variable_del('i18n_browser');
+ variable_del('i18n_content');
+ variable_del('i18n_keep');
+ variable_del('i18n_multi');
+ variable_del('i18n_interface');
+ variable_del('i18n_default');
+ variable_del('i18n_supported_langs');
+ variable_del('i18n_translation_links');
+ variable_del('i18n_translation_node_links');
+ return $items;
+}*/
17 i18n.js
@@ -0,0 +1,17 @@
+// $Id: i18n.js,v 1.1.2.3 2009/01/20 20:35:55 nedjo Exp $
+
+/**
+ * Rewrite autocomplete inputs to pass the language of the node currently being
+ * edited in the path.
+ *
+ * This functionality ensures node autocompletes get suggestions for the node's
+ * language rather than the current interface language.
+ */
+Drupal.behaviors.i18n = function (context) {
+ if (Drupal.settings && Drupal.settings.i18n) {
+ $('form[id^=node-form]', context).find('input.autocomplete[value^=' + Drupal.settings.i18n.interface_path + ']').each(function () {
+ $(this).val($(this).val().replace(Drupal.settings.i18n.interface_path, Drupal.settings.i18n.content_path));
+ });
+ }
+};
+
966 i18n.module
@@ -0,0 +1,966 @@
+<?php
+// $Id: i18n.module,v 1.41.2.50 2010/09/09 12:23:48 jareyero Exp $
+
+/**
+ * @file
+ * Internationalization (i18n) module.
+ *
+ * This module extends multilingual support being the base module for the i18n package.
+ * - Multilingual variables
+ * - Extended languages for nodes
+ * - Extended language API
+ *
+ * @author Jose A. Reyero, 2004
+ */
+
+// Some constants. Language support modes for content
+define('LANGUAGE_SUPPORT_NONE', 0);
+define('LANGUAGE_SUPPORT_NORMAL', 1);
+define('LANGUAGE_SUPPORT_EXTENDED', 2);
+define('LANGUAGE_SUPPORT_EXTENDED_NOT_DISPLAYED', 3);
+
+/**
+ * Implementation of hook_boot()
+ *
+ * Initialize variables, that will be used to decide on site_frontpage
+ */
+function i18n_boot() {
+ drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE);
+ _i18n_init();
+}
+
+/**
+ * Implementation of hook_init().
+ *
+ * Special fix for site_frontpage, that may have been used before the language variables are loaded.
+ */
+function i18n_init() {
+ // If not in bootstrap, variable init. Otherwise we are serving a cached page so we don't need anything else.
+ if (!_i18n_is_bootstrap()) {
+ _i18n_init(TRUE);
+ _i18n_init_mode();
+ }
+}
+
+/**
+ * Initialize multilingual variables and use them for site_frontpage
+ *
+ * Special fix for site_frontpage, that may have been used before the language variables are loaded.
+ */
+function _i18n_init($check_frontpage = FALSE) {
+ static $done, $default_frontpage;
+
+ // Prevent this function from running twice;
+ if (!isset($done)) {
+ $done = TRUE;
+ $default_frontpage = variable_get('site_frontpage', 'node');
+ i18n_variable_init();
+ }
+ // We do aditional frontpage check if this has run after first bootstrap phase.
+ // But if this runs in hook_boot we should be ok
+ if ($check_frontpage && $default_frontpage != variable_get('site_frontpage', 'node') && $_GET['q'] == drupal_get_normal_path($default_frontpage)) {
+ $_GET['q'] = drupal_get_normal_path(variable_get('site_frontpage', 'node'));
+ }
+}
+
+/**
+ * Initialize selection mode
+ */
+function _i18n_init_mode() {
+ if (i18n_selection_mode() != 'off') {
+ // Node language when loading specific nodes or creating translations.
+ if (arg(0) == 'node' ) {
+ if (($node = menu_get_object('node')) && $node->language) {
+ i18n_selection_mode('node', $node->language);
+ }
+ elseif (arg(1) == 'add' && !empty($_GET['translation']) && !empty($_GET['language'])) {
+ i18n_selection_mode('translation', db_escape_string($_GET['language']));
+ }
+ }
+ elseif (arg(0) == 'admin') {
+ // There are some exceptions for admin pages.
+ if (arg(1) == 'content' && user_access('administer all languages')) {
+ // No restrictions for administration pages.
+ i18n_selection_mode('off');
+ }
+ elseif (arg(1) == 'build' && (arg(2) == 'menu-customize' || arg(2) == 'menu')) {
+ // All nodes available when editing custom menu items.
+ i18n_selection_mode('off');
+ }
+ }
+ }
+}
+/**
+ * Implementation of hook_help().
+ */
+function i18n_help($path = 'admin/help#i18n', $arg) {
+ switch ($path) {
+ case 'admin/help#i18n' :
+ $output = '<p>'. t('This module improves support for multilingual content in Drupal sites:') .'</p>';
+ $output .= '<ul>';
+ $output .= '<li>'. t('Shows content depending on page language.') .'</li>';
+ $output .= '<li>'. t('Handles multilingual variables.') .'</li>';
+ $output .= '<li>'. t('Extended language option for chosen content types. For these content types transations will be allowed for all defined languages, not only for enabled ones.') .'</li>';
+ $output .= '<li>'. t('Provides a block for language selection and two theme functions: <em>i18n_flags</em> and <em>i18n_links</em>.') .'</li>';
+ $output .= '</ul>';
+ $output .= '<p>'. t('This is the base module for several others adding different features:') .'</p>';
+ $output .= '<ul>';
+ $output .= '<li>'. t('Multilingual menu items.') .'</li>';
+ $output .= '<li>'. t('Multilingual taxonomy adds a language field for taxonomy vocabularies and terms.') .'</li>';
+ $output .= '</ul>';
+ $output .= '<p>'. t('For more information, see the online handbook entry for <a href="@i18n">Internationalization module</a>.', array('@i18n' => 'http://drupal.org/node/133977')) .'</p>';
+ return $output;
+
+ case 'admin/settings/language/i18n':
+ $output = '<ul>';
+ $output .= '<li>'. t('To enable multilingual support for specific content types go to <a href="@configure_content_types">configure content types</a>.', array('@configure_content_types' => url('admin/content/types'))) .'</li>';
+ $output .= '</ul>';
+ return $output;
+ }
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function i18n_menu() {
+ $items['admin/settings/language/i18n'] = array(
+ 'title' => 'Multilingual system',
+ 'description' => 'Configure extended options for multilingual content and translations.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('i18n_admin_settings'),
+ 'access arguments' => array('administer site configuration'),
+ 'file' => 'i18n.admin.inc',
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 10,
+ );
+ $items['admin/settings/language/i18n/configure'] = array(
+ 'title' => 'Multilingual system',
+ 'description' => 'Configure extended options for multilingual content and translations.',
+ //'page callback' => 'drupal_get_form',
+ //'page arguments' => array('i18n_admin_settings'),
+ //'access arguments' => array('administer site configuration'),
+ 'file' => 'i18n.admin.inc',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ );
+ // Autocomplete callback for nodes
+ $items['i18n/node/autocomplete'] = array(
+ 'title' => 'Node title autocomplete',
+ 'page callback' => 'i18n_node_autocomplete',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ 'file' => 'i18n.pages.inc',
+ );
+ return $items;
+}
+
+/**
+ * Implementation of hook_menu_alter().
+ *
+ * Take over the node translation page.
+ */
+function i18n_menu_alter(&$items) {
+ $items['node/%node/translate']['page callback'] = 'i18n_translation_node_overview';
+ $items['node/%node/translate']['file'] = 'i18n.pages.inc';
+ $items['node/%node/translate']['module'] = 'i18n';
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ */
+function i18n_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
+ global $language;
+
+ if (variable_get('language_content_type_' . $node->type, 0)) {
+ // Set current language for new nodes if option enabled
+ if ($op == 'prepare' && empty($node->nid) && empty($node->language) && variable_get('i18n_newnode_current_' . $node->type, 0)) {
+ $node->language = $language->language;
+ }
+ }
+}
+
+/**
+ * Implementation of hook_alter_translation_link().
+ *
+ * Handles links for extended language. The links will have current language.
+ */
+function i18n_translation_link_alter(&$links, $path) {
+ global $language;
+
+ // Check for a node related path, and for its translations.
+ if ((preg_match("!^node/([0-9]+)(/.+|)$!", $path, $matches)) && ($node = node_load((int)$matches[1])) && !empty($node->tnid)) {
+ // make sure language support is set to LANGUAGE_SUPPORT_EXTENDED, so links
+ // dont get added for LANGUAGE_SUPPORT_EXTENDED_NOT_DISPLAYED
+ if (variable_get('i18n_node_'. $node->type, LANGUAGE_SUPPORT_NORMAL) == LANGUAGE_SUPPORT_EXTENDED) {
+ $languages = language_list();
+ $extended = array();
+ foreach (translation_node_get_translations($node->tnid) as $langcode => $translation_node) {
+ if (!isset($links[$langcode]) && isset($languages[$langcode])) {
+ $extended[$langcode] = array(
+ 'href' => 'node/'. $translation_node->nid . $matches[2],
+ 'language' => $language,
+ 'language_icon' => $languages[$langcode],
+ 'title' => $languages[$langcode]->native,
+ 'attributes' => array('class' => 'language-link'),
+ );
+ }
+ }
+ // This will run after languageicon module, so we add icon in case that one is enabled.
+ if ($extended && function_exists('languageicons_translation_link_alter')) {
+ languageicons_translation_link_alter($extended, $path);
+ }
+ $links = array_merge($links, $extended);
+ }
+ }
+}
+
+/**
+ * Implementation of hook_link_alter().
+ *
+ * Handles links for extended languages. Sets current interface language.
+ */
+function i18n_link_alter(&$links, $node) {
+ global $language;
+
+ $language_support = variable_get('i18n_node_'. $node->type, LANGUAGE_SUPPORT_NORMAL);
+
+ // Hide node translation links.
+ if (variable_get('i18n_hide_translation_links', 0) == 1) {
+ foreach ($links as $module => $link) {
+ if (strpos($module, 'node_translation') === 0) {
+ unset($links[$module]);
+ }
+ }
+ }
+
+ if (!empty($node->tnid)) {
+ foreach (array_keys(i18n_language_list('extended')) as $langcode) {
+ $index = 'node_translation_'. $langcode;
+ if (!empty($links[$index])) {
+ if ($language_support != LANGUAGE_SUPPORT_EXTENDED && $links[$index]['language']->enabled == 0) {
+ unset($links[$index]);
+ }
+ else {
+ $links[$index]['language'] = $language;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Implementation of hook_user().
+ *
+ * Switch to user's language after login.
+ */
+function i18n_user($op, &$edit, &$account, $category = NULL) {
+ if ($op == 'login' && $account->language) {
+ $_SESSION['language'] = $account->language;
+ i18n_get_lang($account->language);
+ }
+}
+
+/**
+ * Implementation of hook_elements().
+ *
+ * Add a process callback for textfields.
+ */
+function i18n_elements() {
+ $type = array();
+ $type['textfield'] = array('#process' => array('i18n_textfield_process'));
+ return $type;
+}
+
+/**
+ * Process callback for textfield elements.
+ *
+ * When editing or translating a node, set Javascript to rewrite autocomplete
+ * paths to use the node language prefix rather than the current content one.
+ */
+function i18n_textfield_process($element) {
+ global $language;
+ static $sent = FALSE;
+
+ // Ensure we send the Javascript only once.
+ if (!$sent && isset($element['#autocomplete_path']) && !empty($element['#autocomplete_path']) && variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) != LANGUAGE_NEGOTIATION_NONE) {
+ // Add a JS file for node forms.
+ // Determine if we are either editing or translating an existing node.
+ // We can't act on regular node creation because we don't have a specified
+ // node language.
+ $node_edit = $node = menu_get_object() && arg(2) == 'edit' && isset($node->language) && !empty($node->language);
+ $node_translate = arg(0) == 'node' && arg(1) == 'add' && !empty($_GET['translation']) && !empty($_GET['language']);
+ if ($node_edit || $node_translate) {
+ $node_language = $node_edit ? $node->language : $_GET['language'];
+ // Only needed if the node language is different from the interface one.
+ if ($node_language != $language->language) {
+ $languages = language_list();
+ if (isset($languages[$node_language])) {
+ drupal_add_js(drupal_get_path('module', 'i18n') . '/i18n.js');
+ // Pass the interface and content language base paths.
+ // Remove any trailing forward slash. Doing so prevents a mismatch
+ // that occurs when a language has no prefix and hence gets a path
+ // with a trailing forward slash.
+ $interface = rtrim(url('', array('absolute' => TRUE)), '/');
+ $content = rtrim(url('', array('absolute' => TRUE, 'language' => $languages[$node_language])), '/');
+ $data = array('interface_path' => $interface, 'content_path' => $content);
+ drupal_add_js(array('i18n' => $data), 'setting');
+ }
+ }
+ }
+ $sent = TRUE;
+ }
+ return $element;
+}
+
+/**
+ * Simple i18n API
+ */
+
+/**
+ * Get language properties.
+ *
+ * @param $code
+ * Language code.
+ * @param $property
+ * It may be 'name', 'native', 'ltr'...
+ */
+function i18n_language_property($code, $property) {
+ $languages = language_list();
+ return isset($languages[$code]->$property) ? $languages[$code]->$property : NULL;
+}
+
+/**
+ * Get node language.
+ */
+function i18n_node_get_lang($nid, $default = '') {
+ $lang = db_result(db_query('SELECT language FROM {node} WHERE nid = %d', $nid));
+ return $lang ? $lang : $default ;
+}
+
+/**
+ * Get allowed languages for node.
+ *
+ * This allows node types to define its own language list implementing hook 'language_list'.
+ *
+ * @param $node
+ * Node to retrieve language list for.
+ * @param $translate
+ * Only languages available for translation. Filter out existing translations.
+ */
+function i18n_node_language_list($node, $translate = FALSE) {
+ // Check if the node module manages its own language list.
+ $languages = node_invoke($node, 'language_list', $translate);
+
+ if (!$languages) {
+ if (variable_get('i18n_node_'. $node->type, 0) >= LANGUAGE_SUPPORT_EXTENDED) {
+ $languages = locale_language_list('name', TRUE); // All defined languages
+ }
+ else {
+ $languages = locale_language_list(); // All enabled languages
+ }
+ if ($translate && isset($node->tnid) && $node->tnid && ($translations = translation_node_get_translations($node->tnid))) {
+ unset($translations[$node->language]);
+ foreach (array_keys($translations) as $langcode) {
+ unset($languages[$langcode]);
+ }
+ }
+ // Language may be locked for this node type, restrict options to current one
+ if (variable_get('i18n_lock_node_' . $node->type, 0) && !empty($node->language) && !empty($languages[$node->language])) {
+ $languages = array($node->language => $languages[$node->language]);
+ }
+ // Check language required for this type (no language neutral)
+ elseif (!variable_get('i18n_required_node_' . $node->type, 0)) {
+ $languages = array('' => t('Language neutral')) + $languages;
+ }
+ }
+
+ return $languages;
+}
+
+/**
+ * Selection mode for content.
+ *
+ * Warning: when used with params they need to be escaped, as some values are thrown directly in queries.
+ *
+ * Allows several modes for query rewriting and to change them programatically.
+ * off = No language conditions inserted.
+ * simple = Only current language and no language.
+ * mixed = Only current and default languages.
+ * strict = Only current language.
+ * default = Only default language.
+ * user = User defined, in the module's settings page.
+ * params = Gets the stored params.
+ * reset = Returns to previous.
+ * custom = add custom where clause, like "%alias.language = 'en'".
+ */
+function i18n_selection_mode($mode = NULL, $params = NULL) {
+ static $current_mode;
+ static $current_value = '';
+ static $store = array();
+
+ // Initialization, first time this runs
+ if (!isset($current_mode)) {
+ $current_mode = variable_get('i18n_selection_mode', 'simple');
+ }
+
+ if (!$mode) {
+ return $current_mode;
+ }
+ elseif ($mode == 'params') {
+ return $current_value;
+ }
+ elseif ($mode == 'reset') {
+ list($current_mode, $current_value) = array_pop($store);
+ }
+ else {
+ array_push($store, array($current_mode, $current_value));
+ $current_mode = $mode;
+ $current_value = $params;
+ }
+}
+
+/**
+ * Implementation of hook_db_rewrite_sql().
+ *
+ * Rewrite node queries so language selection options are enforced.
+ */
+function i18n_db_rewrite_sql($query, $primary_table, $primary_key, $args = array()) {
+ // If mode is 'off' = no rewrite, we cannot return any empty 'where' string so check here.
+ $mode = i18n_selection_mode();
+ if ($mode == 'off') return;
+
+ // Disable language conditions for views.
+ if (array_key_exists('view', $args)) return;
+
+ switch ($primary_table) {
+ case 'n':
+ case 'node':
+ // No rewrite for queries with subselect ? (views count queries).
+ // @ TO DO Actually these queries look un-rewrittable, check with other developers.
+ if (preg_match("/FROM \(SELECT/", $query)) return;
+ // No rewrite for translation module queries.
+ if (preg_match("/.*FROM {node} $primary_table WHERE.*$primary_table\.tnid/", $query)) return;
+ // When loading specific nodes, language conditions shouldn't apply.
+ if (preg_match("/WHERE.*\s$primary_table.nid\s*=\s*(\d|%d)/", $query)) return;
+ // If language conditions already there, get out.
+ if (preg_match("/i18n/", $query)) return;
+
+
+ // Mixed mode is a bit more complex, we need to join in one more table
+ // and add some more conditions, but only if language is not default.
+ if ($mode == 'mixed') {
+ $result['where'] = i18n_db_rewrite_where($primary_table, 'node', 'simple');
+ if (i18n_get_lang() != i18n_default_language()) {
+ $result['join'] = "LEFT JOIN {node} i18n ON $primary_table.tnid > 0 AND $primary_table.tnid = i18n.tnid AND i18n.language = '". i18n_get_lang() ."'";
+ // So we show also nodes that have default language.
+ $result['where'] .= " OR $primary_table.language = '". i18n_default_language() ."' AND i18n.nid IS NULL";
+ }
+ }
+ else {
+ $result['where'] = i18n_db_rewrite_where($primary_table, 'node', $mode);
+ }
+ return $result;
+ }
+}
+
+/**
+ * Rewrites queries depending on rewriting mode.
+ */
+function i18n_db_rewrite_where($alias, $type, $mode = NULL) {
+ if (!$mode) {
+ // Some exceptions for query rewrites.
+ $mode = i18n_selection_mode();
+ }
+
+ // Get languages to simplify query building.
+ $current = i18n_get_lang();
+ $default = i18n_default_language();
+
+ if ($mode == 'strict' && $type != 'node') {
+ // Special case. Selection mode is 'strict' but this should be only for node queries.
+ $mode = 'simple';
+ }
+ elseif ($mode == 'mixed' && $current == $default) {
+ // If mode is mixed but current = default, is the same as 'simple'.
+ $mode = 'simple';
+ }
+
+ switch ($mode) {
+ case 'off':
+ return '';
+
+ case 'simple':
+ return "$alias.language ='$current' OR $alias.language ='' OR $alias.language IS NULL" ;
+
+ case 'mixed':
+ return "$alias.language ='$current' OR $alias.language ='$default' OR $alias.language ='' OR $alias.language IS NULL" ;
+
+ case 'strict':
+ return "$alias.language ='$current'" ;
+
+ case 'node':
+ case 'translation':
+ return "$alias.language ='". i18n_selection_mode('params') ."' OR $alias.language ='' OR $alias.language IS NULL" ;
+
+ case 'default':
+ return "$alias.language ='$default' OR $alias.language ='' OR $alias.language IS NULL" ;
+
+ case 'custom':
+ return str_replace('%alias', $alias, i18n_selection_mode('params'));
+ }
+}
+
+/**
+ * Implementation of hook_preprocess_page().
+ *
+ * Add the language code to the classes for the <body> tag. Unfortunately, some
+ * themes will not respect the variable we're modifying to achieve this - in
+ * particular, Garland and Minelli do not.
+ */
+function i18n_preprocess_page(&$variables) {
+ if (isset($variables['body_classes'])) {
+ global $language;
+ $variables['body_classes'] .= ' i18n-' . $language->language;
+ }
+}
+
+/**
+ * Implementation of hook_exit().
+ */
+function i18n_exit() {
+ _i18n_variable_exit();
+}
+
+/**
+ * Implementation of hook_form_alter();
+ *
+ * This is the place to add language fields to all forms.
+ */
+function i18n_form_alter(&$form, $form_state, $form_id) {
+ global $language;
+
+ switch ($form_id) {
+ case 'node_type_form':
+ $disabled = !variable_get('language_content_type_'. $form['#node_type']->type, 0);
+ $form['i18n'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Multilanguage options'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE,
+ '#description' => t('Extended multilingual options provided by Internationalization module.'),
+ '#disabled' => $disabled,
+ );
+ // Add disabled message
+ if ($disabled) {
+ $form['i18n']['#description'] .= ' <em>' . t('These will be available only when you enable Multilingual support in Workflow settings above.') . '</em>';
+ }
+ // Some settings about node languages
+ $form['i18n']['options'] = array(
+ '#title' => t('Options for node language'),
+ '#type' => 'fieldset',
+ '#disabled' => $disabled,
+ );
+ $form['i18n']['options']['i18n_newnode_current'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Set current language as default for new content.'),
+ '#default_value' => variable_get('i18n_newnode_current_' . $form['#node_type']->type, 0),
+ '#disabled' => $disabled,
+ );
+ $form['i18n']['options']['i18n_required_node'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Require language (Do not allow Language Neutral).'),
+ '#default_value' => variable_get('i18n_required_node_' . $form['#node_type']->type, 0),
+ '#disabled' => $disabled,
+ );
+ $form['i18n']['options']['i18n_lock_node'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Lock language (Cannot be changed).'),
+ '#default_value' => variable_get('i18n_lock_node_' . $form['#node_type']->type, 0),
+ '#disabled' => $disabled,
+ );
+ // Add extended language support option to content type form.
+ $form['i18n']['i18n_node'] = array(
+ '#type' => 'radios',
+ '#title' => t('Extended language support'),
+ '#default_value' => variable_get('i18n_node_'. $form['#node_type']->type, LANGUAGE_SUPPORT_NORMAL),
+ '#options' => _i18n_content_language_options(),
+ '#description' => t('If enabled, all defined languages will be allowed for this content type in addition to only enabled ones. This is useful to have more languages for content than for the interface.'),
+ '#disabled' => $disabled,
+ );
+ break;
+
+ default:
+ // Extensions for node edit forms
+ if (isset($form['#id']) && $form['#id'] == 'node-form') {
+ if (isset($form['#node']->type)) {
+ if (variable_get('language_content_type_'. $form['#node']->type, 0)) {
+ if (!empty($form['language']['#options'])) {
+ $form['language']['#options'] = i18n_node_language_list($form['#node'], TRUE);
+ }
+ }
+ elseif (!isset($form['#node']->nid)) {
+ // Set language to empty for not multilingual nodes when creating
+ $form['language'] = array('#type' => 'value', '#value' => '');
+ }
+ }
+ }
+
+ // Multilingual variables in settings form.
+ if (isset($form['#theme']) && $form['#theme'] == 'system_settings_form' && $variables = i18n_variable()) {
+ if ($i18n_variables = i18n_form_alter_settings($form, $variables)) {
+ array_unshift($form['#submit'], 'i18n_variable_form_submit');
+ $form['#i18n_variables'] = $i18n_variables;
+ }
+ }
+ }
+}
+
+/**
+ * Implementation of hook_perm().
+ *
+ * Permissions defined
+ * - administer all languages
+ * Disables language conditions for administration pages, so the user can view objects for all languages at the same time.
+ * This applies for: menu items, taxonomy
+ * - administer translations
+ * Will allow to add/remove existing nodes to/from translation sets.
+ */
+function i18n_perm() {
+ return array('administer all languages', 'administer translations');
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function i18n_theme() {
+ return array(
+ 'i18n_node_select_translation' => array(
+ 'arguments' => array('element' => NULL),
+ 'file' => 'i18n.pages.inc',
+ ),
+ );
+}
+
+/**
+ * Process menu and menu item add/edit form submissions.
+ */
+function i18n_menu_edit_item_form_submit($form, &$form_state) {
+ $mid = menu_edit_item_save($form_state['values']);
+ db_query("UPDATE {menu} SET language = '%s' WHERE mid = %d", $form_state['values']['language'], $mid);
+ return 'admin/build/menu';
+}
+
+/**
+ * Check for multilingual variables in form.
+ */
+function i18n_form_alter_settings(&$form, &$variables) {
+ $result = array();
+ foreach (element_children($form) as $field) {
+ if (count(element_children($form[$field])) && empty($form[$field]['#tree'])) {
+ $result += i18n_form_alter_settings($form[$field], $variables);
+ }
+ elseif (in_array($field, $variables)) {
+ // Add form field class: i18n-variable
+ $form[$field]['#attributes']['class'] = !empty($form[$field]['#attributes']['class']) ? $form[$field]['#attributes']['class'] . ' i18n-variable' : 'i18n-variable';
+ $form[$field]['#description'] = !empty($form[$field]['#description']) ? $form[$field]['#description'] : '';
+ $form[$field]['#description'] .= ' <strong>'. t('This is a multilingual variable.') .'</strong>';
+ // Addd field => name to result
+ $result[$field] = !empty($form[$field]['#title']) ? $form[$field]['#title'] : $field;
+ }
+ }
+ return $result;
+}
+
+/**
+ * Save multilingual variables and remove them from form.
+ */
+function i18n_variable_form_submit($form, &$form_state) {
+ $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
+ $variables = i18n_variable();
+ $language = i18n_get_lang();
+ $is_default = $language == language_default('language');
+ foreach ($form_state['values'] as $key => $value) {
+ if (i18n_variable($key)) {
+ if ($op == t('Reset to defaults')) {
+ i18n_variable_del($key, $language);
+ }
+ else {
+ if (is_array($value) && isset($form_state['values']['array_filter'])) {
+ $value = array_keys(array_filter($value));
+ }
+ i18n_variable_set($key, $value, $language);
+ }
+ // If current is default language, we allow global (without language) variables to be set too
+ if (!$is_default) {
+ unset($form_state['values'][$key]);
+ }
+ }
+ }
+ // The form will go now through system_settings_form_submit()
+}
+
+/**
+ * Initialization of multilingual variables.
+ *
+ * @param $langcode
+ * Language to retrieve variables. Defaults to current language.
+ */
+function i18n_variable_init($langcode = NULL) {
+ global $conf;
+
+ $langcode = $langcode ? $langcode : i18n_get_lang();
+ if ($variables = _i18n_variable_init($langcode)) {
+ $conf = array_merge($conf, $variables);
+ }
+}
+
+/**
+ * Get language from context.
+ */
+function _i18n_get_context_lang() {
+ // Node language when loading specific nodes or creating translations.
+ if (arg(0) == 'node' ) {
+ if (($node = menu_get_object('node')) && $node->language) {
+ return $node->language;
+ }
+ elseif (arg(1) == 'add' && !empty($_GET['translation']) && !empty($_GET['language'])) {
+ return $_GET['language'];
+ }
+ }
+}
+
+/**
+ * Helper function to create language selector.
+ */
+function _i18n_language_select($value ='', $description ='', $weight = -20, $languages = NULL) {
+ $languages = $languages ? $languages : locale_language_list();
+ return array(
+ '#type' => 'select',
+ '#title' => t('Language'),
+ '#default_value' => $value,
+ '#options' => array_merge(array('' => ''), $languages),
+ '#description' => $description,
+ '#weight' => $weight,
+ );
+}
+
+/**
+ * Load language variables into array.
+ */
+function _i18n_variable_init($langcode) {
+ global $i18n_conf;
+
+ if (!isset($i18n_conf[$langcode])) {
+ $cacheid = 'variables:'. $langcode;
+ if ($cached = cache_get($cacheid)) {
+ $i18n_conf[$langcode] = $cached->data;
+ }
+ else {
+ $result = db_query("SELECT * FROM {i18n_variable} WHERE language = '%s'", $langcode);
+ $i18n_conf[$langcode] = array();
+ while ($variable = db_fetch_object($result)) {
+ $i18n_conf[$langcode][$variable->name] = unserialize($variable->value);
+ }
+ cache_set($cacheid, $i18n_conf[$langcode]);
+ }
+ }
+
+ return $i18n_conf[$langcode];
+}
+
+/**
+ * Save multilingual variables that may have been changed by other methods than settings pages.
+ */
+function _i18n_variable_exit() {
+ global $conf, $i18n_conf;
+
+ $langcode = i18n_get_lang();
+ if (isset($i18n_conf[$langcode])) {
+ $refresh = FALSE;
+ // Rewritten because array_diff_assoc may fail with array variables.
+ foreach (i18n_variable() as $name) {
+ if (isset($conf[$name]) && isset($i18n_conf[$langcode][$name]) && $conf[$name] != $i18n_conf[$langcode][$name]) {
+ $refresh = TRUE;
+ $i18n_conf[$langcode][$name] = $conf[$name];
+ db_query("DELETE FROM {i18n_variable} WHERE name='%s' AND language='%s'", $name, $langcode);
+ db_query("INSERT INTO {i18n_variable} (language, name, value) VALUES('%s', '%s', '%s')", $langcode, $name, serialize($conf[$name]));
+ }
+ }
+ if ($refresh) {
+ cache_set('variables:'. $langcode, $i18n_conf[$langcode]);
+ }
+ }
+}
+
+/**
+ * Check whether we are in bootstrap mode.
+ */
+function _i18n_is_bootstrap() {
+ return !function_exists('drupal_get_headers');
+}
+
+/**
+ * Drupal 6, backwards compatibility layer.
+ *
+ * @ TO DO Fully upgrade all the modules and remove
+ */
+
+/**
+ * This one expects to be called first from common.inc
+ */
+function i18n_get_lang() {
+ global $language;
+ return $language->language;
+}
+
+/**
+ * @defgroup i18n_api Extended language API
+ * @{
+ * This is an extended language API to be used by modules in i18n package.
+ */
+
+/**
+ * Returns language lists.
+ */
+function i18n_language_list($type = 'enabled', $field = 'name') {
+ switch ($type) {
+ case 'enabled':
+ return locale_language_list($field);
+
+ case 'extended':
+ $enabled = locale_language_list($field);
+ $defined = locale_language_list($field, TRUE);
+ return array_diff_assoc($defined, $enabled);
+ }
+}
+
+/**
+ * Returns default language code.
+ */
+function i18n_default_language() {
+ return language_default('language');
+}
+
+/**
+ * Get list of supported languages, native name.
+ *
+ * @param $all
+ * TRUE to get all defined languages.
+ */
+function i18n_supported_languages($all = FALSE) {
+ return locale_language_list('native', $all);
+}
+
+/**
+ * Get list of multilingual variables or check whether a variable is multilingual
+ */
+function i18n_variable($name = NULL) {
+ $variables = variable_get('i18n_variables', array());
+ return $name ? in_array($name, $variables) : $variables;
+}
+
+/**
+ * Set a persistent language dependent variable.
+ *
+ * @param $name
+ * The name of the variable to set.
+ * @param $value
+ * The value to set. This can be any PHP data type; these functions take care
+ * of serialization as necessary.
+ * @param $langcode
+ * Language code.
+ */
+function i18n_variable_set($name, $value, $langcode) {
+ global $conf, $i18n_conf;
+
+ $serialized_value = serialize($value);
+ db_query("UPDATE {i18n_variable} SET value = '%s' WHERE name = '%s' AND language = '%s'", $serialized_value, $name, $langcode);
+ if (!db_affected_rows()) {
+ @db_query("INSERT INTO {i18n_variable} (name, language, value) VALUES ('%s', '%s', '%s')", $name, $langcode, $serialized_value);
+ }
+ cache_clear_all('variables:'. $langcode, 'cache');
+ $i18n_conf[$langcode][$name] = $value;
+ if ($langcode == i18n_get_lang()) {
+ $conf[$name] = $value;
+ }
+}
+
+/**
+ * Get single multilingual variable
+ */
+function i18n_variable_get($name, $langcode, $default = NULL) {
+ if ($variables = _i18n_variable_init($langcode)) {
+ return isset($variables[$name]) ? $variables[$name] : $default;
+ }
+ else {
+ return $default;
+ }
+}
+
+/**
+ * Unset a persistent multilingual variable.
+ *
+ * @param $name
+ * The name of the variable to undefine.
+ * @param $langcode
+ * Optional language code. If not set it will delete the variable for all languages.
+ */
+function i18n_variable_del($name, $langcode = NULL) {
+ global $conf, $i18n_conf;
+
+ if ($langcode) {
+ db_query("DELETE FROM {i18n_variable} WHERE name = '%s' AND language='%s'", $name, $langcode);
+ cache_clear_all('variables:'. $langcode, 'cache');
+ unset($i18n_conf[$langcode][$name]);
+ // If current language, unset also global conf
+ if ($langcode == i18n_get_lang()) {
+ unset($conf[$name]);
+ }
+ }
+ else {
+ db_query("DELETE FROM {i18n_variable} WHERE name = '%s'", $name);
+ if (db_affected_rows()) {
+ cache_clear_all('variables:', 'cache', TRUE);
+ if (is_array($i18n_conf)) {
+ foreach (array_keys($i18n_conf) as $lang) {
+ unset($i18n_conf[$lang][$name]);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Utility. Get part of array variable.
+ */
+function i18n_array_variable_get($name, $element, $default = NULL) {
+ if (($values = variable_get($name, array())) && isset($values[$element])) {
+ return $values[$element];
+ }
+ else {
+ return $default;
+ }
+}
+
+/**
+ * Utility. Set part of array variable.
+ */
+function i18n_array_variable_set($name, $element, $value) {
+ $values = variable_get($name, array());
+ $values[$element] = $value;
+ variable_set($name, $values);
+}
+
+/**
+ * @} End of "defgroup i18n_api".
+ */
+
+/**
+ * List of language support modes for content.
+ */
+function _i18n_content_language_options() {
+ return array(
+ LANGUAGE_SUPPORT_NORMAL => t('Normal - All enabled languages will be allowed.'),
+ LANGUAGE_SUPPORT_EXTENDED => t('Extended - All defined languages will be allowed.'),
+ LANGUAGE_SUPPORT_EXTENDED_NOT_DISPLAYED => t('Extended, but not displayed - All defined languages will be allowed for input, but not displayed in links.'),
+ );
+}
277 i18n.pages.inc
@@ -0,0 +1,277 @@
+<?php
+// $Id: i18n.pages.inc,v 1.1.2.7 2010/06/01 14:15:16 jareyero Exp $
+
+/**
+ * @file
+ * User page callbacks for the translation module.
+ */
+
+/**
+ * Overview page for a node's translations.
+ *
+ * Replacement for Drupal core translation module's pages
+ *
+ * @param $node
+ * Node object.
+ */
+function i18n_translation_node_overview($node) {
+ if ($node->tnid) {
+ // Already part of a set, grab that set.
+ $tnid = $node->tnid;
+ $translations = translation_node_get_translations($node->tnid);
+ }
+ else {
+ // We have no translation source nid, this could be a new set, emulate that.
+ $tnid = $node->nid;
+ $translations = array($node->language => $node);
+ }
+
+ $header = array(t('Language'), t('Title'), t('Status'), t('Operations'));
+
+ foreach (language_list() as $language) {
+ $options = array();
+ $language_name = $language->name;
+ // We may need to switch interface language for translations
+ $params = variable_get('i18n_translation_switch', 0) ? array('language' => $language) : array();
+ if (isset($translations[$language->language])) {
+ // Existing translation in the translation set: display status.
+ // We load the full node to check whether the user can edit it.
+ $translation_node = node_load($translations[$language->language]->nid);
+ $title = l($translation_node->title, 'node/'. $translation_node->nid, $params);
+ if (node_access('update', $translation_node)) {
+ $options[] = l(t('edit'), "node/$translation_node->nid/edit", $params);
+ }
+ $status = $translation_node->status ? t('Published') : t('Not published');
+ $status .= $translation_node->translate ? ' - <span class="marker">'. t('outdated') .'</span>' : '';
+ if ($translation_node->nid == $tnid) {
+ $language_name = t('<strong>@language_name</strong> (source)', array('@language_name' => $language_name));
+ }
+ }
+ else {
+ // No such translation in the set yet: help user to create it.
+ $title = t('n/a');
+ if (node_access('create', $node)) {
+ $options[] = l(t('add translation'), 'node/add/'. str_replace('_', '-', $node->type), array('query' => "translation=$node->nid&language=$language->language") + $params);
+ }
+ $status = t('Not translated');
+ }
+ $rows[] = array($language_name, $title, $status, implode(" | ", $options));
+ }
+
+ drupal_set_title(t('Translations of %title', array('%title' => $node->title)));
+ $output = theme('table', $header, $rows);
+ if (user_access('administer translations')) {
+ $output .= drupal_get_form('i18n_node_select_translation', $node, $translations);
+ }
+ return $output;
+}
+
+/**
+ * Form to select existing nodes as translation
+ *
+ * This one uses autocomplete fields for all languages
+ */
+function i18n_node_select_translation($form_state, $node, $translations) {
+ $form['node'] = array('#type' => 'value', '#value' => $node);
+ $form['translations'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Select translations for %title', array('%title' => $node->title)),
+ '#tree' => TRUE,
+ '#theme' => 'i18n_node_select_translation',
+ '#description' => t("Alternatively, you can select existing nodes as translations of this one or remove nodes from this translation set. Only nodes that have the right language and don't belong to other translation set will be available here.")
+ );
+ foreach (language_list() as $language) {
+ if ($language->language != $node->language) {
+ $trans_nid = isset($translations[$language->language]) ? $translations[$language->language]->nid : 0;
+ $form['translations']['nid'][$language->language] = array('#type' => 'value', '#value' => $trans_nid);
+ $form['translations']['language'][$language->language] = array('#value' => $language->name);
+ $form['translations']['node'][$language->language] = array(
+ '#type' => 'textfield',
+ '#maxlength' => 255,
+ '#autocomplete_path' => 'i18n/node/autocomplete/' . $node->type . '/' . $language->language,
+ '#default_value' => $trans_nid ? i18n_node_nid2autocomplete($trans_nid) : '',
+ );
+ }
+ }
+ $form['buttons']['update'] = array('#type' => 'submit', '#value' => t('Update translations'));
+ //$form['buttons']['clean'] = array('#type' => 'submit', '#value' => t('Delete translation set'));
+ return $form;
+}
+
+/**
+ * Form validation
+ */
+function i18n_node_select_translation_validate($form, &$form_state) {
+ foreach ($form_state['values']['translations']['node'] as $lang => $title) {
+ if (!$title) {
+ $nid = 0;
+ }
+ else {
+ $nid = i18n_node_autocomplete2nid($title, "translations][node][$lang", array($node->type), array($lang));
+ }
+ $form_state['values']['translations']['nid'][$lang] = $nid;
+ }
+}
+
+/**
+ * Form submission: update / delete the translation set
+ */
+function i18n_node_select_translation_submit($form, &$form_state) {
+ $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : NULL;
+ $node = $form_state['values']['node'];
+ $translations = $node->tnid ? translation_node_get_translations($node->tnid) : array($node->language => $node);
+ foreach ($translations as $trans) {
+ $current[$trans->language] = $trans->nid;
+ }
+ $update = array($node->language => $node->nid) + array_filter($form_state['values']['translations']['nid']);
+ // Compute the difference to see which are the new translations and which ones to remove
+ $new = array_diff_assoc($update, $current);
+ $remove = array_diff_assoc($current, $update);
+
+ // The tricky part: If the existing source is not in the new set, we need to create a new tnid
+ if ($node->tnid && in_array($node->tnid, $update)) {
+ $tnid = $node->tnid;
+ $add = $new;
+ }
+ else {
+ // Create new tnid, which is the source node
+ $tnid = $node->nid;
+ $add = $update;
+ }
+ // Now update values for all nodes
+ if ($add) {
+ $args = array('' => $tnid) + $add;
+ db_query('UPDATE {node} SET tnid = %d WHERE nid IN (' . db_placeholders($add) . ')', $args);
+ if (count($new)) {
+ drupal_set_message(format_plural(count($new), 'Added a node to the translation set.', 'Added @count nodes to the translation set.'));
+ }
+ }
+ if ($remove) {
+ db_query('UPDATE {node} SET tnid = 0 WHERE nid IN (' . db_placeholders($remove) . ')', $remove);
+ drupal_set_message(format_plural(count($remove), 'Removed a node from the translation set.', 'Removed @count nodes from the translation set.'));
+ }
+}
+
+/**
+ * Node title autocomplete callback
+ */
+function i18n_node_autocomplete($type, $language, $string = '') {
+ $params = array('type' => $type, 'language' => $language, 'tnid' => 0);
+ $matches = array();
+ foreach (_i18n_node_references($string, 'contains', $params) as $id => $row) {
+ // Add a class wrapper for a few required CSS overrides.
+ $matches[$row['title'] ." [nid:$id]"] = '<div class="reference-autocomplete">'. $row['rendered'] . '</div>';
+ }
+ drupal_json($matches);
+}
+
+/**
+ * Generates 'title [nid:$nid]' for the autocomplete field
+ */
+function i18n_node_nid2autocomplete($nid) {
+ if ($node = node_load($nid)) {
+ return check_plain($node->title) . ' [nid:' . $nid .']';
+ }
+ else {
+ return t('Not found');
+ }
+}
+
+/**
+ * Reverse mapping from node title to nid
+ *
+ * We also handle autocomplete values (title [nid:x]) and validate the form
+ */
+function i18n_node_autocomplete2nid($name, $field = NULL, $type, $language) {
+ if (!empty($name)) {
+ preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $name, $matches);
+ if (!empty($matches)) {
+ // Explicit [nid:n].
+ list(, $title, $nid) = $matches;
+ if (!empty($title) && ($node = node_load($nid)) && $title != $node->title) {
+ if ($field) {
+ form_set_error($field, t('Node title mismatch. Please check your selection.'));
+ }
+ $nid = NULL;
+ }
+ }
+ else {
+ // No explicit nid.
+ $reference = _i18n_node_references($name, 'equals', array('type' => $type, 'language' => $language), 1);
+ if (!empty($reference)) {
+ $nid = key($reference);
+ }
+ elseif ($field) {
+ form_set_error($field, t('Found no valid post with that title: %title', array('%title' => $name)));
+ }
+ }
+ }
+ return !empty($nid) ? $nid : NULL;
+}
+
+/**
+ * Find node title matches.
+ *
+ * @param $string
+ * String to match against node title
+ * @param $match
+ * Match mode: 'contains', 'equals', 'starts_with'
+ * @param $params
+ * Other query arguments: type, language or numeric ones
+ *
+ * Some code from CCK's nodereference.module
+ */
+function _i18n_node_references($string, $match = 'contains', $params = array(), $limit = 10) {
+ $where = $args = array();
+ $match_operators = array(
+ 'contains' => "LIKE '%%%s%%'",
+ 'equals' => "= '%s'",
+ 'starts_with' => "LIKE '%s%%'",
+ );
+ foreach ($params as $key => $value) {
+ $type = in_array($key, array('type', 'language')) ? 'char' : 'int';
+ if (is_array($value)) {
+ $where[] = "n.$key IN (" . db_placeholders($value, $type) . ') ';
+ $args = array_merge($args, $value);
+ }
+ else {
+ $where[] = "n.$key = " . db_type_placeholder($type);
+ $args[] = $value;
+ }
+ }
+ $where[] = 'n.title '. (isset($match_operators[$match]) ? $match_operators[$match] : $match_operators['contains']);
+ $args[] = $string;
+ // Disable and reenable i18n selection mode so no language conditions are inserted
+ i18n_selection_mode('off');
+ $sql = db_rewrite_sql('SELECT n.nid, n.title, n.type FROM {node} n WHERE ' . implode(' AND ', $where) . ' ORDER BY n.title, n.type');
+ $result = db_query_range($sql, $args, 0, $limit) ;
+ i18n_selection_mode('reset');
+ $references = array();
+ while ($node = db_fetch_object($result)) {
+ $references[$node->nid] = array(
+ 'title' => $node->title,
+ 'rendered' => check_plain($node->title),
+ );
+ }
+ return $references;
+}
+
+/**
+ * Theme select translation form
+ * @ingroup themeable
+ */
+function theme_i18n_node_select_translation($elements) {
+ $output = '';
+ if (isset($elements['nid'])) {
+ $rows = array();
+ foreach (element_children($elements['nid']) as $lang) {
+ $rows[] = array(
+ drupal_render($elements['language'][$lang]),
+ drupal_render($elements['node'][$lang]),
+ );
+ }
+ $output .= theme('table', array(), $rows);
+ $output .= drupal_render($elements);
+ }
+ return $output;
+}
6 i18nblocks/i18nblocks.info
@@ -0,0 +1,6 @@
+name = Block translation
+description = Enables multilingual blocks and block translation.
+dependencies[] = i18n
+dependencies[] = i18nstrings
+package = Multilanguage
+core = 6.x
152 i18nblocks/i18nblocks.install
@@ -0,0 +1,152 @@
+<?php
+// $Id: i18nblocks.install,v 1.5.2.11 2010/04/07 19:45:52 jareyero Exp $
+
+/**
+ * @file
+ * Installation file for i18nblocks module.
+ */
+
+// @ TODO Update scripts
+/**
+ * Implementation of hook_install().
+ */
+function i18nblocks_install() {
+ // Create database tables.
+ drupal_install_schema('i18nblocks');
+ // We dont need to change module weight
+ //db_query("UPDATE {system} SET weight = 20 WHERE name = 'i18nblocks' AND type = 'module'");
+}
+
+/**
+ * Implementation of hook_uninstall().
+ */
+function i18nblocks_uninstall() {
+ drupal_uninstall_schema('i18nblocks');
+}
+
+/**
+ * Implementation of hook_schema().
+ */
+function i18nblocks_schema() {
+ $schema['i18n_blocks'] = array(
+ 'description' => 'Special i18n translatable blocks.',
+ 'fields' => array(
+ 'ibid' => array(
+ 'description' => 'The i18n block identifier.',
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE
+ ),
+ 'module' => array(
+ 'type' => 'varchar',
+ 'length' => 64,
+ 'not null' => TRUE,
+ 'description' => "The block's origin module, from {blocks}.module.",
+ ),
+ 'delta' => array(
+ 'type' => 'varchar',
+ 'length' => 32,
+ 'not null' => TRUE,
+ 'default' => '0',
+ 'description' => 'Unique ID for block within a module.',
+ ),
+ 'type' => array(
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Block type.',
+ ),
+ 'language' => array(
+ 'type' => 'varchar',
+ 'length' => 12,
+ 'description' => 'Block language.',
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ ),
+ 'primary key' => array(
+ 'ibid',
+ ),
+ );
+
+ return $schema;
+}
+
+/**
+ * Update: move old variable to new tables.
+ */
+function i18nblocks_update_1() {
+ $ret = array();
+ $t = get_t();
+ require_once drupal_get_path('module', 'i18nblocks') .'/i18nblocks.module';
+ require_once drupal_get_path('module', 'i18n') .'/i18n.module';
+ // Create the tables if updating from previous version.
+ i18nblocks_install();
+ // Move old data from variables into new tables.
+ $languages = i18n_supported_languages();
+ if ($number = variable_get('i18nblocks_number', 0)) {
+ for ($delta = 1; $delta <= $number; $delta++) {
+ if ($block = variable_get('i18nblocks_'. $delta, NULL)) {
+ $update = update_sql("INSERT INTO {i18n_blocks} (delta) VALUES('". db_escape_string($delta) ."')");
+ $ret[] = $update;
+ $metablock = array();
+ if ($update['success']) {
+ $metablock['delta'] = $delta;
+ }
+ $metablock['info'] = isset($block['name']) ? $block['name'] : '';
+ $metablock['i18nblocks'] = array();
+ foreach (array_keys($languages) as $lang) {
+ if (isset($block[$lang]) && isset($block[$lang]['module']) && isset($block[$lang]['delta'])) {
+ $metablock['i18nblocks'][$lang] = $block[$lang]['module'] .':'. $block[$lang]['delta'];
+ }
+ }
+ }
+ i18nblocks_save($metablock);
+ }
+ drupal_set_message($t('The i18nblocks have been updated. Please, review your block settings.'));
+ }
+ return $ret;
+}
+
+/**
+ * Drupal 6 upgrade script.
+ */
+function i18nblocks_update_2() {
+ $ret = array();
+ // Rename old table and install new schema
+ db_rename_table($ret, 'i18n_blocks', 'i18n_blocks_drupal5');
+ drupal_install_schema('i18nblocks');
+ // Fill in new table with old blocks but only for user defined blocks.
+ // The rest will need manual update
+ $ret[] = update_sql("INSERT INTO {i18n_blocks} (module, delta, language) SELECT i.module, i.delta, i.language FROM {i18n_blocks_i18n} i WHERE i.module = 'block'");
+
+ drupal_set_message(t('Multilingual blocks have been updated. Please, review your blocks configuration.'));
+ return $ret;
+}
+
+/**
+ * Drop old tables and fields. Uncomment when the previous one is 100% working.
+ */
+/*
+function i18nblocks_update_3() {
+ $items = array();
+ $items[] = update_sql('DROP TABLE {i18n_blocks_i18n}');
+ $items[] = update_sql('DROP TABLE {i18n_blocks_drupal5}');
+ return $items;
+}*/
+
+/**
+ * Rework block string keys, all must use module, delta
+ */
+function i18nblocks_update_6001() {
+ $ret = array();
+ $result = db_query("SELECT * FROM {i18n_blocks} WHERE module = 'block' AND language = ''");
+ while ($block = db_fetch_object($result)) {
+ foreach (array('title' => 'title', 'content' => 'body') as $property => $rename) {
+ $old = "blocks:block:$block->ibid:$property";
+ $new = "blocks:block:$block->delta:$rename";
+ i18nstrings_update_context($old, $new);
+ }
+ }
+ return $ret;
+}
293 i18nblocks/i18nblocks.module
@@ -0,0 +1,293 @@
+<?php
+// $Id: i18nblocks.module,v 1.7.2.25 2010/08/12 02:06:20 mirodietiker Exp $
+
+/**
+ * @file
+ * Internationalization (i18n) submodule: Multilingual meta-blocks
+ *
+ * @author Jose A. Reyero, 2005
+ *
+ * @ TODO Add strings on block update.
+ */
+
+// Tag for localizable block, cannot be any language.
+define('I18N_BLOCK_LOCALIZE', '__LOCALIZE__');
+// Block type: localizable
+define('I18N_BLOCK_LOCALIZABLE', 1);
+// Block type: block with language
+define('I18N_BLOCK_LANGUAGE', 0);
+
+/**
+ * Block types
+ */
+function _block_types() {
+ return array(
+ I18N_BLOCK_LOCALIZE => t('Localizable block'),
+ I18N_BLOCK_METABLOCK => t('Multilingual block (Metablock)'),
+ );
+}
+
+/**
+ * Implementation of hook_help().
+ */
+function i18nblocks_help($path, $arg) {
+ switch ($path) {
+ case 'admin/help#i18nblocks':
+ $output = '<p>'. t('This module provides support for multilingual blocks.') .'</p>';
+ $output .= '<p>'. t('You can set up a language for a block or define it as translatable:') .'</p>';
+ $output .= '<ul>';
+ $output .= '<li>'. t('Blocks with a language will be displayed only in pages with that language.') .'</li>';
+ $output .= '<li>'. t('Translatable blocks can be translated using the localization interface.') .'</li>';
+ $output .= '</ul>';
+ $output .= '<p>'. t('To search and translate strings, use the <a href="@translate-interface">translation interface</a> pages.', array('@translate-interface' => url('admin/build/translate'))) .'</p>';
+ return $output;
+ }
+}
+
+/**
+ * Implementation of hook_db_rewrite_sql().
+ */
+function i18nblocks_db_rewrite_sql($query, $primary_table, $primary_key) {
+ if ($primary_table == 'b' && $primary_key == 'bid') {
+ $return['join'] = 'LEFT JOIN {i18n_blocks} i18n ON (b.module = i18n.module AND b.delta = i18n.delta)';
+ $return['where'] = i18n_db_rewrite_where('i18n', 'block', 'simple');
+ return $return;
+ }
+}
+
+/**
+ * Implementation of hook_locale().
+ *
+ * This one doesn't need locale refresh because strings are stored from module config form.
+ */
+function i18nblocks_locale($op = 'groups', $group = NULL) {
+ switch ($op) {
+ case 'groups':
+ return array('blocks' => t('Blocks'));
+ case 'info':
+ $info['blocks']['refresh callback'] = 'i18nblocks_locale_refresh';
+ $info['blocks']['format'] = TRUE;
+ return $info;
+ }
+}
+
+/**
+ * Refresh all strings.
+ */
+function i18nblocks_locale_refresh() {
+ $result = db_query("SELECT DISTINCT b.module, b.delta, b.title, bx.body, bx.format, i.ibid, i.language FROM {blocks} b LEFT JOIN {boxes} bx ON b.module = 'block' AND b.delta = bx.bid LEFT JOIN {i18n_blocks} i ON b.module = i.module AND b.delta = i.delta");
+ while ($block = db_fetch_object($result)) {
+ if (!$block->language) {
+ // If the block has a custom title and no language it must be translated
+ if ($block->title && $block->title != '<none>') {
+ i18nstrings_update("blocks:$block->module:$block->delta:title", $block->title);
+ }
+ // If the block has body and no language, must be a custom block (box)
+ if ($block->body) {
+ i18nstrings_update("blocks:$block->module:$block->delta:body", $block->body, $block->format);
+ }
+ }
+ }
+ return TRUE; // Meaning it completed with no issues
+}
+
+/**
+ * Implementation of hook_form_FORM_ID_alter().
+ */
+function i18nblocks_form_block_box_delete_alter(&$form, $form_state) {
+ $delta = db_result(db_query("SELECT ibid FROM {i18n_blocks} WHERE delta = '%d'", arg(4)));
+ $form['delta'] = array(
+ '#type' => 'value',
+ '#value' => $delta,
+ );
+ $form['#submit'][] = 'i18nblocks_block_delete_submit';
+}
+
+/**
+ * Remove strings for deleted custom blocks.
+ */
+function i18nblocks_block_delete_submit(&$form, $form_state) {
+ $delta = $form_state['values']['delta'];
+ // Delete stored strings for the title and content fields.
+ i18nstrings_remove_string("blocks:block:$delta:title");
+ i18nstrings_remove_string("blocks:block:$delta:body");
+}
+
+/**
+ * Implementation of block form_alter().
+ *
+ * Remove block title for multilingual blocks.
+ */
+function i18nblocks_form_alter(&$form, $form_state, $form_id) {
+ if (($form_id == 'block_admin_configure' || $form_id == 'block_box_form' || $form_id == 'block_add_block_form')) {
+ $module = $form['module']['#value'];
+ $delta = $form['delta']['#value'];
+ $form['i18n'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Multilingual settings'),
+ '#collapsible' => TRUE,
+ '#weight' => -1,
+ );
+
+ $i18nblock = i18nblocks_load($module, $delta);
+ $form['i18n'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Multilingual settings'),
+ '#collapsible' => TRUE,
+ '#weight' => 0,
+ );
+ // Language options will depend on block type.
+ $options = array('' => t('All languages'));
+ if ($module == 'block') {
+ $options[I18N_BLOCK_LOCALIZE] = t('All languages (Translatable)');
+ }
+ $options += locale_language_list('name');
+
+ $form['i18n']['language'] = array(
+ '#type' => 'radios',
+ '#title' => t('Language'),
+ '#default_value' => $i18nblock->language,
+ '#options' => $options,
+ );
+ // Pass i18ndelta value.
+ $form['i18n']['ibid'] = array('#type' => 'value', '#value' => $i18nblock->ibid);
+ $form['#submit'][] = 'i18nblocks_form_submit';
+ }
+}
+
+/**
+ * Forms api callback. Submit function.
+ */
+function i18nblocks_form_submit($form, &$form_state) {
+ $values = $form_state['values'];
+ // Dirty trick to act on new created blocks. Delta may be zero for other modules than block.
+ if (!$values['delta'] && $values['module'] == 'block') {
+ // The last insert id will return a different value in mysql
+ //$values['delta'] = db_last_insert_id('boxes', 'bid');
+ $values['delta'] = db_result(db_query("SELECT MAX(bid) FROM {boxes}"));
+ }
+ i18nblocks_save($values);
+}
+
+/**
+ * Get block language data.
+ */
+function i18nblocks_load($module, $delta) {
+ $block = db_fetch_object(db_query("SELECT * FROM {i18n_blocks} WHERE module = '%s' AND delta = '%s'", $module, $delta));
+ // If no result, return default settings
+ if ($block && !$block->language) {
+ $block->language = I18N_BLOCK_LOCALIZE;
+ }
+ return $block ? $block : (object)array('language' => '', 'ibid' => 0);
+}
+
+/**
+ * Set block language data.
+ *
+ * @param array $block
+ * Array of block parameters: module, delata, ibid (internal i18nblocks delta).
+ */
+function i18nblocks_save($block) {
+ if (!empty($block['language'])) {
+ if ($block['language'] == I18N_BLOCK_LOCALIZE) {
+ $block['language'] = '';
+ }
+ // Update strings for localizable blocks.
+ if ($block['ibid']) {
+ drupal_write_record('i18n_blocks', $block, 'ibid');
+ }
+ else {
+ drupal_write_record('i18n_blocks', $block);
+ }
+ }
+ else {
+ // No language, delete all i18n information.
+ db_query("DELETE FROM {i18n_blocks} WHERE module = '%s' AND delta = '%s'", $block['module'], $block['delta']);
+ }
+ // If localize block or block without language
+ if (!$block['language']) {
+ // We use ibid property instead of block's delta as block id for strings
+ $module = $block['module'];
+ $delta = $block['delta'];
+ if (!empty($block['title']) && $block['title'] != '<none>') {
+ i18nstrings_update("blocks:$module:$delta:title", $block['title']);
+ }
+ if (isset($block['body'])) {
+ i18nstrings_update("blocks:$module:$delta:body", $block['body'], $block['format']);
+ }
+ }
+}
+
+/**
+ * Translate block.
+ *
+ * @param $block
+ * Core block object
+ */
+function i18nblocks_translate_block($block) {
+ // Localizable blocks may get the body translated too.
+ $localizable = _i18nblocks_list();
+ if (!empty($block->content) && $localizable && isset($localizable[$block->module][$block->delta])) {
+ $block->content = i18nstrings_text("blocks:$block->module:$block->delta:body", $block->content);
+ }
+ // If it has a custom title, localize it
+ if (!empty($block->title) && $block->title != '<none>') {
+ // Check plain here to allow module generated titles to keep any markup.
+ $block->subject = i18nstrings_string("blocks:$block->module:$block->delta:title", $block->subject);
+ }
+ return $block;
+}
+
+/**
+ * Implementation of hook_preprocess_block().
+ *
+ * Translate blocks.
+ *
+ * @see block.tpl.php
+ */
+function i18nblocks_preprocess_block(&$variables) {
+ global $language;
+
+ $block = $variables['block'];
+
+ // Replace menu blocks by their translated version.
+ if (module_exists('i18nmenu')) {
+ if ($block->module == 'menu') {
+ $block->content = i18nmenu_translated_tree($block->delta);
+ if ($block->subject && empty($block->title)) {
+ $block->subject = i18nstrings_string('menu:menu:' . $block->delta . ':title', $block->subject);
+ }
+ }
+ elseif ($block->module == 'user' && $block->delta == 1) {
+ $block->content = i18nmenu_translated_tree('navigation');
+ }
+ }
+
+ // If the block has language, do nothing, it is suppossed to be translated
+ $havelanguage = _i18nblocks_list($language->language);
+ if ($havelanguage && isset($havelanguage[$block->module][$block->delta])) {
+ return;
+ }
+ else {
+ $variables['block'] = i18nblocks_translate_block($block);
+ }
+}
+
+/**
+ * Get list of blocks i18n properties
+ */
+function _i18nblocks_list($langcode = '') {
+ static $list = array();
+
+ // Handle issues when no $langcode, use a different array index
+ $index = $langcode ? $langcode : I18N_BLOCK_LOCALIZE;
+
+ if (!isset($list[$index])) {
+ $list[$index] = array();
+ $result = db_query("SELECT * FROM {i18n_blocks} WHERE language = '%s'", $langcode);
+ while ($info = db_fetch_object($result)) {
+ $list[$index][$info->module][$info->delta] = $info;
+ }
+ }
+ return $list[$index];
+}
107 i18nblocks/translations/i18nblocks.de.po
@@ -0,0 +1,107 @@
+# $Id: i18nblocks.de.po,v 1.1.2.4 2009/06/25 23:08:05 hass Exp $
+#
+# LANGUAGE translation of Drupal (i18nblocks)
+# Copyright YEAR NAME <EMAIL@ADDRESS>
+# Generated from files:
+# i18nblocks.module,v 1.7.2.4 2008/07/25 20:36:47 jareyero
+# i18nblocks.install,v 1.5.2.1 2008/08/09 13:53:15 jareyero
+# i18nblocks/i18nblocks.info: n/a
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: German translation for i18nblocks\n"
+"POT-Creation-Date: 2009-06-26 00:43+0200\n"
+"PO-Revision-Date: 2009-06-26 00:57+0100\n"
+"Last-Translator: Alexander Haß\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n!=1);\n"
+"X-Poedit-Language: German\n"
+"X-Poedit-Country: GERMANY\n"
+
+#: i18nblocks/i18nblocks.module:26
+msgid "Localizable block"
+msgstr "Lokalisierbarer Block"
+
+#: i18nblocks/i18nblocks.module:27
+msgid "Multilingual block (Metablock)"
+msgstr "Mehrsprachiger Block (Metablock)"
+
+#: i18nblocks/i18nblocks.module:37
+msgid "This module provides support for multilingual blocks."
+msgstr "Dieses Modul ermöglicht die Unterstützung von mehrsprachigen Blöcken."
+
+#: i18nblocks/i18nblocks.module:38
+msgid "You can set up a language for a block or define it as translatable:"
+msgstr "Ein Block kann als Übersetzbar festgelegt oder eine Sprache eingestellt werden:"
+
+#: i18nblocks/i18nblocks.module:40