New module 'Internationalization'
This commit is contained in:
parent
e997e7a20c
commit
003589f73e
47 changed files with 8417 additions and 0 deletions
39
sites/all/modules/i18n/INSTALL.txt
Normal file
39
sites/all/modules/i18n/INSTALL.txt
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
********************************************************************
|
||||||
|
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
|
339
sites/all/modules/i18n/LICENSE.txt
Normal file
339
sites/all/modules/i18n/LICENSE.txt
Normal file
|
@ -0,0 +1,339 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1989
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
29
sites/all/modules/i18n/README.txt
Normal file
29
sites/all/modules/i18n/README.txt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
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
|
140
sites/all/modules/i18n/i18n.admin.inc
Normal file
140
sites/all/modules/i18n/i18n.admin.inc
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variables overview form
|
||||||
|
*/
|
||||||
|
function i18n_admin_variables_form() {
|
||||||
|
$i18n_variables = i18n_variable();
|
||||||
|
$i18n_current = array();
|
||||||
|
$result = db_query("SELECT DISTINCT(name) FROM {i18n_variable}");
|
||||||
|
while ($variable = db_fetch_object($result)) {
|
||||||
|
$i18n_current[] = $variable->name;
|
||||||
|
}
|
||||||
|
$i18n_list = array_unique(array_merge($i18n_variables, $i18n_current));
|
||||||
|
foreach ($i18n_list as $name) {
|
||||||
|
$is_multilingual = in_array($name, $i18n_variables);
|
||||||
|
$has_value = in_array($name, $i18n_current);
|
||||||
|
if ($is_multilingual) {
|
||||||
|
$class = $has_value ? 'ok' : 'info';
|
||||||
|
}
|
||||||
|
elseif ($has_value) {
|
||||||
|
$class = 'error';
|
||||||
|
}
|
||||||
|
$rows[] = array(
|
||||||
|
'class' => $class,
|
||||||
|
'data' => array(
|
||||||
|
array('data' => $name, 'colspan' => 2, 'header' => TRUE),
|
||||||
|
$is_multilingual ? t('Yes') : t('No'),
|
||||||
|
$has_value ? t('Yes') : t('No'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($i18n_list) {
|
||||||
|
$header = array('', t('Variable name'), t('Is multilingual'), t('Has translations'));
|
||||||
|
$form['variables']['#value'] = theme('table', $header, $rows, array('class' => 'system-status-report'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$form['variables']['#value'] = t('There are no multilingual variables.');
|
||||||
|
}
|
||||||
|
if (count($i18n_list) > count($i18n_variables)) {
|
||||||
|
$form['clean'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#description' => t('Delete all existing translations for variables that are not marked as multilingual.'),
|
||||||
|
);
|
||||||
|
$form['clean']['cleanup'] = array(
|
||||||
|
'#type' => 'submit',
|
||||||
|
'#value' => t('Clean up variables'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($i18n_current) {
|
||||||
|
$form['delete'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#description' => t('Delete all existing translations for variables.'),
|
||||||
|
);
|
||||||
|
$form['delete']['deleteall'] = array(
|
||||||
|
'#type' => 'submit',
|
||||||
|
'#value' => t('Delete all translations'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Admin variables form submission
|
||||||
|
*/
|
||||||
|
function i18n_admin_variables_form_submit($form, &$form_state) {
|
||||||
|
$op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
|
||||||
|
switch ($op) {
|
||||||
|
case t('Clean up variables'):
|
||||||
|
if ($variables = i18n_variable()) {
|
||||||
|
db_query("DELETE FROM {i18n_variable} WHERE name NOT IN (". db_placeholders($variables, 'varchar') . ')', $variables);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Intenational no break, if no variables defined delete all
|
||||||
|
case t('Delete all translations'):
|
||||||
|
db_query("DELETE FROM {i18n_variable}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Rebuild cache
|
||||||
|
cache_clear_all('variables:', 'cache', TRUE);
|
||||||
|
}
|
13
sites/all/modules/i18n/i18n.info
Normal file
13
sites/all/modules/i18n/i18n.info
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
name = Internationalization
|
||||||
|
description = Extends Drupal support for multilingual features.
|
||||||
|
dependencies[] = locale
|
||||||
|
dependencies[] = translation
|
||||||
|
package = Multilanguage
|
||||||
|
core = 6.x
|
||||||
|
|
||||||
|
; Information added by drupal.org packaging script on 2011-10-11
|
||||||
|
version = "6.x-1.10"
|
||||||
|
core = "6.x"
|
||||||
|
project = "i18n"
|
||||||
|
datestamp = "1318336004"
|
||||||
|
|
120
sites/all/modules/i18n/i18n.install
Normal file
120
sites/all/modules/i18n/i18n.install
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}*/
|
16
sites/all/modules/i18n/i18n.js
Normal file
16
sites/all/modules/i18n/i18n.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
975
sites/all/modules/i18n/i18n.module
Normal file
975
sites/all/modules/i18n/i18n.module
Normal file
|
@ -0,0 +1,975 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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' ) {
|
||||||
|
// We shouldn't call menu_get_object() because it may end up calling i18n_selection_mode(), see #614548
|
||||||
|
// Also we better don't do the full node loading here because there may be missing modules (init)
|
||||||
|
if (is_numeric(arg(1)) && (arg(2) == 'edit') && ($lang = i18n_node_get_lang(arg(1)))) {
|
||||||
|
i18n_selection_mode('node', $lang);
|
||||||
|
}
|
||||||
|
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' => 'Options',
|
||||||
|
'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,
|
||||||
|
);
|
||||||
|
$items['admin/settings/language/i18n/variables'] = array(
|
||||||
|
'title' => 'Variables',
|
||||||
|
'description' => 'Multilingual variables.',
|
||||||
|
'page callback' => 'drupal_get_form',
|
||||||
|
'page arguments' => array('i18n_admin_variables_form'),
|
||||||
|
'access arguments' => array('administer site configuration'),
|
||||||
|
'file' => 'i18n.admin.inc',
|
||||||
|
'type' => MENU_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) {
|
||||||
|
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.'),
|
||||||
|
);
|
||||||
|
}
|
276
sites/all/modules/i18n/i18n.pages.inc
Normal file
276
sites/all/modules/i18n/i18n.pages.inc
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}
|
13
sites/all/modules/i18n/i18nblocks/i18nblocks.info
Normal file
13
sites/all/modules/i18n/i18nblocks/i18nblocks.info
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
name = Block translation
|
||||||
|
description = Enables multilingual blocks and block translation.
|
||||||
|
dependencies[] = i18n
|
||||||
|
dependencies[] = i18nstrings
|
||||||
|
package = Multilanguage
|
||||||
|
core = 6.x
|
||||||
|
|
||||||
|
; Information added by drupal.org packaging script on 2011-10-11
|
||||||
|
version = "6.x-1.10"
|
||||||
|
core = "6.x"
|
||||||
|
project = "i18n"
|
||||||
|
datestamp = "1318336004"
|
||||||
|
|
151
sites/all/modules/i18n/i18nblocks/i18nblocks.install
Normal file
151
sites/all/modules/i18n/i18nblocks/i18nblocks.install
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
}
|
292
sites/all/modules/i18n/i18nblocks/i18nblocks.module
Normal file
292
sites/all/modules/i18n/i18nblocks/i18nblocks.module
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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];
|
||||||
|
}
|
14
sites/all/modules/i18n/i18ncck/i18ncck.info
Normal file
14
sites/all/modules/i18n/i18ncck/i18ncck.info
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name = CCK translation
|
||||||
|
description = Supports translatable custom CCK fields and fieldgroups.
|
||||||
|
dependencies[] = i18n
|
||||||
|
dependencies[] = content
|
||||||
|
dependencies[] = i18nstrings
|
||||||
|
package = Multilanguage
|
||||||
|
core = 6.x
|
||||||
|
|
||||||
|
; Information added by drupal.org packaging script on 2011-10-11
|
||||||
|
version = "6.x-1.10"
|
||||||
|
core = "6.x"
|
||||||
|
project = "i18n"
|
||||||
|
datestamp = "1318336004"
|
||||||
|
|
16
sites/all/modules/i18n/i18ncck/i18ncck.install
Normal file
16
sites/all/modules/i18n/i18ncck/i18ncck.install
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Installation file for i18ncck module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_enable().
|
||||||
|
*
|
||||||
|
* Create all strings from CCK for translation.
|
||||||
|
*/
|
||||||
|
function i18ncck_enable() {
|
||||||
|
drupal_load('module', 'i18nstrings');
|
||||||
|
i18ncck_locale_refresh();
|
||||||
|
}
|
102
sites/all/modules/i18n/i18ncck/i18ncck.module
Normal file
102
sites/all/modules/i18n/i18ncck/i18ncck.module
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Internationalization (i18n) submodule: CCK.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_locale().
|
||||||
|
*/
|
||||||
|
function i18ncck_locale($op = 'groups', $group = NULL) {
|
||||||
|
switch ($op) {
|
||||||
|
case 'groups':
|
||||||
|
return array('cck' => t('CCK'));
|
||||||
|
case 'info':
|
||||||
|
$info['cck']['refresh callback'] = 'i18ncck_locale_refresh';
|
||||||
|
$info['cck']['format'] = FALSE;
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes safe keys for using in i18nstrings().
|
||||||
|
*/
|
||||||
|
function i18ncck_get_safe_value($value) {
|
||||||
|
return html_entity_decode(strip_tags($value), ENT_QUOTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh locale strings.
|
||||||
|
*/
|
||||||
|
function i18ncck_locale_refresh() {
|
||||||
|
foreach (content_types() as $content_type => $type) {
|
||||||
|
// Localization of CCK fields.
|
||||||
|
if (isset($type['fields'])) {
|
||||||
|
foreach ($type['fields'] as $field_name => $field) {
|
||||||
|
// Localize field title and description per content type.
|
||||||
|
i18nstrings_update('cck:field:'. $content_type .'-'. $field_name .':widget_label', $field['widget']['label']);
|
||||||
|
if (!empty($field['widget']['description'])) {
|
||||||
|
i18nstrings_update('cck:field:'. $content_type .'-'. $field_name .':widget_description', $field['widget']['description']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Localize allowed values per field.
|
||||||
|
if (empty($field['allowed_values_php']) && !empty($field['allowed_values'])) {
|
||||||
|
$function = $field['module'] .'_allowed_values';
|
||||||
|
$allowed_values = function_exists($function) ? $function($field) : (array) content_allowed_values($field);
|
||||||
|
if (!empty($allowed_values)) {
|
||||||
|
foreach ($allowed_values as $key => $value) {
|
||||||
|
i18nstrings_update('cck:field:'. $field_name .':option_'. i18ncck_get_safe_value($key), $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Localization of CCK fieldgroups.
|
||||||
|
if (module_exists('fieldgroup')) {
|
||||||
|
foreach (fieldgroup_groups($content_type) as $group_name => $group) {
|
||||||
|
i18nstrings_update('cck:fieldgroup:'. $content_type .'-'. $group_name .':label', $group['label']);
|
||||||
|
if (!empty($group['settings']['form']['description'])) {
|
||||||
|
i18nstrings_update('cck:fieldgroup:'. $content_type .'-'. $group_name .':form_description', $group['settings']['form']['description']);
|
||||||
|
}
|
||||||
|
if (!empty($group['settings']['display']['description'])) {
|
||||||
|
i18nstrings_update('cck:fieldgroup:'. $content_type .'-'. $group_name .':display_description', $group['settings']['display']['description']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE; // Meaning it completed with no issues
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate widget's labels and descriptions.
|
||||||
|
*/
|
||||||
|
function i18ncck_content_field_strings_alter(&$field_strings, $content_type, $field_name) {
|
||||||
|
$field_strings['widget_label'] = i18nstrings('cck:field:'. $content_type .'-'. $field_name .':widget_label', $field_strings['widget_label']);
|
||||||
|
if (!empty($field_strings['widget_description'])) {
|
||||||
|
$field_strings['widget_description'] = i18nstrings('cck:field:'. $content_type .'-'. $field_name .':widget_description', $field_strings['widget_description']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate allowed values lists.
|
||||||
|
*/
|
||||||
|
function i18ncck_content_allowed_values_alter(&$allowed_values, $field) {
|
||||||
|
foreach ($allowed_values as $key => $value) {
|
||||||
|
$allowed_values[$key] = i18nstrings('cck:field:'. $field['field_name'] .':option_'. i18ncck_get_safe_value($key), $value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate fieldgroup labels and descriptions.
|
||||||
|
*/
|
||||||
|
function i18ncck_content_fieldgroup_strings_alter(&$group_strings, $content_type, $group_name) {
|
||||||
|
$group_strings['label'] = i18nstrings('cck:fieldgroup:'. $content_type .'-'. $group_name .':label', $group_strings['label']);
|
||||||
|
if (!empty($group_strings['form_description'])) {
|
||||||
|
$group_strings['form_description'] = i18nstrings('cck:fieldgroup:'. $content_type .'-'. $group_name .':form_description', $group_strings['form_description']);
|
||||||
|
}
|
||||||
|
if (!empty($group_strings['display_description'])) {
|
||||||
|
$group_strings['display_description'] = i18nstrings('cck:fieldgroup:'. $content_type .'-'. $group_name .':display_description', $group_strings['display_description']);
|
||||||
|
}
|
||||||
|
}
|
14
sites/all/modules/i18n/i18ncontent/i18ncontent.info
Normal file
14
sites/all/modules/i18n/i18ncontent/i18ncontent.info
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name = Content type translation
|
||||||
|
description = Add multilingual options for content and translate related strings: name, description, help text...
|
||||||
|
dependencies[] = i18nstrings
|
||||||
|
package = Multilanguage
|
||||||
|
core = 6.x
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; Information added by drupal.org packaging script on 2011-10-11
|
||||||
|
version = "6.x-1.10"
|
||||||
|
core = "6.x"
|
||||||
|
project = "i18n"
|
||||||
|
datestamp = "1318336004"
|
||||||
|
|
66
sites/all/modules/i18n/i18ncontent/i18ncontent.install
Normal file
66
sites/all/modules/i18n/i18ncontent/i18ncontent.install
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Installation file for i18ncontent module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function i18ncontent_install() {
|
||||||
|
// Create strings in the extended localization system.
|
||||||
|
// There seems to be some issue with module loading on install/update.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_enable().
|
||||||
|
*/
|
||||||
|
function i18ncontent_enable() {
|
||||||
|
// Make sure i18nstrings module is loaded, which may not be when enabling both modules at the same time
|
||||||
|
drupal_load('module', 'i18nstrings');
|
||||||
|
i18ncontent_locale_refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_disable().
|
||||||
|
*
|
||||||
|
* This function depends on i18nstrings, so it must be run on _disable() better than uninstall().
|
||||||
|
*/
|
||||||
|
function i18ncontent_disable() {
|
||||||
|
// Remove and restore help texts.
|
||||||
|
$langcode = language_default('language');
|
||||||
|
foreach (node_get_types() as $type) {
|
||||||
|
if (!$type->help && ($help = i18nstrings_ts("nodetype:type:$type->type:help", $langcode))) {
|
||||||
|
$type->help = $help;
|
||||||
|
node_type_save($type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @ TODO Some more clean up for strings
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The old module with the same name had a different approach, so the update will be full install.
|
||||||
|
*/
|
||||||
|
function i18ncontent_update_6001() {
|
||||||
|
$ret = array();
|
||||||
|
// Update location for existing strings for this textgroup that was wrong
|
||||||
|
$ret[] = update_sql("UPDATE {locales_source} SET location = CONCAT('type:', location) WHERE textgroup = 'nodetype' AND location NOT LIKE 'type:%'");
|
||||||
|
// Delete all indexing data, it will be recreated
|
||||||
|
$ret[] = update_sql("DELETE FROM {i18n_strings} WHERE lid IN (SELECT lid FROM {locales_source} WHERE textgroup = 'nodetype')");
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locale refresh, this will run successfully only after the i18nstrings update has run
|
||||||
|
*/
|
||||||
|
function i18ncontent_update_6002() {
|
||||||
|
$ret = array();
|
||||||
|
$version = (int)drupal_get_installed_schema_version('i18nstrings');
|
||||||
|
if ($version > 6002) {
|
||||||
|
drupal_load('module', 'i18ncontent');
|
||||||
|
drupal_load('module', 'i18nstrings');
|
||||||
|
i18ncontent_locale_refresh();
|
||||||
|
} else {
|
||||||
|
drupal_set_message('The i18ncontent update 6002 needs to run after i18nstrings update 6003. Re-run update.php.', 'warning');
|
||||||
|
$ret['#abort'] = TRUE;
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
225
sites/all/modules/i18n/i18ncontent/i18ncontent.module
Normal file
225
sites/all/modules/i18n/i18ncontent/i18ncontent.module
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Internationalization (i18n) package - translatable content type parameters
|
||||||
|
*
|
||||||
|
* This new module uses a pure forms/strings approach as opposed to the old one (v5)
|
||||||
|
* which was using some dirty db rewrite tricks.
|
||||||
|
*
|
||||||
|
* The i18n strings created by this module are:
|
||||||
|
* - nodetype:type:[type]:[name,title,body,help]
|
||||||
|
*
|
||||||
|
* @author Jose A. Reyero, 2007
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_help().
|
||||||
|
*/
|
||||||
|
function i18ncontent_help($path, $arg) {
|
||||||
|
switch ($path) {
|
||||||
|
case 'admin/help#i18ncontent':
|
||||||
|
$output = '<p>'. t('This module will localize all content type configuration texts.') .'</p>';
|
||||||
|
$output .= '<ul>';
|
||||||
|
$output .= '<li>'. t('Content type names') .'</li>';
|
||||||
|
$output .= '<li>'. t('Submission guidelines') .'</li>';
|
||||||
|
$output .= '<li>'. t("Content type descriptions were previously localized so they won't be affected.") .'</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;
|
||||||
|
}
|
||||||
|
// Add translated help text for node add pages
|
||||||
|
if (!menu_get_object() && $arg[1] == 'add' && $arg[2]) {
|
||||||
|
$type = str_replace('-', '_', $arg[2]);
|
||||||
|
// Fetch default language node type help
|
||||||
|
$source = i18ncontent_node_help_source($type);
|
||||||
|
if (isset($source->source) && ($help = i18nstrings("nodetype:type:$type:help", $source->source))) {
|
||||||
|
return '<p>'. filter_xss_admin($help) .'</p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_locale().
|
||||||
|
*/
|
||||||
|
function i18ncontent_locale($op = 'groups', $group = NULL) {
|
||||||
|
switch ($op) {
|
||||||
|
case 'groups':
|
||||||
|
return array('nodetype' => t('Content type'));
|
||||||
|
case 'info':
|
||||||
|
$info['nodetype']['refresh callback'] = 'i18ncontent_locale_refresh';
|
||||||
|
$info['nodetype']['format'] = FALSE;
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh content type strings.
|
||||||
|
*/
|
||||||
|
function i18ncontent_locale_refresh() {
|
||||||
|
foreach (node_get_types() as $type) {
|
||||||
|
i18nstrings_update("nodetype:type:$type->type:name", $type->name);
|
||||||
|
i18nstrings_update("nodetype:type:$type->type:title", $type->title_label);
|
||||||
|
if (isset($type->body_label)) {
|
||||||
|
i18nstrings_update("nodetype:type:$type->type:body", $type->body_label);
|
||||||
|
}
|
||||||
|
i18nstrings_update("nodetype:type:$type->type:description", $type->description);
|
||||||
|
if ($type->help) {
|
||||||
|
i18nstrings_ts("nodetype:type:$type->type:help", $type->help, NULL, TRUE);
|
||||||
|
$type->help = '';
|
||||||
|
node_type_save($type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE; // Meaning it completed with no issues
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_form_alter().
|
||||||
|
*/
|
||||||
|
function i18ncontent_form_alter(&$form, $form_state, $form_id) {
|
||||||
|
switch ($form_id) {
|
||||||
|
case 'node_type_form':
|
||||||
|
// Replace the help text default value in the node type form with data from
|
||||||
|
// i18nstrings. Help text is handled in hook_node_type() and hook_help().
|
||||||
|
$type = $form['#node_type']->type;
|
||||||
|
if ($type) {
|
||||||
|
// Fetch default language node type help
|
||||||
|
$source = i18ncontent_node_help_source($type);
|
||||||
|
// We dont need to pass the value through i18nstrings_ts()
|
||||||
|
if (isset($source->source)) {
|
||||||
|
$form['submission']['help']['#default_value'] = $source->source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'search_form':
|
||||||
|
// Advanced search form. Add language and localize content type names
|
||||||
|
if ($form['module']['#value'] == 'node' && !empty($form['advanced'])){
|
||||||
|
// @todo Handle language search conditions
|
||||||
|
//$form['advanced']['language'] = _i18n_language_select();
|
||||||
|
if (!empty($form['advanced']['type'])) {
|
||||||
|
foreach ($form['advanced']['type']['#options'] as $type => $name) {
|
||||||
|
$form['advanced']['type']['#options'][$type] = i18nstrings("nodetype:type:$type:name", $name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Translate field names for title and body for the node edit form.
|
||||||
|
if (isset($form['#id']) && $form['#id'] == 'node-form') {
|
||||||
|
$type = $form['#node']->type;
|
||||||
|
if (!empty($form['title']['#title'])) {
|
||||||
|
$form['title']['#title'] = i18nstrings("nodetype:type:$type:title", $form['title']['#title']);
|
||||||
|
}
|
||||||
|
if (!empty($form['body_field']['body']['#title'])) {
|
||||||
|
$form['body_field']['body']['#title'] = i18nstrings("nodetype:type:$type:body", $form['body_field']['body']['#title']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_node_type().
|
||||||
|
*/
|
||||||
|
function i18ncontent_node_type($op, $info) {
|
||||||
|
$language = language_default('language');
|
||||||
|
if ($op == 'insert' || $op == 'update') {
|
||||||
|
if(!empty($info->old_type) && $info->old_type != $info->type) {
|
||||||
|
i18nstrings_update_context("nodetype:type:$old_type:*", "nodetype:type:$type:*");
|
||||||
|
}
|
||||||
|
i18nstrings_update("nodetype:type:$info->type:name", $info->name);
|
||||||
|
i18nstrings_update("nodetype:type:$info->type:title", $info->title_label);
|
||||||
|
i18nstrings_update("nodetype:type:$info->type:body", $info->body_label);
|
||||||
|
i18nstrings_update("nodetype:type:$info->type:description", $info->description);
|
||||||
|
if (empty($info->help)) {
|
||||||
|
i18nstrings_remove("nodetype:type:$info->type:help");
|
||||||
|
} else {
|
||||||
|
i18nstrings_ts("nodetype:type:$info->type:help", $info->help, $language, TRUE);
|
||||||
|
// Remove the 'help' text from {node_type} to avoid both the
|
||||||
|
// original text and translation appearing in hook_help().
|
||||||
|
db_query("UPDATE {node_type} set help = '' WHERE type = '%s'", $info->type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($op == 'delete') {
|
||||||
|
i18nstrings_remove("nodetype:type:$info->type:title");
|
||||||
|
i18nstrings_remove("nodetype:type:$info->type:name");
|
||||||
|
i18nstrings_remove("nodetype:type:$info->type:description");
|
||||||
|
i18nstrings_remove("nodetype:type:$info->type:body");
|
||||||
|
i18nstrings_remove("nodetype:type:$info->type:help");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_menu_alter().
|
||||||
|
*
|
||||||
|
* Take over the node add pages.
|
||||||
|
*/
|
||||||
|
function i18ncontent_menu_alter(&$menu) {
|
||||||
|
$menu['node/add']['page callback'] = 'i18ncontent_node_add_page';
|
||||||
|
if (variable_get('page_manager_node_edit_disabled', TRUE)) {
|
||||||
|
// Replace node/add/* menu items
|
||||||
|
foreach (node_get_types('types', NULL, TRUE) as $type) {
|
||||||
|
$type_url_str = str_replace('_', '-', $type->type);
|
||||||
|
$path = 'node/add/' . $type_url_str;
|
||||||
|
if (!empty($menu[$path]['page callback']) && $menu[$path]['page callback'] == 'node_add') {
|
||||||
|
$menu[$path]['page callback'] = 'i18ncontent_node_add';
|
||||||
|
$menu[$path]['title callback'] = 'i18nstrings_title_callback';
|
||||||
|
$menu[$path]['title arguments'] = array('nodetype:type:'. $type->type .':name', $type->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement for node_add_page.
|
||||||
|
*/
|
||||||
|
function i18ncontent_node_add_page() {
|
||||||
|
$item = menu_get_item();
|
||||||
|
$content = system_admin_menu_block($item);
|
||||||
|
// The node type isn't available in the menu path, but 'title' is equivalent
|
||||||
|
// to the human readable name, so check this against node_get_types() to get
|
||||||
|
// the correct values. First we build an array of node types indexed by names
|
||||||
|
$type_names = array_flip(node_get_types('names'));
|
||||||
|
foreach ($content as $key => $item) {
|
||||||
|
if ($type = $type_names[$item['link_title']]){
|
||||||
|
// We just need to translate the description, the title is translated by the menu system
|
||||||
|
$content[$key]['description'] = i18nstrings("nodetype:type:$type:description", $item['description']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return theme('node_add_list', $content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement for node_add
|
||||||
|
*
|
||||||
|
* This just calls node_add() and switches title. This has to be done here to work always
|
||||||
|
*/
|
||||||
|
function i18ncontent_node_add($type) {
|
||||||
|
global $user;
|
||||||
|
|
||||||
|
$types = node_get_types();
|
||||||
|
$type = isset($type) ? str_replace('-', '_', $type) : NULL;
|
||||||
|
// If a node type has been specified, validate its existence.
|
||||||
|
if (isset($types[$type]) && node_access('create', $type)) {
|
||||||
|
// Initialize settings:
|
||||||
|
$node = array('uid' => $user->uid, 'name' => (isset($user->name) ? $user->name : ''), 'type' => $type, 'language' => '');
|
||||||
|
|
||||||
|
drupal_set_title(t('Create @name', array('@name' => i18nstrings("nodetype:type:$type:name", $types[$type]->name))));
|
||||||
|
$output = drupal_get_form($type .'_node_form', $node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch default source for node type help
|
||||||
|
*/
|
||||||
|
function i18ncontent_node_help_source($type) {
|
||||||
|
$context = i18nstrings_context("nodetype:type:$type:help");
|
||||||
|
$source = i18nstrings_get_source($context);
|
||||||
|
return $source;
|
||||||
|
}
|
15
sites/all/modules/i18n/i18nmenu/i18nmenu.info
Normal file
15
sites/all/modules/i18n/i18nmenu/i18nmenu.info
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
name = Menu translation
|
||||||
|
description = Supports translatable custom menu items.
|
||||||
|
dependencies[] = i18n
|
||||||
|
dependencies[] = menu
|
||||||
|
dependencies[] = i18nblocks
|
||||||
|
dependencies[] = i18nstrings
|
||||||
|
package = Multilanguage
|
||||||
|
core = 6.x
|
||||||
|
|
||||||
|
; Information added by drupal.org packaging script on 2011-10-11
|
||||||
|
version = "6.x-1.10"
|
||||||
|
core = "6.x"
|
||||||
|
project = "i18n"
|
||||||
|
datestamp = "1318336004"
|
||||||
|
|
16
sites/all/modules/i18n/i18nmenu/i18nmenu.install
Normal file
16
sites/all/modules/i18n/i18nmenu/i18nmenu.install
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Installation file for i18nmenu module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_enable().
|
||||||
|
*
|
||||||
|
* Create all strings from menus for translation.
|
||||||
|
*/
|
||||||
|
function i18nmenu_enable() {
|
||||||
|
drupal_load('module', 'i18nstrings');
|
||||||
|
i18nmenu_locale_refresh();
|
||||||
|
}
|
461
sites/all/modules/i18n/i18nmenu/i18nmenu.module
Normal file
461
sites/all/modules/i18n/i18nmenu/i18nmenu.module
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Internationalization (i18n) submodule: Menu translation.
|
||||||
|
*
|
||||||
|
* @author Jose A. Reyero, 2005
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_locale().
|
||||||
|
*/
|
||||||
|
function i18nmenu_locale($op = 'groups', $group = NULL) {
|
||||||
|
switch ($op) {
|
||||||
|
case 'groups':
|
||||||
|
return array('menu' => t('Menu'));
|
||||||
|
case 'info':
|
||||||
|
$info['menu']['refresh callback'] = 'i18nmenu_locale_refresh';
|
||||||
|
$info['menu']['format'] = FALSE;
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh locale strings.
|
||||||
|
*/
|
||||||
|
function i18nmenu_locale_refresh() {
|
||||||
|
// Rebuild menus to ensure all items are altered in i18nmenu_menu_link_alter().
|
||||||
|
menu_rebuild();
|
||||||
|
i18n_selection_mode('off');
|
||||||
|
foreach (menu_get_menus() as $name => $title) {
|
||||||
|
$tree = menu_tree_all_data($name);
|
||||||
|
i18nmenu_localize_tree($tree, TRUE);
|
||||||
|
}
|
||||||
|
i18n_selection_mode('reset');
|
||||||
|
return TRUE; // Meaning it completed with no issues
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_menu_link_alter().
|
||||||
|
*
|
||||||
|
* Catch changed links, update language and set alter option.
|
||||||
|
*/
|
||||||
|
function i18nmenu_menu_link_alter(&$item, $menu) {
|
||||||
|
// If we set option to language it causes an error with the link system
|
||||||
|
// This should handle language only as the links are being manually updated
|
||||||
|
if (!empty($item['language'])) {
|
||||||
|
$item['options']['langcode'] = $item['language'];
|
||||||
|
}
|
||||||
|
elseif (isset($item['language'])) {
|
||||||
|
unset($item['options']['langcode']);
|
||||||
|
}
|
||||||
|
// If we are handling custom menu items of menu module and no language is set,
|
||||||
|
// invoke translation via i18nstrings module.
|
||||||
|
if (empty($item['language']) && $item['module'] == 'menu') {
|
||||||
|
// Set title_callback to FALSE to avoid calling t().
|
||||||
|
$item['title_callback'] = FALSE;
|
||||||
|
// Setting the alter option to true ensures that
|
||||||
|
// hook_translated_menu_link_alter() will be called.
|
||||||
|
$item['options']['alter'] = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_translated_menu_link_alter().
|
||||||
|
*
|
||||||
|
* Translate menu links on the fly.
|
||||||
|
*
|
||||||
|
* @see i18nmenu_menu_link_alter()
|
||||||
|
*/
|
||||||
|
function i18nmenu_translated_menu_link_alter(&$item, $map) {
|
||||||
|
if ($item['module'] == 'menu') {
|
||||||
|
$item['title'] = _i18nmenu_get_item_title($item);
|
||||||
|
$item['localized_options']['attributes']['title'] = _i18nmenu_get_item_description($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_help().
|
||||||
|
*/
|
||||||
|
function i18nmenu_help($path, $arg) {
|
||||||
|
switch ($path) {
|
||||||
|
case 'admin/help#i18nmenu' :
|
||||||
|
$output = '<p>'. t('This module provides support for translatable custom menu items:') .'</p>';
|
||||||
|
$output .= '<ul>';
|
||||||
|
$output .= '<li>'. t('Create menus as usual, with names in the default language, usually English. If the menu is already created, no changes are needed.') .'</li>';
|
||||||
|
$output .= '<li>'. t('Optionally, you can set up a language for a menu item so it is only displayed for that language.') .'</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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get localized menu tree.
|
||||||
|
*/
|
||||||
|
function i18nmenu_translated_tree($menu_name) {
|
||||||
|
static $menu_output = array();
|
||||||
|
|
||||||
|
if (!isset($menu_output[$menu_name])) {
|
||||||
|
$tree = menu_tree_page_data($menu_name);
|
||||||
|
i18nmenu_localize_tree($tree);
|
||||||
|
$menu_output[$menu_name] = menu_tree_output($tree);
|
||||||
|
}
|
||||||
|
return $menu_output[$menu_name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localize menu tree.
|
||||||
|
*/
|
||||||
|
function i18nmenu_localize_tree(&$tree, $update = FALSE) {
|
||||||
|
global $language;
|
||||||
|
foreach ($tree as $index => $item) {
|
||||||
|
$link = $item['link'];
|
||||||
|
if ($link['customized']) {
|
||||||
|
// Remove links for other languages than current.
|
||||||
|
// Links with language wont be localized.
|
||||||
|
if (!empty($link['options']['langcode'])) {
|
||||||
|
if ($link['options']['langcode'] != $language->language) {
|
||||||
|
unset($tree[$index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$router = i18nmenu_get_router($link['router_path']);
|
||||||
|
// If the title is the same it will be localized by the menu system.
|
||||||
|
if ($link['link_title'] != $router['title']) {
|
||||||
|
$tree[$index]['link']['title'] = _i18nmenu_get_item_title($link, $update);
|
||||||
|
}
|
||||||
|
$tree[$index]['link']['localized_options']['attributes']['title'] = _i18nmenu_get_item_description($link, $update);
|
||||||
|
// Localize subtree.
|
||||||
|
if ($item['below'] !== FALSE) {
|
||||||
|
i18nmenu_localize_tree($tree[$index]['below'], $update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of localized links for a navigation menu.
|
||||||
|
*/
|
||||||
|
function i18nmenu_menu_navigation_links($menu_name, $level = 0) {
|
||||||
|
// Don't even bother querying the menu table if no menu is specified.
|
||||||
|
if (empty($menu_name)) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the menu hierarchy for the current page.
|
||||||
|
$tree = menu_tree_page_data($menu_name);
|
||||||
|
i18nmenu_localize_tree($tree);
|
||||||
|
|
||||||
|
// Go down the active trail until the right level is reached.
|
||||||
|
while ($level-- > 0 && $tree) {
|
||||||
|
// Loop through the current level's items until we find one that is in trail.
|
||||||
|
while ($item = array_shift($tree)) {
|
||||||
|
if ($item['link']['in_active_trail']) {
|
||||||
|
// If the item is in the active trail, we continue in the subtree.
|
||||||
|
$tree = empty($item['below']) ? array() : $item['below'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a single level of links.
|
||||||
|
$links = array();
|
||||||
|
foreach ($tree as $item) {
|
||||||
|
if (!$item['link']['hidden']) {
|
||||||
|
$class = '';
|
||||||
|
$l = $item['link']['localized_options'];
|
||||||
|
$l['href'] = $item['link']['href'];
|
||||||
|
$l['title'] = $item['link']['title'];
|
||||||
|
if ($item['link']['in_active_trail']) {
|
||||||
|
$class = ' active-trail';
|
||||||
|
}
|
||||||
|
// Keyed with the unique mlid to generate classes in theme_links().
|
||||||
|
$links['menu-'. $item['link']['mlid'] . $class] = $l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace standard primary and secondary links.
|
||||||
|
*/
|
||||||
|
function i18nmenu_preprocess_page(&$vars) {
|
||||||
|
if (theme_get_setting('toggle_primary_links')) {
|
||||||
|
$vars['primary_links'] = i18nmenu_menu_navigation_links(variable_get('menu_primary_links_source', 'primary-links'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the secondary menu source is set as the primary menu, we display the
|
||||||
|
// second level of the primary menu.
|
||||||
|
|
||||||
|
if (theme_get_setting('toggle_secondary_links')) {
|
||||||
|
if (variable_get('menu_secondary_links_source', 'secondary-links') == variable_get('menu_primary_links_source', 'primary-links')) {
|
||||||
|
$vars['secondary_links'] = i18nmenu_menu_navigation_links(variable_get('menu_primary_links_source', 'primary-links'), 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$vars['secondary_links'] = i18nmenu_menu_navigation_links(variable_get('menu_secondary_links_source', 'secondary-links'), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionally insert/update and return a localized menu item title.
|
||||||
|
*/
|
||||||
|
function _i18nmenu_get_item_title($link, $update = FALSE, $langcode = NULL) {
|
||||||
|
$key = 'menu:item:'. $link['mlid'] .':title';
|
||||||
|
if ($update) {
|
||||||
|
i18nstrings_update($key, $link['link_title']);
|
||||||
|
}
|
||||||
|
return i18nstrings($key, $link['link_title'], $langcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionally insert/update and return a localized menu item description.
|
||||||
|
*/
|
||||||
|
function _i18nmenu_get_item_description($link, $update = FALSE, $langcode = NULL) {
|
||||||
|
if (empty($link['options']['attributes']['title'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$key = 'menu:item:'. $link['mlid'] .':description';
|
||||||
|
$description = $link['options']['attributes']['title'];
|
||||||
|
if ($update) {
|
||||||
|
i18nstrings_update($key, $description);
|
||||||
|
}
|
||||||
|
return i18nstrings($key, $description, $langcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a menu item translation.
|
||||||
|
*/
|
||||||
|
function _i18nmenu_delete_item($mlid) {
|
||||||
|
i18nstrings_remove_string('menu:item:'. $mlid .':title');
|
||||||
|
i18nstrings_remove_string('menu:item:'. $mlid .':description');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the menu router for this router path.
|
||||||
|
*
|
||||||
|
* We need the untranslated title to compare, and this will be fast.
|
||||||
|
* There's no api function to do this?
|
||||||
|
*/
|
||||||
|
function i18nmenu_get_router($path) {
|
||||||
|
static $cache = array();
|
||||||
|
if (!array_key_exists($path, $cache)) {
|
||||||
|
$cache[$path] = db_fetch_array(db_query("SELECT title FROM {menu_router} WHERE path = '%s'", $path));
|
||||||
|
}
|
||||||
|
return $cache[$path];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_form_form_id_alter().
|
||||||
|
*
|
||||||
|
* Register a submit callback to process menu title.
|
||||||
|
*/
|
||||||
|
function i18nmenu_form_menu_edit_menu_alter(&$form, $form_state) {
|
||||||
|
$form['#submit'][] = 'i18nmenu_menu_edit_menu_submit';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit handler for the menu_edit_item form.
|
||||||
|
*
|
||||||
|
* On menu item insert or update, save a translation record.
|
||||||
|
*/
|
||||||
|
function i18nmenu_menu_edit_menu_submit($form, &$form_state) {
|
||||||
|
// Ensure we have a menu to work with.
|
||||||
|
if (isset($form_state['values']['menu_name']) && isset($form_state['values']['title'])) {
|
||||||
|
if ($form['#insert']) {
|
||||||
|
$context = 'menu:menu:menu-' . $form_state['values']['menu_name'] . ':title';
|
||||||
|
} else {
|
||||||
|
$context = 'menu:menu:' . $form_state['values']['menu_name'] . ':title';
|
||||||
|
}
|
||||||
|
i18nstrings_update_string($context, $form_state['values']['title']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_form_form_id_alter().
|
||||||
|
*
|
||||||
|
* Add a language selector to the menu_edit_item form and register a submit
|
||||||
|
* callback to process items.
|
||||||
|
*/
|
||||||
|
function i18nmenu_form_menu_edit_item_alter(&$form, $form_state) {
|
||||||
|
if ($form['menu']['#item'] && isset($form['menu']['#item']['options']['langcode'])) {
|
||||||
|
$language = $form['menu']['#item']['options']['langcode'];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$language = '';
|
||||||
|
}
|
||||||
|
$form['menu']['language'] = array(
|
||||||
|
'#type' => 'select',
|
||||||
|
'#title' => t('Language'),
|
||||||
|
'#description' => t('Select a language for this menu item. Choose "All languages" to make the menu item translatable into different languages.'),
|
||||||
|
'#options' => array('' => t('All languages')) + locale_language_list('name'),
|
||||||
|
'#default_value' => $language,
|
||||||
|
);
|
||||||
|
array_unshift($form['#validate'], 'i18nmenu_menu_item_prepare_normal_path');
|
||||||
|
$form['#submit'][] = 'i18nmenu_menu_item_update';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normal path should be checked with menu item's language to avoid
|
||||||
|
* troubles when a node and it's translation has the same url alias.
|
||||||
|
*/
|
||||||
|
function i18nmenu_menu_item_prepare_normal_path($form, &$form_state) {
|
||||||
|
$item = &$form_state['values']['menu'];
|
||||||
|
$normal_path = drupal_get_normal_path($item['link_path'], $item['language']);
|
||||||
|
if ($item['link_path'] != $normal_path) {
|
||||||
|
drupal_set_message(t('The menu system stores system paths only, but will use the URL alias for display. %link_path has been stored as %normal_path', array('%link_path' => $item['link_path'], '%normal_path' => $normal_path)));
|
||||||
|
$item['link_path'] = $normal_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit handler for the menu_edit_item form.
|
||||||
|
*
|
||||||
|
* On menu item insert or update, save a translation record.
|
||||||
|
*/
|
||||||
|
function i18nmenu_menu_item_update($form, &$form_state) {
|
||||||
|
// Ensure we have a menu item to work with.
|
||||||
|
if (isset($form_state['values']['menu'])) {
|
||||||
|
$item = $form_state['values']['menu'];
|
||||||
|
_i18nmenu_update_item($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the translation data for a menu item that has been inserted
|
||||||
|
* or updated.
|
||||||
|
*
|
||||||
|
* @see i18nmenu_menu_item_update()
|
||||||
|
* @see i18nmenu_nodeapi()
|
||||||
|
*/
|
||||||
|
function _i18nmenu_update_item($item) {
|
||||||
|
list($item['menu_name'], $item['plid']) = explode(':', $item['parent']);
|
||||||
|
// If this was an insert, determine the ID that was set.
|
||||||
|
if (!isset($item['mlid'])) {
|
||||||
|
$item['mlid'] = db_result(db_query("SELECT MAX(mlid) FROM {menu_links} WHERE link_path = '%s' AND menu_name = '%s' AND module = 'menu' AND plid = %d AND link_title = '%s'", $item['link_path'], $item['menu_name'], $item['plid'], $item['link_title']));
|
||||||
|
}
|
||||||
|
if (!empty($item['mlid'])) {
|
||||||
|
_i18nmenu_get_item_title($item, TRUE);
|
||||||
|
_i18nmenu_get_item_description($item, TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function: load the menu item associated to the current node.
|
||||||
|
*/
|
||||||
|
function _i18nmenu_node_prepare(&$node) {
|
||||||
|
menu_nodeapi($node, 'prepare');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_form_form_id_alter().
|
||||||
|
*
|
||||||
|
* Add a submit handler to the the menu item deletion confirmation form.
|
||||||
|
*/
|
||||||
|
function i18nmenu_form_menu_item_delete_form_alter(&$form, $form_state) {
|
||||||
|
$form['#submit'][] = 'i18nmenu_item_delete_submit';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit function for the delete button on the menu item editing form.
|
||||||
|
*/
|
||||||
|
function i18nmenu_item_delete_submit($form, &$form_state) {
|
||||||
|
_i18nmenu_delete_item($form['#item']['mlid']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_form_alter().
|
||||||
|
*
|
||||||
|
* Add language to menu settings of the node form, as well as setting defaults
|
||||||
|
* to match the translated item's menu settings.
|
||||||
|
*/
|
||||||
|
function i18nmenu_form_alter(&$form, $form_state, $form_id) {
|
||||||
|
if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) {
|
||||||
|
$node = $form['#node'];
|
||||||
|
|
||||||
|
if (!empty($form['menu'])) {
|
||||||
|
// Customized must be set to 1 to save language.
|
||||||
|
$form['menu']['customized'] = array('#type' => 'value', '#value' => 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing if the node already has a menu.
|
||||||
|
if (!empty($node->menu['mlid'])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the translation source node. If creating a new node,
|
||||||
|
// translation_source is set. Otherwise, node_load the tnid.
|
||||||
|
// New translation.
|
||||||
|
if (!empty($node->translation_source)) {
|
||||||
|
$tnode = $node->translation_source;
|
||||||
|
}
|
||||||
|
// Editing existing translation.
|
||||||
|
elseif (!empty($node->nid) && !empty($node->tnid) && $node->nid != $node->tnid) {
|
||||||
|
$tnode = node_load($node->tnid);
|
||||||
|
}
|
||||||
|
// If no translation node, return.
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the tnode so the menu item will be available.
|
||||||
|
_i18nmenu_node_prepare($tnode);
|
||||||
|
|
||||||
|
if ($tnode->menu) {
|
||||||
|
// Set default values based on translation source's menu.
|
||||||
|
$form['menu']['link_title']['#default_value'] = $tnode->menu['link_title'];
|
||||||
|
$form['menu']['weight']['#default_value'] = $tnode->menu['weight'];
|
||||||
|
$form['menu']['parent']['#default_value'] = $tnode->menu['menu_name'] .':'. $tnode->menu['plid'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_nodeapi().
|
||||||
|
*
|
||||||
|
* Save or delete menu item strings associated with nodes.
|
||||||
|
*/
|
||||||
|
function i18nmenu_nodeapi(&$node, $op) {
|
||||||
|
switch ($op) {
|
||||||
|
case 'presave':
|
||||||
|
// Ensure that the menu item language always matches node language.
|
||||||
|
if (isset($node->menu) && isset($node->language)) {
|
||||||
|
$node->menu['language'] = $node->language;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'insert':
|
||||||
|
case 'update':
|
||||||
|
if (isset($node->menu)) {
|
||||||
|
$item = $node->menu;
|
||||||
|
if (!empty($item['delete'])) {
|
||||||
|
_i18nmenu_delete_item($item['mlid']);
|
||||||
|
}
|
||||||
|
elseif (trim($item['link_title'])) {
|
||||||
|
$item['link_title'] = trim($item['link_title']);
|
||||||
|
$item['link_path'] = "node/$node->nid";
|
||||||
|
_i18nmenu_update_item($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'delete':
|
||||||
|
// Delete all menu item link translations that point to this node.
|
||||||
|
$result = db_query("SELECT mlid FROM {menu_links} WHERE link_path = 'node/%d' AND module = 'menu'", $node->nid);
|
||||||
|
while ($m = db_fetch_array($result)) {
|
||||||
|
_i18nmenu_delete_item($m['mlid']);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'prepare translation':
|
||||||
|
if (empty($node->menu['mlid']) && !empty($node->translation_source)) {
|
||||||
|
$tnode = $node->translation_source;
|
||||||
|
// Prepare the tnode so the menu item will be available.
|
||||||
|
node_object_prepare($tnode);
|
||||||
|
$node->menu['link_title'] = $tnode->menu['link_title'];
|
||||||
|
$node->menu['weight'] = $tnode->menu['weight'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
15
sites/all/modules/i18n/i18npoll/i18npoll.info
Normal file
15
sites/all/modules/i18n/i18npoll/i18npoll.info
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
name = Poll aggregate
|
||||||
|
description = Aggregates poll results for all translations.
|
||||||
|
dependencies[] = translation
|
||||||
|
dependencies[] = poll
|
||||||
|
package = Multilanguage
|
||||||
|
core = 6.x
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; Information added by drupal.org packaging script on 2011-10-11
|
||||||
|
version = "6.x-1.10"
|
||||||
|
core = "6.x"
|
||||||
|
project = "i18n"
|
||||||
|
datestamp = "1318336004"
|
||||||
|
|
169
sites/all/modules/i18n/i18npoll/i18npoll.module
Normal file
169
sites/all/modules/i18n/i18npoll/i18npoll.module
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Multilingual poll - Aggregates poll results for all translations.
|
||||||
|
*
|
||||||
|
* Most code ripped off poll module and adapted for multilingual results.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_form_alter().
|
||||||
|
*
|
||||||
|
* Rewrite the cancel vote form with the nid for the translation with the actual vote.
|
||||||
|
*/
|
||||||
|
function i18npoll_form_alter(&$form, $form_state, $form_id) {
|
||||||
|
if ($form_id == 'poll_cancel_form') {
|
||||||
|
if (!empty($form['#nid']) && empty($form['#i18ntnid'])) {
|
||||||
|
// Replace nid with the original node the user voted
|
||||||
|
$node = node_load($form['#nid']);
|
||||||
|
if (!empty($node->tnid) && ($vote = i18npoll_get_vote($node->tnid))) {
|
||||||
|
$form['#i18ntnid'] = $node->tnid;
|
||||||
|
$form['#nid'] = $vote->nid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_nodeapi().
|
||||||
|
*
|
||||||
|
* Replaces poll results with aggregated translations.
|
||||||
|
*
|
||||||
|
* We don't add all language results on loading to avoid the data being trashed
|
||||||
|
* when editing and saving nodes again.
|
||||||
|
*/
|
||||||
|
function i18npoll_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
|
||||||
|
global $user;
|
||||||
|
|
||||||
|
if ($node->type == 'poll' && !empty($node->tnid)) {
|
||||||
|
// Replace results for node view
|
||||||
|
if ($op == 'view' && (empty($node->allowvotes) || !empty($node->show_results))) {
|
||||||
|
$node->content['body'] = array(
|
||||||
|
'#value' => i18npoll_view_results($node, $teaser, $page, FALSE),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Check again whether or not this user is allowed to vote.
|
||||||
|
// User may have voted for any of the translations
|
||||||
|
if ($op == 'load' && $node->allowvotes) {
|
||||||
|
$result = i18npoll_get_vote($node->tnid);
|
||||||
|
if (isset($result->chorder)) {
|
||||||
|
$node->vote = $result->chorder;
|
||||||
|
$node->allowvotes = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_block().
|
||||||
|
*
|
||||||
|
* Generates a block containing the latest poll with aggregated results.
|
||||||
|
*/
|
||||||
|
function i18npoll_block($op = 'list', $delta = 0) {
|
||||||
|
if (user_access('access content')) {
|
||||||
|
if ($op == 'list') {
|
||||||
|
$blocks[0]['info'] = t('Most recent poll (Aggregated translations)');
|
||||||
|
return $blocks;
|
||||||
|
}
|
||||||
|
elseif ($op == 'view') {
|
||||||
|
// Retrieve the latest poll.
|
||||||
|
$sql = db_rewrite_sql("SELECT MAX(n.created) FROM {node} n INNER JOIN {poll} p ON p.nid = n.nid WHERE n.status = 1 AND p.active = 1");
|
||||||
|
$timestamp = db_result(db_query($sql));
|
||||||
|
if ($timestamp) {
|
||||||
|
$poll = node_load(array('type' => 'poll', 'created' => $timestamp, 'status' => 1));
|
||||||
|
|
||||||
|
if ($poll->nid) {
|
||||||
|
$poll = i18npoll_view($poll, TRUE, FALSE, TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$block['subject'] = t('Poll');
|
||||||
|
$block['content'] = drupal_render($poll->content);
|
||||||
|
return $block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_view().
|
||||||
|
*
|
||||||
|
* @param $block
|
||||||
|
* An extra parameter that adapts the hook to display a block-ready
|
||||||
|
* rendering of the poll.
|
||||||
|
*/
|
||||||
|
function i18npoll_view($node, $teaser = FALSE, $page = FALSE, $block = FALSE) {
|
||||||
|
global $user;
|
||||||
|
$output = '';
|
||||||
|
|
||||||
|
// Special display for side-block.
|
||||||
|
if ($block) {
|
||||||
|
// No 'read more' link.
|
||||||
|
$node->readmore = FALSE;
|
||||||
|
|
||||||
|
$links = module_invoke_all('link', 'node', $node, 1);
|
||||||
|
$links[] = array(
|
||||||
|
'title' => t('Older polls'),
|
||||||
|
'href' => 'poll',
|
||||||
|
'attributes' => array(
|
||||||
|
'title' => t('View the list of polls on this site.')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if ($node->allowvotes && $block) {
|
||||||
|
$links[] = array(
|
||||||
|
'title' => t('Results'),
|
||||||
|
'href' => 'node/'. $node->nid .'/results',
|
||||||
|
'attributes' => array(
|
||||||
|
'title' => t('View the current poll results.')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$node->links = $links;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($node->allowvotes) && ($block || empty($node->show_results))) {
|
||||||
|
$node->content['body'] = array(
|
||||||
|
'#value' => drupal_get_form('poll_view_voting', $node, $block),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$node->content['body'] = array(
|
||||||
|
'#value' => i18npoll_view_results($node, $teaser, $page, $block),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a graphical representation of the results of a poll.
|
||||||
|
*/
|
||||||
|
function i18npoll_view_results(&$node, $teaser, $page, $block) {
|
||||||
|
// Load the appropriate choices into the $poll object.
|
||||||
|
$result = db_query("SELECT c.chorder, SUM(c.chvotes) AS votes FROM {poll_choices} c INNER JOIN {node} n ON c.nid = n.nid WHERE n.tnid = %d GROUP BY c.chorder", $node->tnid);
|
||||||
|
while ($choice = db_fetch_object($result)) {
|
||||||
|
// If this option not set for the source node, do not show.
|
||||||
|
if (isset($node->choice[$choice->chorder])) {
|
||||||
|
$node->choice[$choice->chorder]['chvotes'] = $choice->votes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return poll_view_results($node, $teaser, $page, $block);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user vote for this node or its translations.
|
||||||
|
*
|
||||||
|
* Returns object with nid, chorder. Has static caching as this will typically be called twice.
|
||||||
|
*/
|
||||||
|
function i18npoll_get_vote($tnid) {
|
||||||
|
global $user;
|
||||||
|
static $vote = array();
|
||||||
|
if (!array_key_exists($tnid, $vote)) {
|
||||||
|
if ($user->uid) {
|
||||||
|
$vote[$tnid] = db_fetch_object(db_query('SELECT v.nid, v.chorder FROM {poll_votes} v INNER JOIN {node} n ON n.nid = v.nid WHERE n.tnid = %d AND v.uid = %d', $tnid, $user->uid));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$vote[$tnid] = db_fetch_object(db_query("SELECT v.chorder FROM {poll_votes} v INNER JOIN {node} n ON n.nid = v.nid WHERE n.tnid = %d AND v.hostname = '%s'", $tnid, ip_address()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $vote[$tnid];
|
||||||
|
}
|
13
sites/all/modules/i18n/i18nprofile/i18nprofile.info
Normal file
13
sites/all/modules/i18n/i18nprofile/i18nprofile.info
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
name = Profile translation
|
||||||
|
description = Enables multilingual profile fields.
|
||||||
|
dependencies[] = profile
|
||||||
|
dependencies[] = i18nstrings
|
||||||
|
package = Multilanguage
|
||||||
|
core = 6.x
|
||||||
|
|
||||||
|
; Information added by drupal.org packaging script on 2011-10-11
|
||||||
|
version = "6.x-1.10"
|
||||||
|
core = "6.x"
|
||||||
|
project = "i18n"
|
||||||
|
datestamp = "1318336004"
|
||||||
|
|
68
sites/all/modules/i18n/i18nprofile/i18nprofile.install
Normal file
68
sites/all/modules/i18n/i18nprofile/i18nprofile.install
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Installation file for i18nprofile module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh strings when enabled.
|
||||||
|
*/
|
||||||
|
function i18nprofile_enable() {
|
||||||
|
drupal_load('module', 'i18nstrings');
|
||||||
|
i18nprofile_locale_refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drupal 6 updates.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drop old table and fields.
|
||||||
|
*/
|
||||||
|
function i18nprofile_update_2() {
|
||||||
|
$items = array();
|
||||||
|
|
||||||
|
// Create source strings for translations.
|
||||||
|
$categories = array();
|
||||||
|
$result = db_query("SELECT * FROM {profile_fields}");
|
||||||
|
while ($field = db_fetch_object($result)) {
|
||||||
|
// Store strings to translate: title, explanation, options.
|
||||||
|
i18nstrings_update_object("profile:field:$field->name", $field, array('title', 'explanation', 'options'));
|
||||||
|
if (!in_array($field->category, $categories)) {
|
||||||
|
$categories[] = $field->category;
|
||||||
|
i18nstrings_update("profile:category", $field->category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Category translations from variables.
|
||||||
|
foreach (array_keys(language_list()) as $lang) {
|
||||||
|
if ($translation = variable_get('i18nprofile_'. $lang, FALSE)) {
|
||||||
|
foreach ($translation as $category => $translation) {
|
||||||
|
if (in_array($category, $categories) && $translation) {
|
||||||
|
$context = i18nstrings_context('profile:category', $category);
|
||||||
|
i18nstrings_update_translation($context, $lang, $translation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Move current data into string translations.
|
||||||
|
$result = db_query("SELECT * FROM {i18n_profile_fields}");
|
||||||
|
while ($field = db_fetch_object($result)) {
|
||||||
|
foreach (array('title', 'explanation', 'options') as $property) {
|
||||||
|
if (!empty($field->$property)) {
|
||||||
|
i18nstrings_update_translation("profile:field:$field->name:$property", $field->language, $field->$property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up. Uncomment when it all works.
|
||||||
|
/*
|
||||||
|
function i18nprofile_update_3() {
|
||||||
|
$items[] = update_sql("DROP TABLE {i18n_profile_fields};");
|
||||||
|
|
||||||
|
foreach (array_keys(language_list()) as $lang) {
|
||||||
|
variable_del('i18nprofile_'.$lang);
|
||||||
|
}
|
||||||
|
return $items;
|
||||||
|
}*/
|
255
sites/all/modules/i18n/i18nprofile/i18nprofile.module
Normal file
255
sites/all/modules/i18n/i18nprofile/i18nprofile.module
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Internationalization (i18n) submodule: Profile translation.
|
||||||
|
*
|
||||||
|
* Allows translation of profile categories and fields.
|
||||||
|
*
|
||||||
|
* The i18n strings created by this module are:
|
||||||
|
* - profile:field:[name]:[title,explanation,options]
|
||||||
|
* - profile:category (Indexed by string)
|
||||||
|
*
|
||||||
|
* @author Jose A. Reyero, 2006
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_help().
|
||||||
|
*/
|
||||||
|
function i18nprofile_help($path, $arg) {
|
||||||
|
switch ($path) {
|
||||||
|
case 'admin/modules#description':
|
||||||
|
$output = '<p>'. t('Supports translation for profile module field names and descriptions.') .'</p>';
|
||||||
|
$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_locale().
|
||||||
|
*/
|
||||||
|
function i18nprofile_locale($op = 'groups', $group = NULL) {
|
||||||
|
switch ($op) {
|
||||||
|
case 'groups':
|
||||||
|
return array('profile' => t('Profile'));
|
||||||
|
case 'info':
|
||||||
|
$info['profile']['refresh callback'] = 'i18nprofile_locale_refresh';
|
||||||
|
$info['profile']['format'] = FALSE;
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh strings.
|
||||||
|
*/
|
||||||
|
function i18nprofile_locale_refresh() {
|
||||||
|
$result = db_query('SELECT * FROM {profile_fields}');
|
||||||
|
$categories = array();
|
||||||
|
while ($field = db_fetch_object($result)) {
|
||||||
|
// Store strings to translate: title, explanation, options.
|
||||||
|
i18nstrings_update_object("profile:field:$field->name", $field, array('title', 'explanation', 'options'));
|
||||||
|
// Store category if not there yet.
|
||||||
|
if (!isset($categories[$field->category])) {
|
||||||
|
i18nstrings_update("profile:category", $field->category);
|
||||||
|
$categories[$field->category] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE; // Meaning it completed with no issues
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_menu_alter().
|
||||||
|
*
|
||||||
|
* Replace title callbacks for profile categories.
|
||||||
|
*/
|
||||||
|
function i18nprofile_menu_alter(&$items) {
|
||||||
|
$empty_account = new stdClass();
|
||||||
|
if (($categories = _user_categories($empty_account)) && (count($categories) > 1)) {
|
||||||
|
foreach ($categories as $key => $category) {
|
||||||
|
// 'account' is already handled by the MENU_DEFAULT_LOCAL_TASK.
|
||||||
|
$path = 'user/%user_category/edit/'. $category['name'];
|
||||||
|
if ($category['name'] != 'account' && !empty($items[$path])) {
|
||||||
|
$items[$path]['title callback'] = 'i18nprofile_translate_category'; // Was 'check_plain',
|
||||||
|
$items[$path]['title arguments'] = array($category['title']); // Was array($category['title'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function i18nprofile_translate_category($title) {
|
||||||
|
return check_plain(i18nstrings('profile:category', $title));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_profile_alter().
|
||||||
|
*
|
||||||
|
* Translates categories and fields.
|
||||||
|
*/
|
||||||
|
function i18nprofile_profile_alter(&$account) {
|
||||||
|
foreach (profile_categories() as $category) {
|
||||||
|
$name = $category['name'];
|
||||||
|
if (!empty($account->content[$name])) {
|
||||||
|
// First ranslate category title then fields.
|
||||||
|
$account->content[$name]['#title'] = i18nstrings('profile:category', $account->content[$name]['#title']);
|
||||||
|
foreach (element_children($account->content[$name]) as $field) {
|
||||||
|
i18nprofile_form_translate_field($account->content[$name], $field);
|
||||||
|
// Translate value if options field
|
||||||
|
if (!empty($account->content[$name][$field]['#value']) && $options = i18nprofile_field_options($field)) {
|
||||||
|
// Get the value from the account because this one may have been formatted.
|
||||||
|
if (isset($options[$account->$field])) {
|
||||||
|
// It may be a link or a paragraph, trick for not loading the field again.
|
||||||
|
if (!preg_match('|^<a href="|', $account->content[$name][$field]['#value'])) {
|
||||||
|
// Plain html
|
||||||
|
$account->content[$name][$field]['#value'] = check_markup($options[$account->$field]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Link
|
||||||
|
$account->content[$name][$field]['#value'] = check_markup(l($options[$account->$field], 'profile/'. $field .'/'. $account->$field));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_form_alter().
|
||||||
|
*/
|
||||||
|
function i18nprofile_form_alter(&$form, $form_state, $form_id) {
|
||||||
|
switch ($form_id) {
|
||||||
|
case 'profile_field_form':
|
||||||
|
$form['#submit'][] = 'i18nprofile_field_form_submit';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'user_profile_form':
|
||||||
|
if (($category = $form['_category']['#value']) && $category != 'account') {
|
||||||
|
i18nprofile_form_translate_category($form, $category);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'user_register':
|
||||||
|
i18nprofile_form_translate_all($form_id, $form);
|
||||||
|
break;
|
||||||
|
case 'profile_field_delete':
|
||||||
|
// Store all field info for further reference
|
||||||
|
$form['field'] = array(
|
||||||
|
'#type' => 'value',
|
||||||
|
'#value' => db_fetch_object(db_query("SELECT * FROM {profile_fields} WHERE fid = %d", $form['fid']['#value'])),
|
||||||
|
);
|
||||||
|
$form['#submit'][] = 'i18nprofile_field_delete_submit';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delte field profile related strings
|
||||||
|
*/
|
||||||
|
function i18nprofile_field_delete_submit($form, $form_state) {
|
||||||
|
$field = $form_state['values']['field'];
|
||||||
|
foreach (array('title', 'explanation', 'options') as $property) {
|
||||||
|
i18nstrings_remove_string("profile:field:$field->name:$property");
|
||||||
|
}
|
||||||
|
// Delete category too if no more fields in the same category
|
||||||
|
if (!db_result(db_query("SELECT COUNT(*) FROM {profile_fields} WHERE category = '%s'", $field->category))) {
|
||||||
|
i18nstrings_remove_string("profile:category", $values->category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process profile_field_form submissions.
|
||||||
|
*/
|
||||||
|
function i18nprofile_field_form_submit($form, &$form_state) {
|
||||||
|
$values = (object)$form_state['values'];
|
||||||
|
// Check old field name in case it has changed.
|
||||||
|
$oldname = $form['fields']['name']['#default_value'];
|
||||||
|
if ($oldname != 'profile_' && $oldname != $values->name) {
|
||||||
|
i18nstrings_update_context("profile:field:$oldname:*", "profile:field:$values->name:*");
|
||||||
|
}
|
||||||
|
// Store category.
|
||||||
|
i18nstrings_update("profile:category", $values->category);
|
||||||
|
// Store strings to translate: title, explanation, options.
|
||||||
|
i18nstrings_update_object("profile:field:$values->name", $values, array('title', 'explanation', 'options'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate form fields for a given category.
|
||||||
|
*/
|
||||||
|
function i18nprofile_form_translate_category(&$form, $category) {
|
||||||
|
if (!empty($form[$category])) {
|
||||||
|
$form[$category]['#title'] = i18nstrings('profile:category', $form[$category]['#title']);
|
||||||
|
foreach (element_children($form[$category]) as $field) {
|
||||||
|
i18nprofile_form_translate_field($form[$category], $field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate form field.
|
||||||
|
*/
|
||||||
|
function i18nprofile_form_translate_field(&$form, $field) {
|
||||||
|
if (!empty($form[$field]['#title'])) {
|
||||||
|
$form[$field]['#title'] = i18nstrings("profile:field:$field:title", $form[$field]['#title']);
|
||||||
|
}
|
||||||
|
elseif (!empty($form[$field]['#value'])) {
|
||||||
|
// Special treating for checboxes.
|
||||||
|
$field_type = db_result(db_query("SELECT type FROM {profile_fields} WHERE name = '%s'", $field));
|
||||||
|
if ($field_type == 'checkbox') {
|
||||||
|
$form[$field]['#value'] = i18nstrings("profile:field:$field:title", $form[$field]['#value']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($form[$field]['#description'])) {
|
||||||
|
$form[$field]['#description'] = i18nstrings("profile:field:$field:explanation", $form[$field]['#description']);
|
||||||
|
}
|
||||||
|
if (!empty($form[$field]['#options'])) {
|
||||||
|
if ($options = i18nprofile_field_options($field, $form[$field]['#options'])) {
|
||||||
|
$form[$field]['#options'] = $options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates field options.
|
||||||
|
*/
|
||||||
|
function i18nprofile_field_options($field, $source = array()) {
|
||||||
|
if ($translation = i18nstrings("profile:field:$field:options", '')) {
|
||||||
|
// Troubles when doing the split, produces empty lines, quick fix
|
||||||
|
$translation = str_replace("\r", '', $translation);
|
||||||
|
$translation = explode("\n", $translation);
|
||||||
|
if ($source) {
|
||||||
|
$options = $source;
|
||||||
|
}
|
||||||
|
elseif ($source = db_result(db_query("SELECT options FROM {profile_fields} WHERE name = '%s'", $field))) {
|
||||||
|
$source = str_replace("\r", '', $source);
|
||||||
|
$source = explode("\n", $source);
|
||||||
|
$options = array();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
foreach ($source as $value) {
|
||||||
|
if ($value != '--') {
|
||||||
|
$string = $translation ? trim(array_shift($translation)) : trim($value);
|
||||||
|
$options[trim($value)] = $string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate form fields for all categories.
|
||||||
|
*
|
||||||
|
* This is useful when we don't know which categories we have, like in the user register form.
|
||||||
|
*/
|
||||||
|
function i18nprofile_form_translate_all($form_id, &$form) {
|
||||||
|
$categories = profile_categories();
|
||||||
|
if (is_array($categories)) {
|
||||||
|
foreach ($categories as $category) {
|
||||||
|
if (isset($form[$category['name']])) {
|
||||||
|
i18nprofile_form_translate_category( $form, $category['name']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
148
sites/all/modules/i18n/i18nstrings/i18nstrings.admin.inc
Normal file
148
sites/all/modules/i18n/i18nstrings/i18nstrings.admin.inc
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Load locale library
|
||||||
|
include_once './includes/locale.inc';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Admin page callbacks for the i18nstrings module.
|
||||||
|
*/
|
||||||
|
function i18nstrings_admin_refresh_page() {
|
||||||
|
$output = '';
|
||||||
|
$output .= drupal_get_form('i18nstrings_admin_refresh');
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form callback. Refresh textgroups.
|
||||||
|
*/
|
||||||
|
function i18nstrings_admin_refresh() {
|
||||||
|
// Select textgroup/s. Just the ones that have a 'refresh callback'
|
||||||
|
$groups = module_invoke_all('locale', 'groups');
|
||||||
|
unset($groups['default']);
|
||||||
|
foreach (array_keys($groups) as $key) {
|
||||||
|
if (!i18nstrings_group_info($key, 'refresh callback')) {
|
||||||
|
unset($groups[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$form['groups'] = array(
|
||||||
|
'#type' => 'checkboxes',
|
||||||
|
'#title' => t('Select text groups'),
|
||||||
|
'#options' => $groups,
|
||||||
|
'#description' => t('If a text group is no showing up here it means this feature is not implemented for it.'),
|
||||||
|
);
|
||||||
|
$form['refresh'] = array(
|
||||||
|
'#type' => 'submit',
|
||||||
|
'#value' => t('Refresh strings'),
|
||||||
|
'#suffix' => '<p>'. t('This will create all the missing strings for the selected text groups.') .'</p>',
|
||||||
|
);
|
||||||
|
// Get all languages, except default language.
|
||||||
|
$languages = locale_language_list('name', TRUE);
|
||||||
|
unset($languages[language_default('language')]);
|
||||||
|
$form['languages'] = array(
|
||||||
|
'#type' => 'checkboxes',
|
||||||
|
'#title' => t('Select languages'),
|
||||||
|
'#options' => $languages,
|
||||||
|
);
|
||||||
|
$form['update'] = array(
|
||||||
|
'#type' => 'submit',
|
||||||
|
'#value' => t('Update translations'),
|
||||||
|
'#suffix' => '<p>'. t('This will fetch all existing translations from the localization tables for the selected text groups and languages.') .'</p>',
|
||||||
|
);
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form submission.
|
||||||
|
*/
|
||||||
|
function i18nstrings_admin_refresh_submit($form, &$form_state) {
|
||||||
|
$op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
|
||||||
|
$groups = array_filter($form_state['values']['groups']);
|
||||||
|
$languages = array_filter($form_state['values']['languages']);
|
||||||
|
$group_names = module_invoke_all('locale', 'groups');
|
||||||
|
if ($op == t('Refresh strings') && $groups) {
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
if (i18nstrings_refresh_group($group, TRUE)) {
|
||||||
|
drupal_set_message(t("Successfully refreshed strings for %group", array('%group' => $group_names[$group])));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
drupal_set_message(t("Cannot refresh strings for %group.", array('%group' => $group_names[$group])), 'warning');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($op == t('Update translations') && $groups && $languages) {
|
||||||
|
$count = 0;
|
||||||
|
foreach ($languages as $language) {
|
||||||
|
$count += i18nstrings_admin_update($language, $groups);
|
||||||
|
}
|
||||||
|
drupal_set_message(format_plural($count, '1 string has been updated.', '@count strings have been updated.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update strings for language.
|
||||||
|
*/
|
||||||
|
function i18nstrings_admin_update($language, $groups) {
|
||||||
|
$params = $groups;
|
||||||
|
$params[] = $language;
|
||||||
|
$sql = 'SELECT g.*, t.translation, t.lid as tlid, i.format FROM {locales_source} g INNER JOIN {locales_source} s ON g.source = s.source AND s.lid <> g.lid ';
|
||||||
|
$sql .= 'INNER JOIN {locales_target} t ON s.lid = t.lid LEFT JOIN {locales_target} t2 ON g.lid = t2.lid ';
|
||||||
|
$sql .= 'INNER JOIN {i18n_strings} i ON i.lid = g.lid ';
|
||||||
|
$sql .= 'WHERE t2.lid IS NULL AND g.textgroup IN ('. db_placeholders($groups, 'varchar') .") AND t.language = '%s'";
|
||||||
|
$result = db_query($sql, $params);
|
||||||
|
$count = 0;
|
||||||
|
while ($string = db_fetch_object($result)) {
|
||||||
|
// Just update strings when no input format, otherwise it could be dangerous under some circumstances.
|
||||||
|
if (empty($string->format) && !empty($string->translation)) {
|
||||||
|
$count++;
|
||||||
|
db_query("INSERT INTO {locales_target} (translation, lid, language) VALUES('%s', %d, '%s')", $string->translation, $string->lid, $language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure filters for string translation.
|
||||||
|
*
|
||||||
|
* This has serious security implications so this form needs the 'administer filters' permission
|
||||||
|
*/
|
||||||
|
function i18nstrings_admin_settings() {
|
||||||
|
include_once './includes/locale.inc';
|
||||||
|
// As the user has administer filters permissions we get a full list here
|
||||||
|
foreach (filter_formats() as $fid => $format) {
|
||||||
|
$format_list[$fid] = $format->name;
|
||||||
|
}
|
||||||
|
$form['i18nstrings_allowed_formats'] = array(
|
||||||
|
'#title' => t('Translatable input formats'),
|
||||||
|
'#options' => $format_list,
|
||||||
|
'#type' => 'checkboxes',
|
||||||
|
'#default_value' => variable_get('i18nstrings_allowed_formats', array(variable_get('filter_default_format', 1))),
|
||||||
|
'#description' => t('Only the strings that have the input formats selected will be allowed by the translation system. All the others will be deleted next time the strings are refreshed.'),
|
||||||
|
);
|
||||||
|
// Whitelist text groups without formatted strings for backwards compatibility
|
||||||
|
$textgroups = module_invoke_all('locale', 'groups');
|
||||||
|
unset($textgroups['default']);
|
||||||
|
foreach (array_keys($textgroups) as $group) {
|
||||||
|
$format = i18nstrings_group_info($group, 'format');
|
||||||
|
if (isset($format)) {
|
||||||
|
// This one already has format information, so remove from list
|
||||||
|
unset($textgroups[$group]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If there are 'old' textgroups, display the bypass option
|
||||||
|
if ($textgroups) {
|
||||||
|
$form['i18nstrings_allowed_textgroups'] = array(
|
||||||
|
'#title' => t('Safe text groups'),
|
||||||
|
'#options' => $textgroups,
|
||||||
|
'#type' => 'checkboxes',
|
||||||
|
'#default_value' => variable_get('i18nstrings_allowed_textgroups', array()),
|
||||||
|
'#description' => t('Select text groups to bypass filter format checking. . <strong>It is unsafe to check this option unless you are sure all the strings from that text groups are safe for translators</strong>. This option is just for backwards compatibility until all the contributed modules implement the new strings API.'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
elseif (variable_get('i18nstrings_allowed_textgroups', 0)) {
|
||||||
|
// Just in case there's a leftover variable before we updated some of the modules
|
||||||
|
variable_del('i18nstrings_allowed_textgroups');
|
||||||
|
}
|
||||||
|
$form['array_filter'] = array('#type' => 'value', '#value' => TRUE);
|
||||||
|
return system_settings_form($form);
|
||||||
|
}
|
12
sites/all/modules/i18n/i18nstrings/i18nstrings.info
Normal file
12
sites/all/modules/i18n/i18nstrings/i18nstrings.info
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
name = String translation
|
||||||
|
description = Provides support for translation of user defined strings.
|
||||||
|
dependencies[] = locale
|
||||||
|
package = Multilanguage
|
||||||
|
core = 6.x
|
||||||
|
|
||||||
|
; Information added by drupal.org packaging script on 2011-10-11
|
||||||
|
version = "6.x-1.10"
|
||||||
|
core = "6.x"
|
||||||
|
project = "i18n"
|
||||||
|
datestamp = "1318336004"
|
||||||
|
|
219
sites/all/modules/i18n/i18nstrings/i18nstrings.install
Normal file
219
sites/all/modules/i18n/i18nstrings/i18nstrings.install
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Installation file for i18nstrings module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_install().
|
||||||
|
*/
|
||||||
|
function i18nstrings_install() {
|
||||||
|
// Create database tables.
|
||||||
|
drupal_install_schema('i18nstrings');
|
||||||
|
|
||||||
|
// Set module weight for it to run after core modules.
|
||||||
|
db_query("UPDATE {system} SET weight = 10 WHERE name = 'i18nstrings' AND type = 'module'");
|
||||||
|
|
||||||
|
$ret = array();
|
||||||
|
|
||||||
|
// Add a field to track whether a translation needs updating.
|
||||||
|
db_add_field($ret, 'locales_target', 'i18n_status', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
|
||||||
|
|
||||||
|
// Add custom index to locales_source table.
|
||||||
|
db_add_index($ret, 'locales_source', 'textgroup_location', array(array('textgroup', 30), 'location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_uninstall().
|
||||||
|
*/
|
||||||
|
function i18nstrings_uninstall() {
|
||||||
|
$ret = array();
|
||||||
|
|
||||||
|
// Create database tables.
|
||||||
|
drupal_uninstall_schema('i18nstrings');
|
||||||
|
// @TODO locale table cleanup, think about it.
|
||||||
|
// Should we drop all strings for groups other than 'default' ?
|
||||||
|
|
||||||
|
// Drop custom field.
|
||||||
|
db_drop_field($ret, 'locales_target', 'i18n_status');
|
||||||
|
// Drop custom index.
|
||||||
|
db_drop_index($ret, 'locales_source', 'textgroup_location');
|
||||||
|
// May be a left over variable
|
||||||
|
variable_del('i18nstrings_update_skip');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_schema().
|
||||||
|
*/
|
||||||
|
function i18nstrings_schema() {
|
||||||
|
$schema['i18n_strings'] = array(
|
||||||
|
'description' => 'Metadata for source strings.',
|
||||||
|
'fields' => array(
|
||||||
|
'lid' => array(
|
||||||
|
'type' => 'int',
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => 0,
|
||||||
|
'description' => 'Source string ID. References {locales_source}.lid.',
|
||||||
|
),
|
||||||
|
'objectid' => array(
|
||||||
|
'type' => 'varchar',
|
||||||
|
'length' => 255,
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => '',
|
||||||
|
'description' => 'Object ID.',
|
||||||
|
),
|
||||||
|
'type' => array(
|
||||||
|
'type' => 'varchar',
|
||||||
|
'length' => 255,
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => '',
|
||||||
|
'description' => 'Object type for this string.',
|
||||||
|
),
|
||||||
|
'property' => array(
|
||||||
|
'type' => 'varchar',
|
||||||
|
'length' => 255,
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => '',
|
||||||
|
'description' => 'Object property for this string.',
|
||||||
|
),
|
||||||
|
'objectindex' => array(
|
||||||
|
'type' => 'int',
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => 0,
|
||||||
|
'description' => 'Integer value of Object ID.',
|
||||||
|
),
|
||||||
|
'format' => array(
|
||||||
|
'description' => "The input format used by this string.",
|
||||||
|
'type' => 'int',
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => 0
|
||||||
|
),
|
||||||
|
|
||||||
|
),
|
||||||
|
'primary key' => array('lid'),
|
||||||
|
);
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_schema_alter().
|
||||||
|
*/
|
||||||
|
function i18nstrings_schema_alter(&$schema) {
|
||||||
|
// Add index for textgroup and location to {locales_source}.
|
||||||
|
$schema['locales_source']['indexes']['textgroup_location'] = array(array('textgroup', 30), 'location');
|
||||||
|
// Add field for tracking whether translations need updating.
|
||||||
|
$schema['locales_target']['fields']['i18n_status'] = array(
|
||||||
|
'description' => 'A boolean indicating whether this translation needs to be updated.',
|
||||||
|
'type' => 'int',
|
||||||
|
'not null' => TRUE,
|
||||||
|
'default' => 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update from 5.x version
|
||||||
|
*/
|
||||||
|
function i18nstrings_update_5900() {
|
||||||
|
i18nstrings_install();
|
||||||
|
// Mark next update to be skipped
|
||||||
|
variable_set('i18nstrings_update_skip', 1);
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add new 'oid' column to i18n_strings table.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
function i18nstrings_update_6000() {
|
||||||
|
$ret = array();
|
||||||
|
db_add_field($ret, 'i18n_strings', 'oid', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drupal 6 update. Change field name, 'oid' was psql reserved name.
|
||||||
|
*
|
||||||
|
* ALTER TABLE `drupal6_i18n`.`i18n_strings` CHANGE COLUMN `oid` `objectid` INTEGER NOT NULL DEFAULT 0;
|
||||||
|
*/
|
||||||
|
function i18nstrings_update_6001() {
|
||||||
|
$ret = array();
|
||||||
|
if (!variable_get('i18nstrings_update_skip', 0)) {
|
||||||
|
db_change_field($ret, 'i18n_strings', 'oid', 'objectid', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add index to {locales_source}.
|
||||||
|
*/
|
||||||
|
function i18nstrings_update_6002() {
|
||||||
|
$ret = array();
|
||||||
|
if (!variable_get('i18nstrings_update_skip', 0)) {
|
||||||
|
db_add_index($ret, 'locales_source', 'textgroup_location', array(array('textgroup', 30), 'location'));
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create i18n_strings_status schema.
|
||||||
|
* Add a field to track whether a translation needs updating.
|
||||||
|
*/
|
||||||
|
function i18nstrings_update_6003() {
|
||||||
|
$ret = array();
|
||||||
|
if (!variable_get('i18nstrings_update_skip', 0)) {
|
||||||
|
db_add_field($ret, 'locales_target', 'status', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Add new D6 columns to i18n_strings table.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
function i18nstrings_update_6004() {
|
||||||
|
$ret = array();
|
||||||
|
|
||||||
|
// Remove D5 primary keys (`strid`,`locale`).
|
||||||
|
db_drop_primary_key($ret, 'i18n_strings');
|
||||||
|
|
||||||
|
// Add new lid field and add primary key back.
|
||||||
|
db_add_field($ret, 'i18n_strings', 'lid', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
|
||||||
|
db_add_primary_key($ret, 'i18n_strings', array('lid'));
|
||||||
|
|
||||||
|
db_add_field($ret, 'i18n_strings', 'type', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''));
|
||||||
|
db_add_field($ret, 'i18n_strings', 'property', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => 'default'));
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add objectindex, format fields, change objectid to varchar
|
||||||
|
*/
|
||||||
|
function i18nstrings_update_6005() {
|
||||||
|
$ret = array();
|
||||||
|
if (!variable_get('i18nstrings_update_skip', 0)) {
|
||||||
|
db_add_field($ret, 'i18n_strings', 'objectindex', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
|
||||||
|
// Populate new field from objectid before changing objectid to varchar
|
||||||
|
$ret[] = update_sql('UPDATE {i18n_strings} SET objectindex = objectid');
|
||||||
|
db_change_field($ret, 'i18n_strings', 'objectid', 'objectid', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''));
|
||||||
|
// Add format field
|
||||||
|
db_add_field($ret, 'i18n_strings', 'format', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
|
||||||
|
// Change wrong default value for property (was 'default')
|
||||||
|
db_change_field($ret, 'i18n_strings', 'property', 'property', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''));
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rename status field so it doesn't clash with other l10n modules
|
||||||
|
*/
|
||||||
|
function i18nstrings_update_6006() {
|
||||||
|
$ret = array();
|
||||||
|
if (!variable_get('i18nstrings_update_skip', 0)) {
|
||||||
|
db_change_field($ret, 'locales_target', 'status', 'i18n_status', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
1230
sites/all/modules/i18n/i18nstrings/i18nstrings.module
Normal file
1230
sites/all/modules/i18n/i18nstrings/i18nstrings.module
Normal file
File diff suppressed because it is too large
Load diff
22
sites/all/modules/i18n/i18nsync/README.txt
Normal file
22
sites/all/modules/i18n/i18nsync/README.txt
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
README.txt
|
||||||
|
==========
|
||||||
|
Drupal module: i18nsync (Synchronization)
|
||||||
|
|
||||||
|
This module will handle content synchronization accross translations.
|
||||||
|
|
||||||
|
The available list of fields to synchronize will include standard node fields and cck fields.
|
||||||
|
To have aditional fields, add the list in a variable in the settings.php file, like this:
|
||||||
|
|
||||||
|
// Available fields for synchronization, for all node types.
|
||||||
|
$conf['i18nsync_fields_node'] = array(
|
||||||
|
'field1' => t('Field 1 name'),
|
||||||
|
'field2' => t('Field 2 name'),
|
||||||
|
...
|
||||||
|
);
|
||||||
|
|
||||||
|
// More fields for a specific content type 'nodetype' only.
|
||||||
|
$conf['i18nsync_fields_node_nodetype'] = array(
|
||||||
|
'field3' => t('Field 3 name'),
|
||||||
|
...
|
||||||
|
);
|
11
sites/all/modules/i18n/i18nsync/i18nsync.info
Normal file
11
sites/all/modules/i18n/i18nsync/i18nsync.info
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
name = Synchronize translations
|
||||||
|
description = Synchronizes taxonomy and fields accross translations of the same content.
|
||||||
|
dependencies[] = i18n
|
||||||
|
package = Multilanguage
|
||||||
|
core = 6.x
|
||||||
|
; Information added by drupal.org packaging script on 2011-10-11
|
||||||
|
version = "6.x-1.10"
|
||||||
|
core = "6.x"
|
||||||
|
project = "i18n"
|
||||||
|
datestamp = "1318336004"
|
||||||
|
|
16
sites/all/modules/i18n/i18nsync/i18nsync.install
Normal file
16
sites/all/modules/i18n/i18nsync/i18nsync.install
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Installation file for i18nsync module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set module weight.
|
||||||
|
*
|
||||||
|
* Make sure this runs after taxonomy, i18n and translation modules
|
||||||
|
* and ideally after all other modules implementing nodeapi hook.
|
||||||
|
*/
|
||||||
|
function i18nsync_install() {
|
||||||
|
db_query("UPDATE {system} SET weight = 100 WHERE name = 'i18nsync' AND type = 'module'");
|
||||||
|
}
|
587
sites/all/modules/i18n/i18nsync/i18nsync.module
Normal file
587
sites/all/modules/i18n/i18nsync/i18nsync.module
Normal file
|
@ -0,0 +1,587 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Internationalization (i18n) package. Synchronization of translations
|
||||||
|
*
|
||||||
|
* Keeps vocabulary terms in sync for translations.
|
||||||
|
* This is a per-vocabulary option.
|
||||||
|
*
|
||||||
|
* Ref: http://drupal.org/node/115463
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* This module needs to run after taxonomy, i18n, translation. Check module weight.
|
||||||
|
*
|
||||||
|
* @ TODO Test with CCK when possible, api may have changed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_help().
|
||||||
|
*/
|
||||||
|
function i18nsync_help($path, $arg) {
|
||||||
|
switch ($path) {
|
||||||
|
case 'admin/help#i18nsync' :
|
||||||
|
$output = '<p>'. t('This module synchronizes content taxonomy and fields accross translations:') .'</p>';
|
||||||
|
$output .= '<p>'. t('First you need to select which fields should be synchronized. Then, after a node has been updated, all enabled vocabularies and fields will be synchronized as follows:') .'</p>';
|
||||||
|
$output .= '<ul>';
|
||||||
|
$output .= '<li>'. t('All the node fields selected for synchronization will be set to the same value for all translations.') .'</li>';
|
||||||
|
$output .= '<li>'. t('For multilingual vocabularies, the terms for all translations will be replaced by the translations of the original node terms.') .'</li>';
|
||||||
|
$output .= '<li>'. t('For other vocabularies, the terms will be just copied over to all the translations.') .'</li>';
|
||||||
|
$output .= '</ul>';
|
||||||
|
$output .= '<p><strong>'. t('Note that permissions are not checked for each node. So if someone can edit a node and it is set to synchronize, all the translations will be synchronized anyway.') .'</strong></p>';
|
||||||
|
$output .= '<p>'. t('To enable synchronization check content type options to select which fields to synchronize for each node type.') .'</p>';
|
||||||
|
$output .= '<p>'. t('The list of available fields for synchronization will include some standard node fields and all CCK fields. You can add more fields to the list in a configuration variable. See README.txt for how to do it.') .'</p>';
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_theme().
|
||||||
|
*/
|
||||||
|
function i18nsync_theme() {
|
||||||
|
return array(
|
||||||
|
'i18nsync_workflow_checkbox' => array(
|
||||||
|
'arguments' => array('item' => NULL),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_form_alter().
|
||||||
|
* - Vocabulary options
|
||||||
|
* - Content type options
|
||||||
|
*/
|
||||||
|
function i18nsync_form_alter(&$form, $form_state, $form_id) {
|
||||||
|
// Taxonomy vocabulary form.
|
||||||
|
switch ($form_id) {
|
||||||
|
case 'node_type_form':
|
||||||
|
$type = $form['#node_type']->type;
|
||||||
|
$current = i18nsync_node_fields($type);
|
||||||
|
$disabled = $form['i18n']['#disabled'];
|
||||||
|
$form['i18n']['i18nsync_nodeapi'] = array(
|
||||||
|
'#type' => 'fieldset',
|
||||||
|
'#tree' => TRUE,
|
||||||
|
'#title' => t('Synchronize translations'),
|
||||||
|
'#collapsible' => TRUE,
|
||||||
|
'#collapsed' => !count($current),
|
||||||
|
'#description' => t('Select which fields to synchronize for all translations of this content type.'),
|
||||||
|
'#disabled' => $disabled,
|
||||||
|
);
|
||||||
|
// Each set provides title and options. We build a big checkboxes control for it to be
|
||||||
|
// saved as an array. Special themeing for group titles.
|
||||||
|
foreach (i18nsync_node_available_fields($type) as $group => $data) {
|
||||||
|
$title = $data['#title'];
|
||||||
|
if (!empty($data['#options'])) {
|
||||||
|
foreach ($data['#options'] as $field => $name) {
|
||||||
|
$form['i18n']['i18nsync_nodeapi'][$field] = array(
|
||||||
|
'#group_title' => $title,
|
||||||
|
'#title' => $name,
|
||||||
|
'#type' => 'checkbox',
|
||||||
|
'#default_value' => in_array($field, $current),
|
||||||
|
'#theme' => 'i18nsync_workflow_checkbox',
|
||||||
|
'#disabled' => $disabled,
|
||||||
|
);
|
||||||
|
$title = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'node_delete_confirm':
|
||||||
|
// Intercept form submission so we can handle uploads, replace callback
|
||||||
|
$form['#submit'] = array_merge(array('i18nsync_node_delete_submit'), $form['#submit']);
|
||||||
|
break;
|
||||||
|
case 'node_admin_content':
|
||||||
|
if (!empty($form['operation']) && $form['operation']['#value'] == 'delete') {
|
||||||
|
$form['#submit'] = array_merge(array('i18nsync_node_delete_submit'), $form['#submit']);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit callback for
|
||||||
|
* - node delete confirm
|
||||||
|
* - node multiple delete confirm
|
||||||
|
*/
|
||||||
|
function i18nsync_node_delete_submit($form, $form_state) {
|
||||||
|
if ($form_state['values']['confirm']) {
|
||||||
|
if (!empty($form_state['values']['nid'])) {
|
||||||
|
// Single node
|
||||||
|
i18nsync_node_delete_prepare($form_state['values']['nid']);
|
||||||
|
}
|
||||||
|
elseif (!empty($form_state['values']['nodes'])) {
|
||||||
|
// Multiple nodes
|
||||||
|
foreach ($form_state['values']['nodes'] as $nid => $value) {
|
||||||
|
i18nsync_node_delete_prepare($nid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Then it will go through normal form submission
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare node for deletion, work out synchronization issues
|
||||||
|
*/
|
||||||
|
function i18nsync_node_delete_prepare($nid) {
|
||||||
|
$node = node_load($nid);
|
||||||
|
// Delete file associations when files are shared with existing translations
|
||||||
|
// so they are not removed by upload module
|
||||||
|
if (!empty($node->tnid) && module_exists('upload')) {
|
||||||
|
$result = db_query('SELECT u.* FROM {upload} u WHERE u.nid = %d AND u.fid IN (SELECT t.fid FROM {upload} t WHERE t.fid = u.fid AND t.nid <> u.nid)', $nid);
|
||||||
|
while ($up = db_fetch_object($result)) {
|
||||||
|
db_query("DELETE FROM {upload} WHERE fid = %d AND vid = %d", $up->fid, $up->vid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Theming function for workflow checkboxes.
|
||||||
|
*/
|
||||||
|
function theme_i18nsync_workflow_checkbox($element) {
|
||||||
|
$output = $element['#group_title'] ? '<div class="description">'. $element['#group_title'] .'</div>' : '';
|
||||||
|
$output .= theme('checkbox', $element);
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_nodeapi().
|
||||||
|
*/
|
||||||
|
function i18nsync_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
|
||||||
|
global $i18nsync; // This variable will be true when a sync operation is in progress.
|
||||||
|
|
||||||
|
// Only for nodes that have language and belong to a translation set.
|
||||||
|
if (translation_supported_type($node->type) && !empty($node->language) && !$i18nsync) {
|
||||||
|
switch ($op) {
|
||||||
|
case 'load':
|
||||||
|
// Add instance count for cck fields so we can use the information later, see hook_file_references()
|
||||||
|
if (!empty($node->tnid) && ($sync_fields = i18nsync_node_fields($node->type)) && ($content_fields = _i18nsync_cck_fields($node->type))) {
|
||||||
|
if ($translations = _i18nsync_node_translations($node, TRUE)) {
|
||||||
|
$count = count($translations);
|
||||||
|
foreach ($sync_fields as $field) {
|
||||||
|
if (isset($content_fields[$field]) && !empty($node->$field) && is_array($node->$field)) {
|
||||||
|
// The node field should be an array with one or more fields
|
||||||
|
// Reminder: Use brackets for $node->{$field}[$key] as $node->$field[$key] won't work
|
||||||
|
foreach (array_keys($node->$field) as $key) {
|
||||||
|
if (is_array($node->{$field}[$key])) {
|
||||||
|
$node->{$field}[$key]['i18nsync'] = $count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'prepare translation':
|
||||||
|
// We copy over all the fields to be synchronized.
|
||||||
|
if ($fields = i18nsync_node_fields($node->type)) {
|
||||||
|
i18nsync_prepare_translation($node, $node->translation_source, $fields);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'insert':
|
||||||
|
// When creating a translation, there are some aditional steps, different from update
|
||||||
|
if (!empty($node->translation_source)) {
|
||||||
|
// Set tnid that is not set by translation module
|
||||||
|
$node->tnid = $node->translation_source->tnid ? $node->translation_source->tnid : $node->translation_source->nid;
|
||||||
|
// If we have files, we need to save the files that have been inherited
|
||||||
|
if (!empty($node->files) && i18nsync_node_fields($node->type, 'files')) {
|
||||||
|
foreach ($node->files as $fid => $file) {
|
||||||
|
$file = (object)$file;
|
||||||
|
if (empty($file->remove) && empty($file->new)) {
|
||||||
|
db_query("INSERT INTO {upload} (fid, nid, vid, list, description, weight) VALUES (%d, %d, %d, %d, '%s', %d)", $file->fid, $node->nid, $node->vid, $file->list, $file->description, $file->weight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Intentional no break.
|
||||||
|
case 'update':
|
||||||
|
// Let's go with field synchronization.
|
||||||
|
if (!empty($node->tnid) && ($fields = i18nsync_node_fields($node->type)) && ($translations = _i18nsync_node_translations($node, TRUE))) {
|
||||||
|
$i18nsync = TRUE;
|
||||||
|
$count = 0;
|
||||||
|
// If we have fields we need to reload them so we have the full data (fid, etc...)
|
||||||
|
if (!empty($node->files) && in_array('files', $fields)) {
|
||||||
|
$node->files = upload_load($node);
|
||||||
|
}
|
||||||
|
// Disable language selection temporarily, enable it again later
|
||||||
|
i18n_selection_mode('off');
|
||||||
|
foreach ($translations as $trnode) {
|
||||||
|
if ($node->nid != $trnode->nid) {
|
||||||
|
i18nsync_node_translation($node, $trnode, $fields, $op);
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i18n_selection_mode('reset');
|
||||||
|
$i18nsync = FALSE;
|
||||||
|
drupal_set_message(format_plural($count, 'One node translation has been synchronized.', 'All @count node translations have been synchronized.'));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare node translation. Copy over sincronizable fields.
|
||||||
|
*/
|
||||||
|
function i18nsync_prepare_translation(&$node, $source, $field_list) {
|
||||||
|
foreach ($field_list as $field) {
|
||||||
|
if (empty($source->$field)) continue;
|
||||||
|
switch ($field) {
|
||||||
|
case 'taxonomy':
|
||||||
|
// Do nothing, this is handled by the i18ntaxonomy module
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$node->$field = $source->$field;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronizes fields for node translation.
|
||||||
|
*
|
||||||
|
* There's some specific handling for known fields like:
|
||||||
|
* - files, for file attachments.
|
||||||
|
* - iid (CCK node attachments, translations for them will be handled too).
|
||||||
|
*
|
||||||
|
* All the rest of the fields will be just copied over.
|
||||||
|
* The 'revision' field will have the special effect of creating a revision too for the translation.
|
||||||
|
*
|
||||||
|
* @param $node
|
||||||
|
* Source node being edited.
|
||||||
|
* @param $translation
|
||||||
|
* Node translation to synchronize, just needs nid property.
|
||||||
|
* @param $fields
|
||||||
|
* List of fields to synchronize.
|
||||||
|
* @param $op
|
||||||
|
* Node operation (insert|update).
|
||||||
|
*/
|
||||||
|
function i18nsync_node_translation($node, $translation, $fields, $op) {
|
||||||
|
// Load full node, we need all data here.
|
||||||
|
$translation = node_load($translation->nid, NULL, TRUE);
|
||||||
|
|
||||||
|
// Collect info on any CCK fields.
|
||||||
|
$content_fields = _i18nsync_cck_fields($node->type);
|
||||||
|
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
// Check for CCK fields first.
|
||||||
|
if (isset($content_fields[$field]) && isset($node->$field)) {
|
||||||
|
switch ($content_fields[$field]['type']) {
|
||||||
|
// TODO take type specific actions.
|
||||||
|
|
||||||
|
// Filefields and imagefields are syncronized equally.
|
||||||
|
case 'filefield':
|
||||||
|
case 'imagefield':
|
||||||
|
i18nsync_node_translation_filefield_field($node, $translation, $field);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'nodereference':
|
||||||
|
i18nsync_node_translation_nodereference_field($node, $translation, $field);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// For fields that don't need special handling.
|
||||||
|
$translation->$field = $node->$field;
|
||||||
|
}
|
||||||
|
// Skip over the regular handling.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch ($field) {
|
||||||
|
case 'taxonomy': // Do nothing it has already been syncd.
|
||||||
|
i18nsync_node_taxonomy($translation, $node);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'parent': // Book outlines, translating parent page if exists.
|
||||||
|
case 'iid': // Attached image nodes.
|
||||||
|
i18nsync_node_translation_attached_node($node, $translation, $field);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'images':
|
||||||
|
$translation->images = $node->images;
|
||||||
|
// Intentional no break so 'images' synchronizes files too.
|
||||||
|
// About images, see related patch status: http://drupal.org/node/360643
|
||||||
|
// @todo Weird things may happen if 'images' and 'files' are both selected
|
||||||
|
case 'files':
|
||||||
|
// Sync existing attached files. This should work for images too
|
||||||
|
foreach ((array)$node->files as $fid => $file) {
|
||||||
|
if (isset($translation->files[$fid])) {
|
||||||
|
// Just update list and weight properties, description can be different
|
||||||
|
$translation->files[$fid]->list = $file->list;
|
||||||
|
$translation->files[$fid]->weight = $file->weight;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// New file. Clone so we can set the new property just for this translation
|
||||||
|
$translation->files[$fid] = clone $file;
|
||||||
|
$translation->files[$fid]->new = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Drop removed files.
|
||||||
|
foreach ((array)$translation->files as $fid => $file) {
|
||||||
|
if (!isset($node->files[$fid])) {
|
||||||
|
$translation->files[$fid]->remove = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// For fields that don't need special handling.
|
||||||
|
if (isset($node->$field)) {
|
||||||
|
$translation->$field = $node->$field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node_save($translation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronize taxonomy.
|
||||||
|
*
|
||||||
|
* Translate translatable terms, just copy over the rest.
|
||||||
|
*/
|
||||||
|
function i18nsync_node_taxonomy(&$node, &$source) {
|
||||||
|
if (module_exists('i18ntaxonomy') && is_array($source->taxonomy)) {
|
||||||
|
// Load clean source node taxonomy so we don't need to handle weird form input
|
||||||
|
if (!isset($source->i18ntaxonomy)) {
|
||||||
|
$source->i18ntaxonomy = i18ntaxonomy_node_get_terms($source);
|
||||||
|
}
|
||||||
|
$node->taxonomy = i18ntaxonomy_translate_terms($source->i18ntaxonomy, $node->language, FALSE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If not multilingual taxonomy enabled, just copy over.
|
||||||
|
$node->taxonomy = $source->taxonomy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node attachments (CCK) that may have translation.
|
||||||
|
*/
|
||||||
|
function i18nsync_node_translation_attached_node(&$node, &$translation, $field) {
|
||||||
|
if ($attached = node_load($node->$field)) {
|
||||||
|
$translation->$field = i18nsync_node_translation_reference_field($attached, $node->$field, $translation->language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translating a nodereference field (cck).
|
||||||
|
*/
|
||||||
|
function i18nsync_node_translation_nodereference_field(&$node, &$translation, $field) {
|
||||||
|
$translated_references = array();
|
||||||
|
foreach ($node->$field as $reference) {
|
||||||
|
if ($reference_node = node_load($reference['nid'])) {
|
||||||
|
$translated_references[] = array(
|
||||||
|
'nid' => i18nsync_node_translation_reference_field($reference_node, $reference['nid'], $translation->language)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$translation->$field = $translated_references;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translating an filefield (cck).
|
||||||
|
*/
|
||||||
|
function i18nsync_node_translation_filefield_field(&$node, &$translation, $field) {
|
||||||
|
if (is_array($node->$field)) {
|
||||||
|
$translated_images = array();
|
||||||
|
foreach ($node->$field as $file) {
|
||||||
|
$found = false;
|
||||||
|
|
||||||
|
// Try to find existing translations of the filefield items and reference them.
|
||||||
|
foreach ($translation->$field as $translation_image) {
|
||||||
|
if ($file['fid'] == $translation_image['fid']) {
|
||||||
|
$translated_images[] = $translation_image;
|
||||||
|
$found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there was no translation found for the filefield item, just copy it.
|
||||||
|
if (!$found) {
|
||||||
|
$translated_images[] = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$translation->$field = $translated_images;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to which translates reference field. We try to use translations for reference, otherwise fallback.
|
||||||
|
* Example:
|
||||||
|
* English A references English B and English C.
|
||||||
|
* English A and B are translated to German A and B, but English C is not.
|
||||||
|
* The syncronization from English A to German A would it German B and English C.
|
||||||
|
*/
|
||||||
|
function i18nsync_node_translation_reference_field(&$reference_node, $default_value, $langcode) {
|
||||||
|
if (isset($reference_node->tnid) && translation_supported_type($reference_node->type)) {
|
||||||
|
// This content type has translations, find the one.
|
||||||
|
if (($reference_trans = translation_node_get_translations($reference_node->tnid)) && isset($reference_trans[$langcode])) {
|
||||||
|
return $reference_trans[$langcode]->nid;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// No requested language found, just copy the field.
|
||||||
|
return $default_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Content type without language, just copy the field.
|
||||||
|
return $default_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list of fields to synchronize for a given content type.
|
||||||
|
*
|
||||||
|
* @param $type
|
||||||
|
* Node type.
|
||||||
|
* @param $field
|
||||||
|
* Optional field name to check whether it is in the list
|
||||||
|
*/
|
||||||
|
function i18nsync_node_fields($type, $field = NULL) {
|
||||||
|
$fields = variable_get('i18nsync_nodeapi_'. $type, array());
|
||||||
|
return $field ? in_array($field, $fields) : $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list of available fields for given content type.
|
||||||
|
*
|
||||||
|
* There are two hidden variables (without UI) that can be used to add fields
|
||||||
|
* with the form array('field' => 'Field name')
|
||||||
|
* - i18nsync_fields_node
|
||||||
|
* - i18nsync_fields_node_$type;
|
||||||
|
*
|
||||||
|
* Fields can also be changed using hook_i18nsync_fields_alter($fields, $type)
|
||||||
|
*
|
||||||
|
* @param $type
|
||||||
|
* Node type.
|
||||||
|
*/
|
||||||
|
function i18nsync_node_available_fields($type) {
|
||||||
|
static $cache;
|
||||||
|
|
||||||
|
if (!isset($cache[$type])) {
|
||||||
|
// Default node fields.
|
||||||
|
$fields['node']['#title'] = t('Standard node fields.');
|
||||||
|
$options = variable_get('i18nsync_fields_node', array());
|
||||||
|
$options += array(
|
||||||
|
'name' => t('Author'),
|
||||||
|
'status' => t('Status'),
|
||||||
|
'promote' => t('Promote'),
|
||||||
|
'moderate' => t('Moderate'),
|
||||||
|
'sticky' => t('Sticky'),
|
||||||
|
'revision' => t('Revision (Create also new revision for translations)'),
|
||||||
|
'parent' => t('Book outline (with the translated parent)'),
|
||||||
|
'taxonomy' => t('Taxonomy terms'),
|
||||||
|
);
|
||||||
|
if (module_exists('comment')) {
|
||||||
|
$options['comment'] = t('Comment settings');
|
||||||
|
}
|
||||||
|
if (module_exists('upload')) {
|
||||||
|
$options['files'] = t('File attachments');
|
||||||
|
}
|
||||||
|
// Location module
|
||||||
|
if (module_exists('location')) {
|
||||||
|
$options['locations'] = t('Location settings');
|
||||||
|
}
|
||||||
|
// If no type defined yet, that's it.
|
||||||
|
$fields['node']['#options'] = $options;
|
||||||
|
|
||||||
|
if (!$type) {
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get variable for this node type.
|
||||||
|
$fields += variable_get("i18nsync_fields_node_$type", array());
|
||||||
|
|
||||||
|
// Image and image attach.
|
||||||
|
if (module_exists('image') && $type == 'image') {
|
||||||
|
$image['images'] = t('Image files');
|
||||||
|
}
|
||||||
|
if (module_exists('image_attach') && variable_get('image_attach_'. $type, 0)) {
|
||||||
|
$image['iid'] = t('Attached image nodes');
|
||||||
|
}
|
||||||
|
if (!empty($image)) {
|
||||||
|
$fields['image']['#title'] = t('Image module');
|
||||||
|
$fields['image']['#options'] = $image;
|
||||||
|
}
|
||||||
|
// Event fields.
|
||||||
|
if (variable_get('event_nodeapi_'. $type, 'never') != 'never') {
|
||||||
|
$fields['event']['#title'] = t('Event fields');
|
||||||
|
$fields['event']['#options'] = array(
|
||||||
|
'event_start' => t('Event start'),
|
||||||
|
'event_end' => t('Event end'),
|
||||||
|
'timezone' => t('Timezone')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get CCK fields.
|
||||||
|
if (($contentfields = _i18nsync_cck_fields($type))) {
|
||||||
|
// Get context information.
|
||||||
|
$info = module_invoke('content', 'fields', NULL, $type);
|
||||||
|
$fields['cck']['#title'] = t('CCK fields');
|
||||||
|
foreach ($contentfields as $name => $data) {
|
||||||
|
$fields['cck']['#options'][$data['field_name']] = $data['widget']['label'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give a chance to modules to change/remove/add their own fields
|
||||||
|
drupal_alter('i18nsync_fields', $fields, $type);
|
||||||
|
|
||||||
|
$cache[$type] = $fields;
|
||||||
|
}
|
||||||
|
return $cache[$type];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to get list of cck fields
|
||||||
|
*/
|
||||||
|
function _i18nsync_cck_fields($type) {
|
||||||
|
if (($content = module_invoke('content', 'types', $type)) && !empty($content['fields'])) {
|
||||||
|
return $content['fields'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get node translations if any, optionally excluding this node
|
||||||
|
*
|
||||||
|
* Translations will be stored in the node itself so we have them cached
|
||||||
|
*/
|
||||||
|
function _i18nsync_node_translations($node, $exclude = FALSE) {
|
||||||
|
// Maybe translations are already here
|
||||||
|
if (!empty($node->tnid) && ($translations = translation_node_get_translations($node->tnid))) {
|
||||||
|
if ($exclude && $node->language) {
|
||||||
|
unset($translations[$node->language]);
|
||||||
|
}
|
||||||
|
return $translations;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_file_references()
|
||||||
|
*
|
||||||
|
* Inform CCK's filefield that we have other nodes using that file so it won't be deleted
|
||||||
|
*/
|
||||||
|
function i18nsync_file_references($file) {
|
||||||
|
// We have marked the field previously on nodeapi load
|
||||||
|
return !empty($file->i18nsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sample CCK field definition for Drupal 5.
|
||||||
|
'field_text' =>
|
||||||
|
array
|
||||||
|
'field_name' => string 'field_text' (length=10)
|
||||||
|
'type' => string 'text' (length=4)
|
||||||
|
'required' => string '0' (length=1)
|
||||||
|
'multiple' => string '1' (length=1)
|
||||||
|
'db_storage' => string '0' (length=1)
|
||||||
|
'text_processing' => string '0' (length=1)
|
||||||
|
'max_length' => string '' (length=0)
|
||||||
|
'allowed_values' => string '' (length=0)
|
||||||
|
'allowed_values_php' => string '' (length=0)
|
||||||
|
'widget' =>
|
||||||
|
array
|
||||||
|
...
|
||||||
|
'type_name' => string 'test' (length=4)
|
||||||
|
*/
|
181
sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.admin.inc
Normal file
181
sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.admin.inc
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Helper functions for taxonomy administration.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the callback for taxonomy translations.
|
||||||
|
*
|
||||||
|
* Gets the urls:
|
||||||
|
* admin/content/taxonomy/%taxonomy_vocabulary/translation
|
||||||
|
* admin/content/taxonomy/i18n/term/xx
|
||||||
|
* admin/content/taxonomy/i18n/term/new/xx
|
||||||
|
* admin/content/taxonomy/vid/translation/op/trid
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_page_vocabulary($vocabulary, $op = NULL, $tid = NULL) {
|
||||||
|
switch ($op) {
|
||||||
|
case 'edit':
|
||||||
|
drupal_set_title(t('Edit term translations'));
|
||||||
|
$output = drupal_get_form('i18ntaxonomy_translation_term_form', $vocabulary->vid, $tid);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$output = i18ntaxonomy_translation_overview($vocabulary->vid);
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces a vocabulary translation form.
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_translation_term_form($form_state, $vid, $trid = NULL, $edit = array()) {
|
||||||
|
$languages = i18n_supported_languages();
|
||||||
|
|
||||||
|
if ($trid == 'new') {
|
||||||
|
$translations = array();
|
||||||
|
$form['trid'] = array(
|
||||||
|
'#type' => 'hidden',
|
||||||
|
'#value' => 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$form['trid'] = array(
|
||||||
|
'#type' => 'hidden',
|
||||||
|
'#value' => $trid
|
||||||
|
);
|
||||||
|
$translations = i18ntaxonomy_term_get_translations(array('trid' => $trid));
|
||||||
|
}
|
||||||
|
$vocabulary = taxonomy_vocabulary_load($vid);
|
||||||
|
|
||||||
|
// List of terms for languages.
|
||||||
|
foreach ($languages as $lang => $langname) {
|
||||||
|
$current = isset($translations[$lang]) ? $translations[$lang]->tid : '';
|
||||||
|
$tree = i18ntaxonomy_get_tree($vid, $lang);
|
||||||
|
$form[$lang] = array('#type' => 'fieldset', '#tree' => TRUE);
|
||||||
|
$form[$lang]['tid'] = _i18ntaxonomy_term_select($langname, $current, $tree);
|
||||||
|
$form[$lang]['old'] = array(
|
||||||
|
'#type' => 'hidden',
|
||||||
|
'#value' => $current
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$form['submit'] = array(
|
||||||
|
'#type' => 'submit',
|
||||||
|
'#value' => t('Save')
|
||||||
|
);
|
||||||
|
if ($trid != 'new') {
|
||||||
|
$form['delete'] = array(
|
||||||
|
'#type' => 'submit',
|
||||||
|
'#value' => t('Delete')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$form['destination'] = array(
|
||||||
|
'#type' => 'hidden',
|
||||||
|
'#value' => 'admin/content/taxonomy/'. arg(3) .'/translation'
|
||||||
|
);
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form callback: Process vocabulary translation form.
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_translation_term_form_submit($form, &$form_state) {
|
||||||
|
switch ($form_state['values']['op']) {
|
||||||
|
case t('Save'):
|
||||||
|
i18ntaxonomy_translation_save($form_state['values'], $form_state['values']['trid']);
|
||||||
|
drupal_set_message(t('Term translations have been updated.'));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case t('Delete'):
|
||||||
|
// Delete old translations for this trid.
|
||||||
|
db_query("UPDATE {term_data} SET trid = 0 WHERE trid = %d", $form_state['values']['trid']);
|
||||||
|
drupal_set_message(t('The term translation has been deleted.'));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save taxonomy term translations.
|
||||||
|
*
|
||||||
|
* @param $terms
|
||||||
|
* Array of terms indexed by language.
|
||||||
|
* @param $trid
|
||||||
|
* Optional translation set id.
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_translation_save($terms, $trid = 0) {
|
||||||
|
// Delete old translations for this trid.
|
||||||
|
if ($trid) {
|
||||||
|
db_query("UPDATE {term_data} SET trid = 0 WHERE trid = %d", $trid);
|
||||||
|
}
|
||||||
|
// Now pick up all the tids in an array.
|
||||||
|
$translations = array();
|
||||||
|
foreach (i18n_supported_languages() as $lang => $name) {
|
||||||
|
if (isset($terms[$lang]) && ($term = (array)$terms[$lang]) && $tid = $term['tid']) {
|
||||||
|
$translations[$lang] = $tid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now set a translation set with all these terms. We need some table locking to avoid race conditions.
|
||||||
|
// when other translations created simulaneously. @TODO Find a better way.
|
||||||
|
if (count($translations)) {
|
||||||
|
db_lock_table('term_data');
|
||||||
|
$trid = (is_numeric($trid) && $trid) ? $trid : i18ntaxonomy_next_trid();
|
||||||
|
$params = array_merge(array($trid), $translations);
|
||||||
|
db_query('UPDATE {term_data} SET trid = %d WHERE tid IN('. db_placeholders($translations) .')', $params);
|
||||||
|
db_unlock_tables();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get next free trid.
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_next_trid() {
|
||||||
|
$current = (int)db_result(db_query('SELECT max(trid) FROM {term_data}'));
|
||||||
|
return $current + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a tabular listing of translations for vocabularies.
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_translation_overview($vid) {
|
||||||
|
$vocabulary = taxonomy_vocabulary_load($vid);
|
||||||
|
drupal_set_title(check_plain($vocabulary->name));
|
||||||
|
$output = '';
|
||||||
|
|
||||||
|
$languages = i18n_supported_languages();
|
||||||
|
$header = array_merge($languages, array(t('Operations')));
|
||||||
|
$links = array();
|
||||||
|
$types = array();
|
||||||
|
// Get terms/translations for this vocab.
|
||||||
|
$result = db_query('SELECT * FROM {term_data} t WHERE vid = %d', $vocabulary->vid);
|
||||||
|
$terms = $messages = array();
|
||||||
|
while ($term = db_fetch_object($result)) {
|
||||||
|
if ($term->trid && $term->language) {
|
||||||
|
$terms[$term->trid][$term->language] = $term;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Reorder data for rows and languages.
|
||||||
|
$rows = array();
|
||||||
|
foreach ($terms as $trid => $terms) {
|
||||||
|
$thisrow = array();
|
||||||
|
foreach ($languages as $lang => $name) {
|
||||||
|
if (array_key_exists($lang, $terms)) {
|
||||||
|
$thisrow[] = l($terms[$lang]->name, 'taxonomy/term/'. $terms[$lang]->tid);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$thisrow[] = '--';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$thisrow[] = l(t('edit'), "admin/content/taxonomy/$vid/translation/edit/$trid");
|
||||||
|
$rows[] = $thisrow;
|
||||||
|
}
|
||||||
|
if ($rows) {
|
||||||
|
$output .= theme('table', $header, $rows);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$messages[] = t('No translations defined for this vocabulary.');
|
||||||
|
}
|
||||||
|
$messages[]= l(t('Create new translation'), "admin/content/taxonomy/$vid/translation/edit/new");
|
||||||
|
$output .= theme('item_list', $messages);
|
||||||
|
return $output;
|
||||||
|
}
|
14
sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.info
Normal file
14
sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.info
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name = Taxonomy translation
|
||||||
|
description = Enables multilingual taxonomy.
|
||||||
|
dependencies[] = i18n
|
||||||
|
dependencies[] = taxonomy
|
||||||
|
dependencies[] = i18nstrings
|
||||||
|
package = Multilanguage
|
||||||
|
core = 6.x
|
||||||
|
|
||||||
|
; Information added by drupal.org packaging script on 2011-10-11
|
||||||
|
version = "6.x-1.10"
|
||||||
|
core = "6.x"
|
||||||
|
project = "i18n"
|
||||||
|
datestamp = "1318336004"
|
||||||
|
|
94
sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.install
Normal file
94
sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.install
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Installation file for i18ntaxonomy module.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set language field in its own table.
|
||||||
|
* Do not drop node.language now, just in case.
|
||||||
|
* TO-DO: Drop old tables, fields
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_install() {
|
||||||
|
$ret = array();
|
||||||
|
db_add_field($ret, 'vocabulary', 'language', array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => ''));
|
||||||
|
db_add_field($ret, 'term_data', 'language', array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => ''));
|
||||||
|
db_add_field($ret, 'term_data', 'trid', array('type' => 'int', 'not null' => TRUE, 'default' => 0));
|
||||||
|
|
||||||
|
// Set module weight for it to run after core modules, but before views.
|
||||||
|
db_query("UPDATE {system} SET weight = 5 WHERE name = 'i18ntaxonomy' AND type = 'module'");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_uninstall().
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_uninstall() {
|
||||||
|
$ret = array();
|
||||||
|
db_drop_field($ret, 'vocabulary', 'language');
|
||||||
|
db_drop_field($ret, 'term_data', 'language');
|
||||||
|
db_drop_field($ret, 'term_data', 'trid');
|
||||||
|
|
||||||
|
variable_del('i18ntaxonomy_vocabulary');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_schema_alter().
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_schema_alter(&$schema) {
|
||||||
|
$schema['vocabulary']['fields']['language'] = array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '');
|
||||||
|
$schema['term_data']['fields']['language'] = array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '');
|
||||||
|
$schema['term_data']['fields']['trid'] = array('type' => 'int', 'not null' => TRUE, 'default' => 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_enable().
|
||||||
|
*
|
||||||
|
* Just add strings to locale tables.
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_enable() {
|
||||||
|
drupal_load('module', 'i18nstrings');
|
||||||
|
i18ntaxonomy_locale_refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drupal 6 update.
|
||||||
|
*
|
||||||
|
* Move i18n vocabulary options to new variables.
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_update_1() {
|
||||||
|
$items = array();
|
||||||
|
$options = variable_get('i18ntaxonomy_vocabulary', array());
|
||||||
|
$translate = variable_get('i18ntaxonomy_vocabularies', array());
|
||||||
|
foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) {
|
||||||
|
if ($vocabulary->language) {
|
||||||
|
$options[$vid] = I18N_TAXONOMY_LANGUAGE;
|
||||||
|
}
|
||||||
|
elseif (isset($translate[$vid]) && $translate[$vid]) {
|
||||||
|
$options[$vid] = I18N_TAXONOMY_LOCALIZE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Search for terms with language.
|
||||||
|
$count = db_result(db_query("SELECT COUNT(language) FROM {term_data} WHERE vid = %d AND NOT language = ''", $vid));
|
||||||
|
if ($count) {
|
||||||
|
$options[$vid] = I18N_TAXONOMY_TRANSLATE;
|
||||||
|
}
|
||||||
|
elseif (!isset($options[$vid])) {
|
||||||
|
$options[$vid] = I18N_TAXONOMY_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
variable_set('i18ntaxonomy_vocabulary', $options);
|
||||||
|
drupal_set_message(t('The multilingual vocabulary settings have been updated. Please review them in the <a href="@taxonomy_admin page">taxonomy administration</a>.', array('@taxonomy_admin' => url('admin/content/taxonomy'))));
|
||||||
|
// @ TODO Update strings in localization tables.
|
||||||
|
return $items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set module weight for it to run after core modules, but before views.
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_update_6002() {
|
||||||
|
$items = array();
|
||||||
|
$items[] = update_sql("UPDATE {system} SET weight = 5 WHERE name = 'i18ntaxonomy' AND type = 'module'");
|
||||||
|
return $items;
|
||||||
|
}
|
16
sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.js
Normal file
16
sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
Drupal.behaviors.i18ntaxonomy = function (context) {
|
||||||
|
if (Drupal.settings && Drupal.settings.i18ntaxonomy_vocabulary_form) {
|
||||||
|
$('form#taxonomy-form-vocabulary input.form-radio', context).filter('[name=i18nmode]').click(function () {
|
||||||
|
var languageSelect = $('form#taxonomy-form-vocabulary select#edit-language', context);
|
||||||
|
if ($(this).val() == Drupal.settings.i18ntaxonomy_vocabulary_form.I18N_TAXONOMY_LANGUAGE) {
|
||||||
|
// Make sure language form is enabled when I18N_TAXONOMY_LANGUAGE is clicked
|
||||||
|
languageSelect.removeAttr("disabled");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Make sure language form is disabled otherwise and set to blank.
|
||||||
|
languageSelect.val("");
|
||||||
|
languageSelect.attr("disabled", "disabled");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
1106
sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.module
Normal file
1106
sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.module
Normal file
File diff suppressed because it is too large
Load diff
150
sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.pages.inc
Normal file
150
sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.pages.inc
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Page callbacks for the taxonomy module, i18n remake.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu callback; displays all nodes associated with a term.
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_term_page($str_tids = '', $depth = 0, $op = 'page') {
|
||||||
|
$terms = taxonomy_terms_parse_string($str_tids);
|
||||||
|
if ($terms['operator'] != 'and' && $terms['operator'] != 'or') {
|
||||||
|
drupal_not_found();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($terms['tids']) {
|
||||||
|
// We need vid here, too.
|
||||||
|
$result = db_query(db_rewrite_sql('SELECT t.tid, t.vid, t.name FROM {term_data} t WHERE t.tid IN ('. db_placeholders($terms['tids']) .')', 't', 'tid'), $terms['tids']);
|
||||||
|
$tids = array(); // we rebuild the $tids-array so it only contains terms the user has access to.
|
||||||
|
$names = array();
|
||||||
|
while ($term = db_fetch_object($result)) {
|
||||||
|
$tids[] = $term->tid;
|
||||||
|
$names[] = i18ntaxonomy_translate_term_name($term);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($names) {
|
||||||
|
$title = check_plain(implode(', ', $names));
|
||||||
|
drupal_set_title($title);
|
||||||
|
|
||||||
|
switch ($op) {
|
||||||
|
case 'page':
|
||||||
|
// Build breadcrumb based on first hierarchy of first term:
|
||||||
|
$current->tid = $tids[0];
|
||||||
|
$breadcrumb = array();
|
||||||
|
while ($parents = taxonomy_get_parents($current->tid)) {
|
||||||
|
$current = array_shift($parents);
|
||||||
|
$breadcrumb[] = l($current->name, 'taxonomy/term/'. $current->tid);
|
||||||
|
}
|
||||||
|
$breadcrumb[] = l(t('Home'), NULL);
|
||||||
|
$breadcrumb = array_reverse($breadcrumb);
|
||||||
|
drupal_set_breadcrumb($breadcrumb);
|
||||||
|
|
||||||
|
$output = theme('i18ntaxonomy_term_page', $tids, taxonomy_select_nodes($tids, $terms['operator'], $depth, TRUE));
|
||||||
|
drupal_add_feed(url('taxonomy/term/'. $str_tids .'/'. $depth .'/feed'), 'RSS - '. $title);
|
||||||
|
return $output;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'feed':
|
||||||
|
$channel['link'] = url('taxonomy/term/'. $str_tids .'/'. $depth, array('absolute' => TRUE));
|
||||||
|
$channel['title'] = variable_get('site_name', 'Drupal') .' - '. $title;
|
||||||
|
|
||||||
|
// Only display the description if we have a single term, to avoid clutter and confusion.
|
||||||
|
if (count($tids) == 1) {
|
||||||
|
$terms = array(taxonomy_get_term($tids[0]));
|
||||||
|
$terms = i18ntaxonomy_localize_terms($terms, array('description'));
|
||||||
|
$terms['operator'] = 'or';
|
||||||
|
// HTML will be removed from feed description, so no need to filter here.
|
||||||
|
$channel['description'] = $terms[0]->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = taxonomy_select_nodes($tids, $terms['operator'], $depth, FALSE);
|
||||||
|
$items = array();
|
||||||
|
while ($row = db_fetch_object($result)) {
|
||||||
|
$items[] = $row->nid;
|
||||||
|
}
|
||||||
|
|
||||||
|
node_feed($items, $channel);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
drupal_not_found();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
drupal_not_found();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a taxonomy term page HTML output.
|
||||||
|
*
|
||||||
|
* @param $tids
|
||||||
|
* An array of term ids.
|
||||||
|
* @param $result
|
||||||
|
* A pager_query() result, such as that performed by taxonomy_select_nodes().
|
||||||
|
*
|
||||||
|
* @ingroup themeable
|
||||||
|
*/
|
||||||
|
function theme_i18ntaxonomy_term_page($tids, $result) {
|
||||||
|
drupal_add_css(drupal_get_path('module', 'taxonomy') .'/taxonomy.css');
|
||||||
|
|
||||||
|
$output = '';
|
||||||
|
|
||||||
|
// Only display the description if we have a single term, to avoid clutter and confusion.
|
||||||
|
if (count($tids) == 1) {
|
||||||
|
$term = taxonomy_get_term($tids[0]);
|
||||||
|
if (i18ntaxonomy_vocabulary($term->vid) == I18N_TAXONOMY_LOCALIZE) {
|
||||||
|
$description = i18nstrings("taxonomy:term:$term->tid:description", $term->description);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$description = $term->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that a description is set.
|
||||||
|
if (!empty($description)) {
|
||||||
|
$output .= '<div class="taxonomy-term-description">';
|
||||||
|
$output .= filter_xss_admin($description);
|
||||||
|
$output .= '</div>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$output .= taxonomy_render_nodes($result);
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for autocompletion.
|
||||||
|
*
|
||||||
|
* @ TODO Optimized localization. We cannot just i18nstrings() huge lists of terms.
|
||||||
|
*/
|
||||||
|
function i18ntaxonomy_autocomplete($vid, $string = '') {
|
||||||
|
// The user enters a comma-separated list of tags. We only autocomplete the last tag.
|
||||||
|
$array = drupal_explode_tags($string);
|
||||||
|
|
||||||
|
// Is this vocabulary localizable?
|
||||||
|
$localizable = i18ntaxonomy_vocabulary($vid) == I18N_TAXONOMY_LOCALIZE;
|
||||||
|
|
||||||
|
// Fetch last tag.
|
||||||
|
$last_string = trim(array_pop($array));
|
||||||
|
$matches = array();
|
||||||
|
if ($last_string != '') {
|
||||||
|
$result = db_query_range(db_rewrite_sql("SELECT t.tid, t.name FROM {term_data} t WHERE t.vid = %d AND LOWER(t.name) LIKE LOWER('%%%s%%')", 't', 'tid'), $vid, $last_string, 0, 10);
|
||||||
|
|
||||||
|
$prefix = count($array) ? implode(', ', $array) .', ' : '';
|
||||||
|
|
||||||
|
while ($tag = db_fetch_object($result)) {
|
||||||
|
$n = $tag->name;
|
||||||
|
// Commas and quotes in terms are special cases, so encode 'em.
|
||||||
|
if (strpos($tag->name, ',') !== FALSE || strpos($tag->name, '"') !== FALSE) {
|
||||||
|
$n = '"'. str_replace('"', '""', $tag->name) .'"';
|
||||||
|
}
|
||||||
|
$matches[$prefix . $n] = check_plain($tag->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drupal_json($matches);
|
||||||
|
}
|
293
sites/all/modules/i18n/tests/drupal_i18n_test_case.php
Normal file
293
sites/all/modules/i18n/tests/drupal_i18n_test_case.php
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
<?php
|
||||||
|
// Core: Id: drupal_web_test_case.php,v 1.96 2009/04/22 09:57:10 dries Exp
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Provide required modifications to Drupal 7 core DrupalWebTestCase in order
|
||||||
|
* for it to function properly in Drupal 6.
|
||||||
|
*
|
||||||
|
* Copyright 2008-2009 by Jimmy Berry ("boombatower", http://drupal.org/user/214218)
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once drupal_get_path('module', 'simpletest') . '/drupal_web_test_case.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case for typical Drupal tests.
|
||||||
|
*/
|
||||||
|
class Drupali18nTestCase extends DrupalWebTestCase {
|
||||||
|
// We may want to install the site in a different language
|
||||||
|
var $install_locale = 'en';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a language
|
||||||
|
*
|
||||||
|
* @param $langcode
|
||||||
|
* @param $default
|
||||||
|
* Whether this is the default language
|
||||||
|
* @param $load
|
||||||
|
* Whether to load available translations for that language
|
||||||
|
*/
|
||||||
|
function addLanguage($langcode, $default = FALSE, $load = TRUE) {
|
||||||
|
require_once './includes/locale.inc';
|
||||||
|
// Enable installation language as default site language.
|
||||||
|
locale_add_language($langcode, NULL, NULL, NULL, NULL, NULL, 1, $default);
|
||||||
|
// Reset language list
|
||||||
|
language_list('language', TRUE);
|
||||||
|
// We may need to refresh default language
|
||||||
|
drupal_init_language();
|
||||||
|
// @todo import po files for this language
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create translation set from a node
|
||||||
|
*
|
||||||
|
* @param $source
|
||||||
|
* Source node
|
||||||
|
* @param $languages
|
||||||
|
* Optional list of language codes
|
||||||
|
*/
|
||||||
|
function drupalCreateTranslations(&$source, $languages = NULL) {
|
||||||
|
$languages = $languages ? $languages : array_keys(language_list());
|
||||||
|
if (empty($source->tnid)) {
|
||||||
|
db_query("UPDATE {node} SET tnid = %d, translate = %d WHERE nid = %d", $source->nid, 0, $source->nid);
|
||||||
|
$source->tnid = $source->nid;
|
||||||
|
}
|
||||||
|
$translations[$source->language] = $source;
|
||||||
|
foreach ($languages as $lang) {
|
||||||
|
if ($lang != $source->language) {
|
||||||
|
$translations[$lang] = $this->drupalCreateNode(array('type' => $source->type, 'language' => $lang, 'translation_source' => $source, 'status' => $source->status, 'promote' => $source->promote, 'uid' => $source->uid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable language switcher block
|
||||||
|
*/
|
||||||
|
function enableBlock($module, $delta, $region = 'left') {
|
||||||
|
db_query("UPDATE {blocks} SET status = %d, weight = %d, region = '%s', throttle = %d WHERE module = '%s' AND delta = '%s' AND theme = '%s'", 1, 0, $region, 0, $module, $delta, variable_get('theme_default', 'garland'));
|
||||||
|
cache_clear_all();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Reset theme to default so we can play with blocks
|
||||||
|
*/
|
||||||
|
function initTheme() {
|
||||||
|
global $theme, $theme_key;
|
||||||
|
unset($theme);
|
||||||
|
unset($theme_key);
|
||||||
|
init_theme();
|
||||||
|
_block_rehash();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Generates a random database prefix, runs the install scripts on the
|
||||||
|
* prefixed database and enable the specified modules. After installation
|
||||||
|
* many caches are flushed and the internal browser is setup so that the
|
||||||
|
* page requests will run on the new prefix. A temporary files directory
|
||||||
|
* is created with the same name as the database prefix.
|
||||||
|
*
|
||||||
|
* @param ...
|
||||||
|
* List of modules to enable for the duration of the test.
|
||||||
|
*/
|
||||||
|
function setUp() {
|
||||||
|
global $db_prefix, $user, $language; // $language (Drupal 6).
|
||||||
|
global $install_locale;
|
||||||
|
|
||||||
|
// Store necessary current values before switching to prefixed database.
|
||||||
|
$this->db_prefix_original = $db_prefix;
|
||||||
|
$clean_url_original = variable_get('clean_url', 0);
|
||||||
|
|
||||||
|
// Generate temporary prefixed database to ensure that tests have a clean starting point.
|
||||||
|
$db_prefix = 'simpletest' . mt_rand(1000, 1000000);
|
||||||
|
|
||||||
|
include_once './includes/install.inc';
|
||||||
|
|
||||||
|
drupal_install_system();
|
||||||
|
|
||||||
|
// Add the specified modules to the list of modules in the default profile.
|
||||||
|
$args = func_get_args();
|
||||||
|
// Add language and basic i18n modules
|
||||||
|
$install_locale = $this->install_locale;
|
||||||
|
$i18n_modules = array('locale', 'translation', 'i18n', 'i18n_test');
|
||||||
|
$modules = array_unique(array_merge(drupal_verify_profile('default', $this->install_locale), $args, $i18n_modules));
|
||||||
|
drupal_install_modules($modules);
|
||||||
|
|
||||||
|
// Install locale
|
||||||
|
if ($this->install_locale != 'en') {
|
||||||
|
$this->addLanguage($this->install_locale, TRUE);
|
||||||
|
}
|
||||||
|
// Run default profile tasks.
|
||||||
|
$task = 'profile';
|
||||||
|
default_profile_tasks($task, '');
|
||||||
|
|
||||||
|
// Rebuild caches.
|
||||||
|
actions_synchronize();
|
||||||
|
_drupal_flush_css_js();
|
||||||
|
$this->refreshVariables();
|
||||||
|
$this->checkPermissions(array(), TRUE);
|
||||||
|
user_access(NULL, NULL, TRUE); // Drupal 6.
|
||||||
|
|
||||||
|
// Log in with a clean $user.
|
||||||
|
$this->originalUser = $user;
|
||||||
|
// drupal_save_session(FALSE);
|
||||||
|
// $user = user_load(1);
|
||||||
|
session_save_session(FALSE);
|
||||||
|
$user = user_load(array('uid' => 1));
|
||||||
|
|
||||||
|
// Restore necessary variables.
|
||||||
|
variable_set('install_profile', 'default');
|
||||||
|
variable_set('install_task', 'profile-finished');
|
||||||
|
variable_set('clean_url', $clean_url_original);
|
||||||
|
variable_set('site_mail', 'simpletest@example.com');
|
||||||
|
|
||||||
|
// Use temporary files directory with the same prefix as database.
|
||||||
|
$this->originalFileDirectory = file_directory_path();
|
||||||
|
variable_set('file_directory_path', file_directory_path() . '/' . $db_prefix);
|
||||||
|
$directory = file_directory_path();
|
||||||
|
// Create the files directory.
|
||||||
|
file_check_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
|
||||||
|
|
||||||
|
set_time_limit($this->timeLimit);
|
||||||
|
|
||||||
|
// Some more includes
|
||||||
|
require_once 'includes/language.inc';
|
||||||
|
// Refresh theme
|
||||||
|
$this->initTheme();
|
||||||
|
// Set path languages so we can retrieve pages in different languages
|
||||||
|
variable_set('language_negotiation', LANGUAGE_NEGOTIATION_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete created files and temporary files directory, delete the tables created by setUp(),
|
||||||
|
* and reset the database prefix.
|
||||||
|
*/
|
||||||
|
protected function tearDown() {
|
||||||
|
parent::tearDown();
|
||||||
|
|
||||||
|
// Reset language list
|
||||||
|
language_list('language', TRUE);
|
||||||
|
drupal_init_language();
|
||||||
|
if (module_exists('locale')) {
|
||||||
|
locale(NULL, NULL, TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a Drupal path or an absolute path with language
|
||||||
|
*/
|
||||||
|
protected function i18nGet($langcode, $path = '', array $options = array(), array $headers = array()) {
|
||||||
|
$options += array('language' => $this->getLanguage($langcode));
|
||||||
|
return $this->drupalGet($path, $options, $headers);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get language object for langcode
|
||||||
|
*/
|
||||||
|
public function getLanguage($langcode) {
|
||||||
|
if (is_object($langcode)) {
|
||||||
|
return $langcode;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$language_list = language_list();
|
||||||
|
return $language_list[$langcode];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Switch global language
|
||||||
|
*/
|
||||||
|
public function switchLanguage($newlang = NULL) {
|
||||||
|
$newlang = $newlang ? $newlang : $this->install_locale;
|
||||||
|
$GLOBALS['language'] = $this->getLanguage($newlang);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all languages that are not default
|
||||||
|
*/
|
||||||
|
public function getOtherLanguages() {
|
||||||
|
$languages = language_list();
|
||||||
|
unset($languages[language_default('language')]);
|
||||||
|
return $languages;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create and store one translation into the db
|
||||||
|
*/
|
||||||
|
public function i18nstringsCreateTranslation($name, $lang, $length = 20) {
|
||||||
|
$translation = $this->randomName($length, "i18n-$lang-");
|
||||||
|
$count = self::i18nstringsSaveTranslation($name, $lang, $translation);
|
||||||
|
$this->assertTrue($count, "A translation($lang) has been created for string $name");
|
||||||
|
return $translation;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Translate one string into the db
|
||||||
|
*/
|
||||||
|
public static function i18nstringsSaveTranslation($name, $lang, $translation, $update = FALSE) {
|
||||||
|
$source = i18nstrings_get_source($name);
|
||||||
|
if ($source) {
|
||||||
|
if ($update) {
|
||||||
|
db_query("UPDATE {locales_target} SET translation = '%s' WHERE lid = %d AND language = '%s'", $translation, $source->lid, $lang);
|
||||||
|
} else {
|
||||||
|
db_query("INSERT INTO {locales_target} (translation, lid, language) VALUES ('%s', %d, '%s')", $translation, $source->lid, $lang);
|
||||||
|
}
|
||||||
|
return db_affected_rows();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Print out a variable for debugging
|
||||||
|
*/
|
||||||
|
function printDebug($data, $title = '') {
|
||||||
|
$string = is_array($data) || is_object($data) ? print_r($data, TRUE) : $data;
|
||||||
|
$output = $title ? $title . ':' . $string : $string;
|
||||||
|
//$this->assertTrue(TRUE, $output);
|
||||||
|
$this->assertTrue(TRUE, $output, 'Debug');
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Debug dump object with some formatting
|
||||||
|
*/
|
||||||
|
function printObject($object, $title = 'Object') {
|
||||||
|
$output = $this->formatTable($object);
|
||||||
|
$this->printDebug($output, $title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print out current HTML page
|
||||||
|
*/
|
||||||
|
function printPage() {
|
||||||
|
$this->printDebug($this->drupalGetContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump table contents
|
||||||
|
function dumpTable($table) {
|
||||||
|
$result = db_query('SELECT * FROM {' . $table . '}');
|
||||||
|
$output = 'Table dump <strong>' . $table . '</strong>:';
|
||||||
|
while ($row = db_fetch_array($result)) {
|
||||||
|
$rows[] = $row;
|
||||||
|
if (empty($header)) {
|
||||||
|
$header = array_keys($row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($rows)) {
|
||||||
|
$output .= theme('table', $header, $rows);
|
||||||
|
} else {
|
||||||
|
$output .= ' No rows';
|
||||||
|
}
|
||||||
|
$this->assertTrue(TRUE, $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format object as table, recursive
|
||||||
|
*/
|
||||||
|
function formatTable($object) {
|
||||||
|
foreach ($object as $key => $value) {
|
||||||
|
$rows[] = array(
|
||||||
|
$key,
|
||||||
|
is_array($value) || is_object($value) ? $this->formatTable($value) : $value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!empty($rows)) {
|
||||||
|
return theme('table', array(), $rows);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 'No properties';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
84
sites/all/modules/i18n/tests/i18n_api.test
Normal file
84
sites/all/modules/i18n/tests/i18n_api.test
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Class for testing messaging module.
|
||||||
|
*
|
||||||
|
* Tests basic API functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once 'drupal_i18n_test_case.php';
|
||||||
|
|
||||||
|
class i18n_API_Tests extends Drupali18nTestCase {
|
||||||
|
|
||||||
|
function getInfo() {
|
||||||
|
return array(
|
||||||
|
'name' => 'Internationalization API',
|
||||||
|
'group' => 'Internationalization',
|
||||||
|
'description' => 'Internationalization API functions'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUp() {
|
||||||
|
parent::setUp('i18n', 'locale');
|
||||||
|
$this->addLanguage('es');
|
||||||
|
$this->addLanguage('de');
|
||||||
|
// A language with two letter code may help too
|
||||||
|
$this->addLanguage('pt-br');
|
||||||
|
|
||||||
|
//variable_set('i18n_variables', array('site_name','site_frontpage',));
|
||||||
|
|
||||||
|
// Log in user with access content permission
|
||||||
|
$user = $this->drupalCreateUser(array('access comments', 'access content'));
|
||||||
|
$this->drupalLogin($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBasicAPI() {
|
||||||
|
$language_count = count(language_list());
|
||||||
|
$this->assertTrue($language_count > 1, 'Multiple languages created: ' . $language_count);
|
||||||
|
$this->assertEqual(i18n_get_lang(), 'en', 'Default language (en) properly set.');
|
||||||
|
|
||||||
|
// Set site name for each language and check pages later
|
||||||
|
foreach (language_list() as $lang) {
|
||||||
|
i18n_variable_set('site_name', "Drupal-$lang->name", $lang->language);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable language switcher block
|
||||||
|
//$this->enableBlock('locale', 0);
|
||||||
|
//$this->dumpTable('blocks');
|
||||||
|
|
||||||
|
// Create some content and check selection modes
|
||||||
|
variable_set('language_content_type_story', 1);
|
||||||
|
$neutral = $this->drupalCreateNode(array('type' => 'story', 'promote' => 1));
|
||||||
|
$source = $this->drupalCreateNode(array('type' => 'story', 'promote' => 1, 'language' => i18n_default_language()));
|
||||||
|
$translations = $this->drupalCreateTranslations($source);
|
||||||
|
// This fails because the _get_translations() function has static caching
|
||||||
|
//$this->assertEqual(count(translation_node_get_translations($source->tnid)), $language_count, "Created $language_count $source->type translations.");
|
||||||
|
$this->assertEqual(count($translations), $language_count, "Created $language_count $source->type translations.");
|
||||||
|
|
||||||
|
// Default selection module, only language neutral and current
|
||||||
|
variable_set('i18n_selection_mode', 'simple');
|
||||||
|
foreach (language_list() as $lang) {
|
||||||
|
$this->drupalGet('', array('language' => $lang));
|
||||||
|
$this->assertText("Drupal-$lang->name", 'Checked translated site name: Drupal-' . $lang->name);
|
||||||
|
$display = array($translations[$lang->language], $neutral);
|
||||||
|
$hide = $translations;
|
||||||
|
unset($hide[$lang->language]);
|
||||||
|
$this->assertContent($display, $hide);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check some nodes are displayed, some are not
|
||||||
|
*/
|
||||||
|
function assertContent($display, $hide = array()) {
|
||||||
|
$languages = language_list();
|
||||||
|
foreach ($display as $node) {
|
||||||
|
$name = $node->language ? $languages[$node->language]->name : 'Language neutral';
|
||||||
|
$this->assertText($node->title, 'Content displayed for ' . $name);
|
||||||
|
}
|
||||||
|
foreach ($hide as $node) {
|
||||||
|
$name = $node->language ? $languages[$node->language]->name : 'Language neutral';
|
||||||
|
$this->assertNoText($node->title, 'Content not displayed for ' . $name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
160
sites/all/modules/i18n/tests/i18n_blocks.test
Normal file
160
sites/all/modules/i18n/tests/i18n_blocks.test
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Class for testing messaging module.
|
||||||
|
*
|
||||||
|
* Tests basic API functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once 'i18n_strings.test';
|
||||||
|
|
||||||
|
class i18n_Blocks_Test extends Drupali18nTestCase {
|
||||||
|
|
||||||
|
function getInfo() {
|
||||||
|
return array(
|
||||||
|
'name' => 'Block translation',
|
||||||
|
'group' => 'Internationalization',
|
||||||
|
'description' => 'Block translation functions'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUp() {
|
||||||
|
parent::setUp('118n', 'locale', 'i18nstrings', 'i18nblocks');
|
||||||
|
$this->addLanguage('es');
|
||||||
|
$this->addLanguage('de');
|
||||||
|
// Create and login user
|
||||||
|
$admin_user = $this->drupalCreateUser(array('administer blocks'));
|
||||||
|
$this->drupalLogin($admin_user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBlockTranslation() {
|
||||||
|
// Create a translatable block
|
||||||
|
$box = $this->i18nCreateBox(array('language' => I18N_BLOCK_LOCALIZE));
|
||||||
|
$i18nblock = i18nblocks_load('block', $box->bid);
|
||||||
|
$this->assertTrue($i18nblock->ibid && $i18nblock->language == I18N_BLOCK_LOCALIZE, "The block has been created with the right i18n settings.");
|
||||||
|
// Create translations for title and body, source strings should be already there
|
||||||
|
$translations = $this->i18nTranslateBlock('block', $box-bid, array('title', 'body'));
|
||||||
|
// Now set a language for the block and confirm it shows just for that one (without translation)
|
||||||
|
$languages = $this->getOtherLanguages();
|
||||||
|
$setlanguage = array_shift($languages);
|
||||||
|
$otherlanguage = array_shift($languages);
|
||||||
|
$this->i18nUpdateBlock('block', $box->bid, array('language' => $setlanguage->language));
|
||||||
|
// Do not show in default language
|
||||||
|
$this->drupalGet('');
|
||||||
|
$this->assertNoText($box->title);
|
||||||
|
// Show in block's language but not translated
|
||||||
|
$this->i18nGet($setlanguage);
|
||||||
|
$this->assertText($box->title);
|
||||||
|
// Do not show in the other language
|
||||||
|
$this->i18nGet($otherlanguage);
|
||||||
|
$this->assertNoText($box->title);
|
||||||
|
$this->assertNoText($translations[$otherlanguage->language]['title']);
|
||||||
|
|
||||||
|
// Add a custom title to any other block: Navigation (user, 1)
|
||||||
|
$title = $this->randomName(10);
|
||||||
|
$this->i18nUpdateBlock('user', 1, array('title' => $title));
|
||||||
|
$this->assertText($title, "The new custom title is displayed on the home page.");
|
||||||
|
$translate = $this->i18nTranslateBlock('user', 1, array('title'));
|
||||||
|
$this->drupalGet('');
|
||||||
|
|
||||||
|
// Refresh block strings, the ones for the first box should be gone. Not the others
|
||||||
|
$box2 = $this->i18nCreateBox(array('language' => I18N_BLOCK_LOCALIZE));
|
||||||
|
$translations = $this->i18nTranslateBlock('block', $box2->bid, array('title', 'body'));
|
||||||
|
i18nstrings_refresh_group('blocks', TRUE);
|
||||||
|
$this->assertFalse(i18nstrings_get_source("blocks:block:$box->bid:title", $box->title), "The string for the box title is gone.");
|
||||||
|
$this->assertFalse(i18nstrings_get_source("blocks:block:$box->bid:body", $box->body), "The string for the box body is gone.");
|
||||||
|
$this->assertTrue(i18nstrings_get_source("blocks:user:1:title"), "We have a string for the Navigation block title");
|
||||||
|
$this->assertTrue(i18nstrings_get_source("blocks:block:$box2->bid:title", $box2->title), "The string for the second box title is still there.");
|
||||||
|
$this->assertTrue(i18nstrings_get_source("blocks:block:$box2->bid:body", $box2->body), "The string for the second box body is still there.");
|
||||||
|
// Test a block with filtering and input formats
|
||||||
|
$box3 = $this->i18nCreateBox(array(
|
||||||
|
'title' => '<div><script>alert(0)</script>Title</script>',
|
||||||
|
'body' => "One line\nTwo lines<script>alert(1)</script>",
|
||||||
|
'language' => I18N_BLOCK_LOCALIZE,
|
||||||
|
));
|
||||||
|
$language = current($this->getOtherLanguages());
|
||||||
|
// We add language name to the title just to make sure we get the right translation later
|
||||||
|
$this->i18nstringsSaveTranslation("blocks:block:$box3->bid:title", $language->language, $box3->title . $language->name);
|
||||||
|
$this->i18nstringsSaveTranslation("blocks:block:$box3->bid:body", $language->language, $box3->body);
|
||||||
|
// This should be the actual HTML displayed
|
||||||
|
$title = check_plain($box3->title);
|
||||||
|
$body = check_markup($box3->body);
|
||||||
|
$this->drupalGet('');
|
||||||
|
$this->assertRaw($title, "Title being displayed for default language: " . $title);
|
||||||
|
$this->assertRaw($body, "Body being displayed for default language: " . check_plain($body));
|
||||||
|
$this->i18nGet($language);
|
||||||
|
$this->assertRaw($title . $language->name, "Translated title displayed with right filtering.");
|
||||||
|
$this->assertRaw($body, "Translated body displayed with right filtering.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate block fields to all languages
|
||||||
|
*/
|
||||||
|
function i18nTranslateBlock($module, $delta, $fields = array('title', 'body')) {
|
||||||
|
foreach ($this->getOtherLanguages() as $language) {
|
||||||
|
foreach ($fields as $key) {
|
||||||
|
$text[$key] = $this->i18nstringsCreateTranslation("blocks:$module:$delta:$key", $language->language);
|
||||||
|
}
|
||||||
|
// Now check translated strings display on page
|
||||||
|
$this->i18nGet($language->language, '');
|
||||||
|
foreach ($text as $string) {
|
||||||
|
$this->assertText($string);
|
||||||
|
}
|
||||||
|
$translations[$language->language] = $text;
|
||||||
|
}
|
||||||
|
return $translations;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Test creating custom block (i.e. box), moving it to a specific region and then deleting it.
|
||||||
|
*/
|
||||||
|
function i18nCreateBox($box = array(), $region = 'left', $check_display = TRUE) {
|
||||||
|
// Add a new box by filling out the input form on the admin/build/block/add page.
|
||||||
|
$box += array(
|
||||||
|
'info' => $this->randomName(8),
|
||||||
|
'title' => $this->randomName(8),
|
||||||
|
'body' => $this->randomName(32),
|
||||||
|
);
|
||||||
|
$this->drupalPost('admin/build/block/add', $box, t('Save block'));
|
||||||
|
// Confirm that the box has been created, and then query the created bid.
|
||||||
|
$this->assertText(t('The block has been created.'), 'Box successfully created.');
|
||||||
|
$bid = db_result(db_query("SELECT bid FROM {boxes} WHERE info = '%s'", array($box['info'])));
|
||||||
|
// Check to see if the box was created by checking that it's in the database..
|
||||||
|
$this->assertNotNull($bid, 'Box found in database');
|
||||||
|
// Display the block on left region
|
||||||
|
$this->i18nUpdateBlockRegion('block', $bid, $region);
|
||||||
|
if ($check_display) {
|
||||||
|
// Confirm that the box is being displayed.
|
||||||
|
$this->assertText(check_plain($box['title']), 'Box successfully being displayed on the page.');
|
||||||
|
}
|
||||||
|
$box['bid'] = $block['delta'] = $bid;
|
||||||
|
$box['module'] = 'block';
|
||||||
|
return (object)$box;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Update block
|
||||||
|
*/
|
||||||
|
function i18nUpdateBlock($module, $delta, $update = array()) {
|
||||||
|
$this->drupalPost("admin/build/block/configure/$module/$delta", $update, t('Save block'));
|
||||||
|
$this->assertText(t('The block configuration has been saved.'));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Update block region
|
||||||
|
*/
|
||||||
|
function i18nUpdateBlockRegion($module, $delta, $region) {
|
||||||
|
// Set the created box to a specific region.
|
||||||
|
// TODO: Implement full region checking.
|
||||||
|
$edit = array();
|
||||||
|
$edit[$module . '_'. $delta .'[region]'] = $region;
|
||||||
|
$this->drupalPost('admin/build/block', $edit, t('Save blocks'));
|
||||||
|
// Confirm that the box was moved to the proper region.
|
||||||
|
$this->assertText(t('The block settings have been updated.'), "Box successfully moved to $region region.");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Delete block
|
||||||
|
*/
|
||||||
|
function i18nDeleteBlock($bid) {
|
||||||
|
// Delete the created box & verify that it's been deleted and no longer appearing on the page.
|
||||||
|
$this->drupalPost('admin/build/block/delete/'. $bid, array(), t('Delete'));
|
||||||
|
$this->assertRaw(t('The block %title has been removed.', array('%title' => $box['info'])), t('Box successfully deleted.'));
|
||||||
|
$this->assertNoText(t($box['title']), t('Box no longer appears on page.'));
|
||||||
|
}
|
||||||
|
}
|
102
sites/all/modules/i18n/tests/i18n_strings.test
Normal file
102
sites/all/modules/i18n/tests/i18n_strings.test
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Class for testing i18nstrings and modules using these features
|
||||||
|
*
|
||||||
|
* Tests basic API functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once 'drupal_i18n_test_case.php';
|
||||||
|
|
||||||
|
class i18n_Strings_Test extends Drupali18nTestCase {
|
||||||
|
|
||||||
|
function getInfo() {
|
||||||
|
return array(
|
||||||
|
'name' => 'String translation API',
|
||||||
|
'group' => 'Internationalization',
|
||||||
|
'description' => 'User defined strings translation functions'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUp() {
|
||||||
|
parent::setUp('i18n', 'locale', 'i18nstrings');
|
||||||
|
$this->addLanguage('es');
|
||||||
|
$this->addLanguage('de');
|
||||||
|
// A language with two letter code may help too
|
||||||
|
$this->addLanguage('pt-br');
|
||||||
|
// Set path languages so we can retrieve pages in different languages
|
||||||
|
variable_set('language_negotiation', LANGUAGE_NEGOTIATION_PATH);
|
||||||
|
//variable_set('i18n_variables', array('site_name','site_frontpage',));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test base i18nstrings API
|
||||||
|
*/
|
||||||
|
function testStringsAPI() {
|
||||||
|
// Create a bunch of strings for three languages
|
||||||
|
$strings = $this->stringCreateAll(10);
|
||||||
|
|
||||||
|
// Save source strings and store translations
|
||||||
|
foreach ($strings['en'] as $key => $string) {
|
||||||
|
$name = "test:string:$key:name";
|
||||||
|
i18nstrings_update($name, $string);
|
||||||
|
$count = $this->stringSaveTranslation($name, 'es', $strings['es'][$key]);
|
||||||
|
$count += $this->stringSaveTranslation($name, 'pt-br', $strings['pt-br'][$key]);
|
||||||
|
$this->assertEqual($count, 2, "Two translatins have been saved");
|
||||||
|
}
|
||||||
|
// Check translations
|
||||||
|
$language_list = language_list();
|
||||||
|
foreach (array('pt-br', 'es') as $lang) {
|
||||||
|
$language = $language_list[$lang];
|
||||||
|
foreach ($strings[$lang] as $key => $value) {
|
||||||
|
$name = "test:string:$key:name";
|
||||||
|
$translation = i18nstrings($name, 'NOT FOUND', $lang);
|
||||||
|
$this->assertEqual($translation, $value, "The right $language->language translation has been retrieved for $name, $translation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create strings for all languages
|
||||||
|
*/
|
||||||
|
public static function stringCreateAll($number = 10, $length = 100) {
|
||||||
|
foreach (language_list() as $lang => $language) {
|
||||||
|
$strings[$lang] = self::stringCreateArray($number, $length);
|
||||||
|
}
|
||||||
|
return $strings;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create a bunch of random strings to test the API
|
||||||
|
*/
|
||||||
|
public static function stringCreateArray($number = 10, $length = 100) {
|
||||||
|
for ($i=1 ; $i <= $number ; $i++) {
|
||||||
|
$strings[$i] = self::randomName($length);
|
||||||
|
}
|
||||||
|
return $strings;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Create and store one translation into the db
|
||||||
|
*/
|
||||||
|
public static function stringCreateTranslation($name, $lang, $length = 20) {
|
||||||
|
$translation = $this->randomName($length);
|
||||||
|
if (self::stringSaveTranslation($name, $lang, $translation)) {
|
||||||
|
return $translation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Translate one string into the db
|
||||||
|
*/
|
||||||
|
public static function stringSaveTranslation($name, $lang, $translation, $update = FALSE) {
|
||||||
|
$source = i18nstrings_get_source($name);
|
||||||
|
if ($source) {
|
||||||
|
if ($update) {
|
||||||
|
db_query("UPDATE {locales_target} SET translation = '%s' WHERE lid = %d AND language = '%s'", $translation, $source->lid, $lang);
|
||||||
|
} else {
|
||||||
|
db_query("INSERT INTO {locales_target} (translation, lid, language) VALUES ('%s', %d, '%s')", $translation, $source->lid, $lang);
|
||||||
|
}
|
||||||
|
return db_affected_rows();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
sites/all/modules/i18n/tests/i18n_taxonomy.test
Normal file
58
sites/all/modules/i18n/tests/i18n_taxonomy.test
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Class for testing messaging module.
|
||||||
|
*
|
||||||
|
* Tests basic API functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once 'i18n_strings.test';
|
||||||
|
|
||||||
|
class i18n_Taxonomy_Test extends Drupali18nTestCase {
|
||||||
|
|
||||||
|
function getInfo() {
|
||||||
|
return array(
|
||||||
|
'name' => 'Taxonomy translation',
|
||||||
|
'group' => 'Internationalization',
|
||||||
|
'description' => 'Taxonomy translation functions'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUp() {
|
||||||
|
parent::setUp('i18nstrings', 'taxonomy', 'i18ntaxonomy');
|
||||||
|
$this->addLanguage('es');
|
||||||
|
$this->addLanguage('de');
|
||||||
|
}
|
||||||
|
|
||||||
|
function testTaxonomyTranslationAPI() {
|
||||||
|
// Create a vocabulary with some terms
|
||||||
|
$number = 4;
|
||||||
|
$vocab = $this->drupalCreateVocabulary(array('i18nmode' => I18N_TAXONOMY_LOCALIZE));
|
||||||
|
$this->assertEqual(i18ntaxonomy_vocabulary($vocab-vid), I18N_TAXONOMY_LOCALIZE, 'A vocabulary has been created and it is localizable.');
|
||||||
|
$terms = $this->drupalCreateTerms($number, array('vid' => $vocab->vid));
|
||||||
|
$this->assertEqual(count($terms), $number, "Four translatable terms have been created.");
|
||||||
|
// Create and Save Spanish translation for all of them
|
||||||
|
$count = 0;
|
||||||
|
$lang = 'es';
|
||||||
|
foreach ($terms as $term) {
|
||||||
|
$translations[$term->tid] = $this->randomName(10);
|
||||||
|
// Save Spanish translation
|
||||||
|
$translations[$term->tid] = $this->i18nstringsCreateTranslation("taxonomy:term:$term->tid:name", $lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create vocabulary with given fields
|
||||||
|
function drupalCreateVocabulary($vocab = array()) {
|
||||||
|
$vocab += array('name' => $this->randomName(10), 'description' => $this->randomName(20));
|
||||||
|
taxonomy_save_vocabulary($vocab);
|
||||||
|
return (object)$vocab;
|
||||||
|
}
|
||||||
|
// Create term with given fields
|
||||||
|
function drupalCreateTerms($number = 1, $data = array()) {
|
||||||
|
$list = array();
|
||||||
|
for ($i = 1; $i <= $number ; $i++ ) {
|
||||||
|
$term = $data + array('name' => $this->randomName(10), 'description' => $this->randomName(20));
|
||||||
|
taxonomy_save_term($term);
|
||||||
|
$list[] = (object)$term;
|
||||||
|
}
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
}
|
14
sites/all/modules/i18n/tests/i18n_test.info
Normal file
14
sites/all/modules/i18n/tests/i18n_test.info
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name = Internationalization tests
|
||||||
|
description = Helper module for testing i18n (do not enable manually)
|
||||||
|
dependencies[] = locale
|
||||||
|
dependencies[] = translation
|
||||||
|
dependencies[] = i18n
|
||||||
|
package = Testing
|
||||||
|
core = 6.x
|
||||||
|
|
||||||
|
; Information added by drupal.org packaging script on 2011-10-11
|
||||||
|
version = "6.x-1.10"
|
||||||
|
core = "6.x"
|
||||||
|
project = "i18n"
|
||||||
|
datestamp = "1318336004"
|
||||||
|
|
48
sites/all/modules/i18n/tests/i18n_test.module
Normal file
48
sites/all/modules/i18n/tests/i18n_test.module
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* Helper module for testing i18n
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Add some multilingual variables, override existing ones from settings so
|
||||||
|
// we have a known list and we don't need any addition to the settings file for testing i18n
|
||||||
|
_i18n_test_variable_init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_init()
|
||||||
|
*/
|
||||||
|
function i18n_test_init() {
|
||||||
|
// We just implement this hook so this one is loaded always on bootstap
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set default multilingual variables and add any others defined by testing scripts
|
||||||
|
*
|
||||||
|
* More variables can be added using 'i18n_variables_test';
|
||||||
|
*/
|
||||||
|
function _i18n_test_variable_init() {
|
||||||
|
global $conf;
|
||||||
|
$conf['i18n_variables'] = array_merge(array('site_name', 'site_frontpage'), variable_get('i18n_variables_test', array()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of hook_locale()
|
||||||
|
*/
|
||||||
|
function i18n_test_locale() {
|
||||||
|
switch ($op) {
|
||||||
|
case 'groups':
|
||||||
|
return array('test' => t('Test'));
|
||||||
|
case 'info':
|
||||||
|
$info['test']['refresh callback'] = 'i18n_test_locale_refresh';
|
||||||
|
$info['test']['format'] = FALSE;
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locale refresh
|
||||||
|
*/
|
||||||
|
function i18n_test_locale_refresh() {
|
||||||
|
return TRUE;
|
||||||
|
}
|
Reference in a new issue