Im saving the temporary solution that worked for me after i have chosen to use WPML and Oxygenbuilder together for a web for a client. At the end of project came the time to do translations and i was quite sad of the state of things.

WPML officially won’t support the oxygenbuilder and advises against mainly „CatchAll“ templates. But you can actually set the ct_template as translatable, translate the templates via interface and it seems like its working. Its not – users that are not logged in will see different templates/data language mutations.

Also im NOT going to assign the templates manually to every post. Sorry but thats just not feasible. So i went the other way and it seems to work quite well.

Problem

I think that the problem lies in wpmls „wpml_object_id“ function. That thing is really inconsistent and while it SHOULD return the ID of translation, it returns the original ID. You can probably guess what that does on frontend. You can try it out by using this:

$translationLanguage = 'en';
if (ICL_LANGUAGE_CODE == $translationLanguage) {
    $wpmlId = apply_filters( 'wpml_object_id', $template->ID, 'post_ct_template', false, $translationLanguage);
    $translationId = null;
    if ($wpmlId){
        $translationId = $wpmlId;
    }
    die(json_encode([$template->ID,$translationId]));
}

And you will probably find out that no matter if you have „ct_template“ setup like „translate“ or „act as if translated“ in WPML – you will get back the original ID instead of NULL as was expected, in either way you wont get the translated ID and thats the real problem.

Solution

oxygen>component-framework>includes>templates.php

replace all occurencies of „return $template;“ with „$template = my_translate_template($template); return $template;“ and then in your own code define the function

function my_translate_template($template){
    if (ICL_LANGUAGE_CODE=='en') {
        switch ($template->ID){
            case 1029: $template = get_post(4383); break; // my_ctp single template
            case 4260: $template = get_post(4382); break; // my_ctp list template
            default: break;
        }
    }
    return $template;
}

What this dumb piece of code actually does is what is wpml actually supposed to, but doesnt. It replaces the original ID with the translation. Why dumb? Because you need to do it in code and probably in UI as well. And you have to modify Oxygenbuilder core files to apply it.