I would like to try to implement the translation of profile2 (http://drupal.org/project/profile2) entities.

I explored the code a bit but would like to know if some of the maitainers could point me to a good starting point (doc, sample code, tutorial, whatever).

Any information would be useful.

Comments

Countzero’s picture

As of now, I'm just trying to make the profile2 type appear on admin/config/regional/entity_translation.

I implemented this :

function profile2_translation_info($types = NULL) {
  $info['profile2'] = array(
    'translation' => array(
      'translation' => array(
        'class' => 'EntityTranslationProfile2Handler',
        'base path' => 'profile2/%profile2',
        'access callback' => 'translation_profile2_tab_access',
        'access arguments' => array(1),
        'edit form' => TRUE,
      ),
    ),
  );
  return $info;
}

... in profile2.module (translation_profile2_tab_access returns TRUE) and this :

class EntityTranslationProfile2Handler extends EntityTranslationDefaultHandler {

  public function __construct($entity_type, $entity_info, $entity, $entity_id) {
    parent::__construct('profile2', $entity_info, $entity, $entity_id);
  }

//  public function isRevision() {
//    return !empty($this->entity->revision);
//  }

  public function getLanguage() {
    return $this->entity->language;
  }

//  public function getAccess($op) {
//    return node_access($op, $this->entity);
//  }

  protected function getStatus() {
    return (boolean) $this->entity->status;
  }
}

... which I know won't be functional but just I'm just starting.

Nothing happened on the page yet, so if anybody can point me to what I'm missing ...

Countzero’s picture

Well, in fact it was already there and I feel really stupid.

So I guess it's a matter of providing the correct info to ET to give the correct paths to edit the translations.

Anyway, any info about this is also welcome.

plach’s picture

Yes, hook_translation_info (which I am planning to deprecate in favor of a plain hook_entity_info implementation, btw) can be used to override information for the UI paths, translation handler classes and so on. The defaults should work in most cases, the most important info is perhaps the base path which actually enables the translate tab.

Just keep in mind that the implementation in #1 is wrong, the correct array structure is:

<?php
function profile2_translation_info($types = NULL) {
  $info['profile2'] = array(
    'translation' => array(
      'entity_translation' => array(  // This the field translation handler, see @http://api.drupal.org/api/drupal/modules--field--field.multilingual.inc/function/field_has_translation_handler/7.
        'class' => 'EntityTranslationProfile2Handler',  // This is needed only if you actually need to override some behavior of the base class.
        'base path' => 'profile2/%profile2',  // This is not needed, it's the default value.
        'access callback' => 'translation_profile2_tab_access',  // I guess this callback is not existing, the default one should suffice for most cases, node is the only tricky one.
        'access arguments' => array(1),
        'edit form' => TRUE,  // This is not needed, it's the default value.
      ),
    ),
  );
  return $info;
}
?>
Countzero’s picture

Thanks a lot for this answer.

The paths are managed in a very special way in profile2 and I guess that's why I have such a hard time implementing at least a basic working thing.

In profile2, the actual entity is never accessible directly, but through the user pages, so finding the right menu path is not easy.

Am I right thinking the end path should be /profile2/pid/translate, just like nodes ?

Also, I'm not sure if all the node code should be implemented or if things are supposed to work as is.

plach’s picture

We already have an open issue for the problem of attaching the translate tab to complex base paths: #1092332: Support arbitrary base paths, perhaps it is the moment to address this issue in a more solid way.

Countzero’s picture

OK, my ego feels better ;-).

I'll have a look at this issue after I have finished to implement my ugly temporary hack for my use case.

Countzero’s picture

Did some tests : as soon as I define a base path (even with only one wildcard) in profile2_translation_info, I cannot access profile information in user/xxx/edit/main anymore : the form displays empty values.

Countzero’s picture

As of now, providing any base path leads to problems, even without any wildcard.

Countzero’s picture

Another note : the access callback seems mandotary, otherwise it throws a notice saying

Notice : Undefined index: access callback in entity_translation_menu() (ligne 171

plach’s picture

Countzero’s picture

I don't think so. The faulty line is this one :

        'access arguments' => array_merge(array($entity_type, $entity_position, $language_position, $item['access callback']), $item['access arguments']),

It's simply a direct reference to an inexisting index so nothing to write home about. I don't define the access callback, ET tells me so, period.

I went a bit further : I set a fake path like this :
'base path' => 'user/7/edit/main/7'

and tried to access user/7/edit/main/7/translate.

This error came out :

Fatal error: Call to a member function label() on a non-object in /srv/sites/web59/web/sites/all/modules/entity/entity.module on line 724

Trying to follow the track up to the faulty call.

Countzero’s picture

Backtracing, I came to

  $handler = entity_translation_get_handler($entity_type, $entity);
  // Initialize translations if they are empty.
  $translations = $handler->getTranslations();

  if (empty($translations->original)) {
    $handler->initTranslations();
    $handler->saveTranslations();
  }

... in entity_translation.admin.inc where $translations seems to be empty even after the 'if' statement.

plach’s picture

Please, don't post every step. Create an issue when you find a standalone bug or post a summary when you come up with a sensible result, after all this is a feature request not a collector of sparse bug reports.

Countzero’s picture

I thought giving as much feedback as possible could be useful. But well ...

plach’s picture

Title: Profile2 : trouble creating the translate tabs » Profile2
Category: bug » feature

The problem here is we are going OT.

If you have problems with attaching the translate tab to a base path, you should file a separate bug report summarizing the issue (unless we want to turn this one into a bug report). Then if you mean to fix the bug just post your solution along with a description. A series of debugging steps does not help too much, and distract me (I get an email notification for each post :)

Countzero’s picture

Title: Profile2 » Profile2 : trouble creating the translate tabs
Category: feature » bug

I turn this into a bug report, as I don't need the feature so much now.

I was trying to help on good intent. I thought the steps could be useful for you to debug your module. I understand they're not so I won't do it again.

plach’s picture

No bad feelings I hope: any help is welcome if well focused :)

Countzero’s picture

Title: Profile2 » Profile2 : trouble creating the translate tabs
Category: feature » bug

No bad feelings : just a little overreaction after a hard day of work.

See you.

Countzero’s picture

Back to business, but not with very much success.

One thing (among many others) I don't understand, and I hope you'll agree to help me about :

This code :

      $items["$path/translate"] = array(
        'title' => 'Translate',
        'page callback' => 'entity_translation_overview',
        'page arguments' => array($entity_type, $entity_position),
        'type' => MENU_LOCAL_TASK,
        'weight' => 2,
      ) + $item;

... passes two arguments from the URL to the entity_translation_overview function.

This function then treats the second argument, and actually succeeds, as an entity object.

I cannot understand how a numeric argument can become a class instance between the menu entry and the actual function code. Is there some Drupal magic I'm not aware of going on here ?

It works for node entities but all my tries with profile entities lead up to errors because the numeric argument stays so and never becomes a class which the entity_translation_overview can handle.

Maybe I just need some rest ...

plach’s picture

It is the standard way to define menu items: the 'page arguments' array specifies in which position of the menu path the entity object can be found. For instance in 'node/%node/translate' you find the entity object (actually the menu loader wildcard) in the second position (index 1), $entity_position basically just holds the computed value assuming that there will be at some point a menu loader wildcard which the /translate segment needs to be appeded to:

'node/%node/translate'
'taxonomy/term/%term/translate'
etc.

Obviously this scheme is not flexible enough to cover each use case.

Countzero’s picture

Thanks for your answer. I thought I had understood this system, but obviously I don't.

So the value holded by the wildcard must be passed to a loader function isn't it ? Because the URL just holds an id, not an object. I saw there is a function called entity_translation_menu_entity_load but it doesn't seem to get called at any time.

plach’s picture

Well, autoloaders appearing in the base paths are supposed to be provided by the entity modules theirselves, for instance %node maps to node_load(), this way entity_translation_overview() gets a node instead of a nid.

Countzero’s picture

Okay, now that I have a better understanding of the process, I managed to provide an arbitrary path and got the translation form loaded with a profile2 entity. The loader indeed exists in profile2 code. The menu callback has to be implemented like that :

function profile2_menu() {
  $items['arbitrarypath/%profile2'] = array(
    'title' => 'Profile translation',
    'description' => 'Provide arbitrary base path for profile2 entity translations',
    'page callback' => 'entity_translation_overview',
    'page arguments' => array('profile2', 1),
    'access arguments' => array(TRUE),
    'file path' => drupal_get_path('module', 'entity_translation'),
    'file' => 'entity_translation.admin.inc',
   );

Of course, it doesn't fit with profile2 architecture and only fulfills testing purposes but I think it can be useful to some.

With a usable base path, all the ET stuff works like a charm : you can add translatable fields, add translations to them and see the translated version appear on the other languages pages.

So the problem problem boils down, as you said before, to providing a real path, consistent with profile2 paths, which is not possible at the time being due to the aforementioned issue : #1092332: Support arbitrary base paths.

I think the ET part of the present issue is, if not solved, reduced to its root elements, so maybe you can close this but it's of course up to you.

Thanks for your patience.

plach’s picture

Title: Profile2 : trouble creating the translate tabs » Profile2: how to make the translate tabs appear
Category: bug » support
Status: Active » Fixed

Just a minor remark, the following definition should be more or less equivalent:

<?php
function profile2_translation_info($types = NULL) {
  $info['profile2'] = array(
    'translation' => array(
      'entity_translation' => array(
        'base path' => 'arbitrarypath/%profile2',
      ),
    ),
  );
  return $info;
}
?>
Countzero’s picture

My, you're right. I was really off big time on this one. Will try to do better next time.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

kurageart’s picture

Component: Code » Base system

Hi... do you have any suggestion on how to make this work on core drupal profiles (with attached fields)? thanks