WarmUpTill / SceneSwitcher

An automation tool for OBS Studio
https://obsproject.com/forum/resources/automatic-scene-switching.395/
GNU General Public License v2.0
984 stars 78 forks source link

i18n support ? #62

Closed shugen002 closed 3 years ago

shugen002 commented 4 years ago

This plugin looks well , and i want to share this to my friend . But i notice this plugin doesn't provide Chinese language display. And I try to find a language config file in this project but it seems all text were hard code in file .

I am willing to provide Chinese Language translate for this project.

WarmUpTill commented 4 years ago

That is a good suggestion!

The plugin currently does not support the language configuration files - so you didn't miss anything. I might look into adding this option on the weekend.

And thank you very much for offering to translate!

shugen002 commented 4 years ago

image

I try to complie this project myself and I get C2664 error at https://github.com/WarmUpTill/SceneSwitcher/blob/master/src/win/advanced-scene-switcher-win.cpp#L129 https://github.com/WarmUpTill/SceneSwitcher/blob/master/src/win/advanced-scene-switcher-win.cpp#L208 https://github.com/WarmUpTill/SceneSwitcher/blob/master/src/win/advanced-scene-switcher-win.cpp#L235 Is this my environment problem?

WarmUpTill commented 4 years ago

I personally have not encountered this problem yet, but I assume it is related to using non-unicode characters. I see that your system if falling back to using the Ansi functions (e.g. FindWindowExA() instead of the FindWindowExW()) so the conversion between Ansi and Unicode strings probably causes these issues.

shugen002 commented 4 years ago

ok, I solve this problem by change the charset to unicode. but i got a new problem image

I am using the official build of obs.dll . Maybe I should clone the project into obs-studio source code build like in the workflow ?

shugen002 commented 4 years ago

It looks like simply do like in the workflow is the easiest way to build , if you have a working obs-studio build environment. Maybe we should write this tip into readme? image

WarmUpTill commented 4 years ago

I am glad that you got it to work! The linker error you faced before seems to caused by using a .dll instead of a .lib file.

Compiling the plugin in-tree is also my preferred way doing things as (for me at least) it is much easier to set up.

Usually if one has the needed header files and libs laying around it is very likely to be a complete obs-studio build environment anyways, so adding a comment that compiling in-tree should be the preferred method is probably a good idea. (I will add it to the todo for the weekend)

shugen002 commented 4 years ago

I try to add i18n support myself and you can check at https://github.com/shugen002/SceneSwitcher I just simply change the window title and the name display in tool menu . In Simplified Chinese 简体中文 , you will see like this: image image In English , you will see as original.

I don 't know whether you accpet this change . if you feel well to these change , I will continue work with it.

WarmUpTill commented 4 years ago

I don 't know whether you accpet this change . if you feel well to these change , I will continue work with it.

Yes, that is exactly what I had in mind - so feel free to continue. I really appreciate it! 👍

shugen002 commented 4 years ago

https://github.com/WarmUpTill/SceneSwitcher/blob/45a880d8f8c497c456e8a23fb1dc2f8406df7706/src/general.cpp#L388 This kind of using tabText after add i18n support may cause strange issue.

https://github.com/WarmUpTill/SceneSwitcher/blob/45a880d8f8c497c456e8a23fb1dc2f8406df7706/forms/advanced-scene-switcher.ui#L43

Can we use this name instead of tabText and which property i should use ? (I am new to Qt )

WarmUpTill commented 4 years ago

Hm, good point. I haven't thought of that.

I assume the object name could be used to access this value. https://doc.qt.io/qt-5/qobject.html#objectName-prop

Unfortunately I will to be able to test this before the weekend.

As a short term workaround the reordering of the tabs could just be disabled temporarily here: https://github.com/WarmUpTill/SceneSwitcher/blob/45a880d8f8c497c456e8a23fb1dc2f8406df7706/src/advanced-scene-switcher.cpp#L49

shugen002 commented 4 years ago

I try with this code

blog(LOG_INFO, "The tab %s is %s", tabName.toUtf8().constData(),bar->objectName().toUtf8().constData());

what i got:

log ```log [adv-ss] The tab General is qt_tabwidget_tabbar [adv-ss] The tab Transition is qt_tabwidget_tabbar [adv-ss] The tab Transition is qt_tabwidget_tabbar [adv-ss] The tab Pause is qt_tabwidget_tabbar [adv-ss] The tab Pause is qt_tabwidget_tabbar [adv-ss] The tab Pause is qt_tabwidget_tabbar [adv-ss] The tab Title is qt_tabwidget_tabbar [adv-ss] The tab Title is qt_tabwidget_tabbar [adv-ss] The tab Title is qt_tabwidget_tabbar [adv-ss] The tab Title is qt_tabwidget_tabbar [adv-ss] The tab Executable is qt_tabwidget_tabbar [adv-ss] The tab Executable is qt_tabwidget_tabbar [adv-ss] The tab Executable is qt_tabwidget_tabbar [adv-ss] The tab Executable is qt_tabwidget_tabbar [adv-ss] The tab Executable is qt_tabwidget_tabbar [adv-ss] The tab Region is qt_tabwidget_tabbar [adv-ss] The tab Region is qt_tabwidget_tabbar [adv-ss] The tab Region is qt_tabwidget_tabbar [adv-ss] The tab Region is qt_tabwidget_tabbar [adv-ss] The tab Region is qt_tabwidget_tabbar [adv-ss] The tab Region is qt_tabwidget_tabbar [adv-ss] The tab Media is qt_tabwidget_tabbar [adv-ss] The tab Media is qt_tabwidget_tabbar [adv-ss] The tab Media is qt_tabwidget_tabbar [adv-ss] The tab Media is qt_tabwidget_tabbar [adv-ss] The tab Media is qt_tabwidget_tabbar [adv-ss] The tab Media is qt_tabwidget_tabbar [adv-ss] The tab Media is qt_tabwidget_tabbar [adv-ss] The tab File is qt_tabwidget_tabbar [adv-ss] The tab File is qt_tabwidget_tabbar [adv-ss] The tab File is qt_tabwidget_tabbar [adv-ss] The tab File is qt_tabwidget_tabbar [adv-ss] The tab File is qt_tabwidget_tabbar [adv-ss] The tab File is qt_tabwidget_tabbar [adv-ss] The tab File is qt_tabwidget_tabbar [adv-ss] The tab File is qt_tabwidget_tabbar [adv-ss] The tab Random is qt_tabwidget_tabbar [adv-ss] The tab Random is qt_tabwidget_tabbar [adv-ss] The tab Random is qt_tabwidget_tabbar [adv-ss] The tab Random is qt_tabwidget_tabbar [adv-ss] The tab Random is qt_tabwidget_tabbar [adv-ss] The tab Random is qt_tabwidget_tabbar [adv-ss] The tab Random is qt_tabwidget_tabbar [adv-ss] The tab Random is qt_tabwidget_tabbar [adv-ss] The tab Random is qt_tabwidget_tabbar [adv-ss] The tab Time is qt_tabwidget_tabbar [adv-ss] The tab Time is qt_tabwidget_tabbar [adv-ss] The tab Time is qt_tabwidget_tabbar [adv-ss] The tab Time is qt_tabwidget_tabbar [adv-ss] The tab Time is qt_tabwidget_tabbar [adv-ss] The tab Time is qt_tabwidget_tabbar [adv-ss] The tab Time is qt_tabwidget_tabbar [adv-ss] The tab Time is qt_tabwidget_tabbar [adv-ss] The tab Time is qt_tabwidget_tabbar [adv-ss] The tab Time is qt_tabwidget_tabbar [adv-ss] The tab Idle is qt_tabwidget_tabbar [adv-ss] The tab Idle is qt_tabwidget_tabbar [adv-ss] The tab Idle is qt_tabwidget_tabbar [adv-ss] The tab Idle is qt_tabwidget_tabbar [adv-ss] The tab Idle is qt_tabwidget_tabbar [adv-ss] The tab Idle is qt_tabwidget_tabbar [adv-ss] The tab Idle is qt_tabwidget_tabbar [adv-ss] The tab Idle is qt_tabwidget_tabbar [adv-ss] The tab Idle is qt_tabwidget_tabbar [adv-ss] The tab Idle is qt_tabwidget_tabbar [adv-ss] The tab Idle is qt_tabwidget_tabbar [adv-ss] The tab Sequence is qt_tabwidget_tabbar [adv-ss] The tab Sequence is qt_tabwidget_tabbar [adv-ss] The tab Sequence is qt_tabwidget_tabbar [adv-ss] The tab Sequence is qt_tabwidget_tabbar [adv-ss] The tab Sequence is qt_tabwidget_tabbar [adv-ss] The tab Sequence is qt_tabwidget_tabbar [adv-ss] The tab Sequence is qt_tabwidget_tabbar [adv-ss] The tab Sequence is qt_tabwidget_tabbar [adv-ss] The tab Sequence is qt_tabwidget_tabbar [adv-ss] The tab Sequence is qt_tabwidget_tabbar [adv-ss] The tab Sequence is qt_tabwidget_tabbar [adv-ss] The tab Sequence is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar [adv-ss] The tab Audio is qt_tabwidget_tabbar ```

Hide for wrong way and too long useless logs

shugen002 commented 4 years ago

Ok , I solve this problem with this line

    at = tabWidget->indexOf(tabWidget->findChild<QWidget *>(tabName));

should I make a single PR for this change ?

By the way , can i rename some tab and what should i pay attention when i rename ?

Current Name New Name
titleTab windowTitleTab
IdleTab idleTab
WarmUpTill commented 4 years ago

Yes, if that works as expected feel free to create a PR.

And changing the names to be more consistent is a good idea, also. Thank you for the suggestion!

WarmUpTill commented 3 years ago

Just wanted to check back with you if there is anything I can support you with in this regard. If so please let me know! :)

shugen002 commented 3 years ago

Just wanted to check back with you if there is anything I can support you with in this regard. If so please let me know! :)

Yes , I am thinking how to request support from you in English . I almost adapt for i18n support and most of translation . Now I need to update the github action workflow file to include the locale file in artifacts. I can do the windows and ubuntu part , but I don 't know any about mac . So , can I request you to do this part ? ( I currently work on i18n branch in my repo. )

WarmUpTill commented 3 years ago

Sure no problem :) You can find the changes I would suggest here https://github.com/WarmUpTill/SceneSwitcher/pull/69.

Unfortunately I do not have a setup to test the macOS version of the plugin either but the result seems correct.

shugen002 commented 3 years ago

Is possible to change layout/template for different language . Different language have different grammar , it mean simple string concat may not suitable for all language and it may look strange for some people. Some strange may tolerable, but some are very strange. Such as :

"Switch to {0} using {1} if {2} contains: {3}"
"如果 {2} 包含:{3} 使用转场特效 {1} 切换到场景 {0}"

{0}: Scene Name {1}: Transition Name {2}: File Name {3}: text

If possible , replace item not only can be string ,but also selector such as Scene Selector.

Now , because of the existence of #71 , you may have write access to my branch . Feel free to direct commit to my branch.

WarmUpTill commented 3 years ago

Does this problem only affect the switch entry widgets or other elements of the UI also?

If only the former is affected then maybe using obs_get_locale() to check which locale is currently active and then conditionally adjusting the layout would be an option, as the layout for these widgets is created at runtime anyways.

So for example here such a check could be added for the scene sequence: https://github.com/WarmUpTill/SceneSwitcher/blob/22a90c7d645571e61d638f5796c69350f3ebffea/src/switch-sequence.cpp#L357-L365

But that solution could of course get very messy over time if more languages are added.

shugen002 commented 3 years ago

can we do something like this ?

  1. what we need to prepare locale template

    "Switch to {0} balabala"

    the QML of the item we need to insert

    <item>
    <widget class="QLineEdit" name="sth" />
    </item>
  2. generate QML replace {0} with

      </string>
    </property>
    </widget>
    </item>

    +

<item>
  <widget class="QLineEdit" name="sth" />
</item>

+

<item>
  <widget class="QLabel">
    <property>
      <string>

then add head

<item>
  <widget class="QLabel">
    <property>
      <string>

and foot

      </string>
    </property>
  </widget>
</item>
  1. send to QUiLoader
  2. add to UI
shugen002 commented 3 years ago

Does this problem only affect the switch entry widgets or other elements of the UI also?

other elements also effected , but most of them can be tolorate or ignore

WarmUpTill commented 3 years ago

can we do something like this ?

1. what we need to prepare
   locale template
"Switch to {0} balabala"

the QML of the item we need to insert

<item>
  <widget class="QLineEdit" name="sth" />
</item>
1. generate QML
   replace `{0}` with
      </string>
    </property>
  </widget>
</item>

+

<item>
  <widget class="QLineEdit" name="sth" />
</item>

+

<item>
  <widget class="QLabel">
    <property>
      <string>

then add head

<item>
  <widget class="QLabel">
    <property>
      <string>

and foot

      </string>
    </property>
  </widget>
</item>
1. send to QUiLoader

2. add to UI

Would this not be more complicated then just creating the layout yourself like it is done for the switch widgets? What would the advantage of using the QML be?

But I get what you are saying. Each locale should define the order of the labels and widgets used.

Could we not just encode this information in the i18n locale file?

So for example somewhere in the translations file we add something like this:

...
; Audio Tab
AdvSceneSwitcher.audioTab.title="Audio"
AdvSceneSwitcher.audioTab.whenLabel="When the volume of"
AdvSceneSwitcher.audioTab.aboveLabel="is above"
AdvSceneSwitcher.audioTab.switchLabel="switch to"
AdvSceneSwitcher.audioTab.usingLabel="using"
; new addition below
AdvSceneSwitcher.audioTab.widgetOrder="whenLabel;audioSources;aboveLabel;volumeWidget;switchLabel;scenes;usingLabel;transitions"

...

Then at runtime one could get this information by using ... std::string widgetOrder = obs_module_text("AdvSceneSwitcher.audioTab.widgetOrder"); ... and insert the widgets and labels in the desired order.

What do you think?

shugen002 commented 3 years ago

I use QML just simply because it looks like HTML or XML for me.

And in translation I prefer replace

; Audio Tab
AdvSceneSwitcher.audioTab.title="Audio"
AdvSceneSwitcher.audioTab.whenLabel="When the volume of"
AdvSceneSwitcher.audioTab.aboveLabel="is above"
AdvSceneSwitcher.audioTab.switchLabel="switch to"
AdvSceneSwitcher.audioTab.usingLabel="using"

with

; Audio Tab
AdvSceneSwitcher.audioTab.title="Audio"
AdvSceneSwitcher.audioTab.entry="When the volume of {{audioSources}} is above {{volumeWidget}} switch to {{scenes}} using {{transitions}}"

. So that the translation would be like an sentences instead of sparate part , and should be easy for other translator to comprehend .

WarmUpTill commented 3 years ago

Ah, I understand now - thank you for the example! This would indeed make the translation of the plugin a lot simpler as the whole sentence can be used.

I am fine with doing it this way but I cannot really judge how much work it would be to implement this.

WarmUpTill commented 3 years ago

What do you think about the following approach?

std::string getNextDelim(std::string text,
             std::unordered_map<std::string, QWidget*> placeholders)
{
    size_t pos = std::string::npos;
    std::string res = "";

    for (const auto &ph : placeholders) {
        size_t newPos = text.find(ph.first);
        if (newPos <= pos) {
            pos = newPos;
            res = ph.first;
        }
    }

    if (pos == std::string::npos)
        return "";

    return res;
}

void addToLayout(std::string text, QBoxLayout *layout,
         std::unordered_map<std::string, QWidget*> placeholders)
{
    std::vector<std::pair<std::string, QWidget*>> labelsWidgetsPairs;

    std::string delim = getNextDelim(text, placeholders);
    while (delim != "") {
        size_t pos = text.find(delim);
        if (pos != std::string::npos) {
            labelsWidgetsPairs.emplace_back(text.substr(0, pos),
                            placeholders[delim]);
            text.erase(0, pos + delim.length());
        }
        delim = getNextDelim(text, placeholders);
    }

    if (text != "") {
        labelsWidgetsPairs.emplace_back(text, nullptr);
    }

    for (auto &lw : labelsWidgetsPairs) {
        if (lw.first != "")
            layout->addWidget(new QLabel(lw.first.c_str()));
        if (lw.second)
            layout->addWidget(lw.second);
    }
    layout->addStretch();
}
AudioSwitchWidget::AudioSwitchWidget(AudioSwitch *s) : SwitchWidget(s)
{
...
    std::unordered_map<std::string, QWidget*> exampleMap;
    mymap = {{"{{audioSources}}", audioSources},
         {"{{volumeWidget}}", audioVolumeThreshold},
         {"{{scenes}}", scenes},
         {"{{transitions}}", transitions}};
    addToLayout(
        "When the volume of {{audioSources}} is above {{volumeWidget}} switch to {{scenes}} using {{transitions}} blub",
        switchLayout, exampleMap);
...
}

Feel free to make suggestions regarding the implementation / naming - its just the first thing that came to mind so it might not be any good :)

shugen002 commented 3 years ago

Look good to me. After you commit this, i will try to do the same at other tab.

WarmUpTill commented 3 years ago

Just wanted to let you know that I committed the changes for the other tabs as well.

shugen002 commented 3 years ago

Do you have any idea about this code ?

https://github.com/WarmUpTill/SceneSwitcher/blob/ad31561baa89daccd0471b86c74d77069a88b706/src/switch-file.cpp#L490-L504

WarmUpTill commented 3 years ago

Ah good point - I forgot about that one. I think I will redesign it so it will also support the placeWidgets() functionality.

WarmUpTill commented 3 years ago

Rework is committed now. (Sorry I got a bit carried away and added some changes unrelated to translation)

You should now be able to use ...

AdvSceneSwitcher.fileTab.entry="Switch to {{scenes}} using {{transitions}} if content of {{fileType}} {{filePath}} {{browseButton}} matches:"
AdvSceneSwitcher.fileTab.entry2="{{matchText}}"
AdvSceneSwitcher.fileTab.entry3="{{useRegex}} {{checkModificationDate}} {{checkFileContent}}"
shugen002 commented 3 years ago

image

the input field for network url is is hard to be notice ,

maybe add some placeholder text is better.

WarmUpTill commented 3 years ago

Thanks for the hint. I added a border to make it more visible.

Edit: Nevermind, I prefer the placeholder text approach.

WarmUpTill commented 3 years ago

if I don't add any commit to this branch in 3 days, you can merge it.

Just to confirm once more - you are finished with everything, right? If so I will merge the changes tomorrow.

Thanks again for your support in translating the plugin! :)