FlineDev / BartyCrouch

Localization/I18n: Incrementally update/translate your Strings files from .swift, .h, .m(m), .storyboard or .xib files.
MIT License
1.36k stars 121 forks source link

No Translation attempted: No source language files found. No input files found #193

Closed frankrei closed 4 years ago

frankrei commented 4 years ago

I have a fairly non-standard setup for a macOS project with multiple targets.

My config file is "vanilla":

[update]
tasks = ["interfaces", "code", "transform", "normalize", "translate"]

[update.interfaces]
paths = ["."]
defaultToBase = false
ignoreEmptyStrings = false
unstripped = false

[update.code]
codePaths = ["."]
localizablePaths = ["."]
defaultToKeys = false
additive = true
unstripped = false
plistArguments = true

[update.transform]
codePaths = ["."]
localizablePaths = ["."]
transformer = "foundation"
supportedLanguageEnumPath = "."
typeName = "BartyCrouch"
translateMethodName = "translate"

[update.normalize]
paths = ["."]
sourceLocale = "en"
harmonizeWithSource = true
sortByKeys = true

[update.translate]
paths = "."
secret = "--removed for posting but valid--"
sourceLocale = "en"

[lint]
paths = ["."]
duplicateKeys = true
emptyValues = true

running the update task outputs:

2020-07-16 17:27:55.151: ✅  ./Base.lproj/RemoveGPSData.xib:  Successfully updated strings file(s) of Storyboard or XIB file.
Task 'Update Interfaces' took 4.585 seconds.
Starting Task 'Update Code' ...
2020-07-16 17:27:55.697: ℹ️  Incrementally updated keys of file '/Users/frankreiff/Dropbox/git/abfa4/Resources/FMT5/de.lproj/Localizable.strings'.
2020-07-16 17:27:55.710: ℹ️  Incrementally updated keys of file '/Users/frankreiff/Dropbox/git/abfa4/Resources/Distribution/de.lproj/Localizable.strings'.
2020-07-16 17:27:55.721: ℹ️  Incrementally updated keys of file '/Users/frankreiff/Dropbox/git/abfa4/Resources/Distribution/fr.lproj/Localizable.strings'.
2020-07-16 17:27:55.735: ℹ️  Incrementally updated keys of file '/Users/frankreiff/Dropbox/git/abfa4/Resources/FDC5/de.lproj/Localizable.strings'.
2020-07-16 17:27:55.751: ℹ️  Incrementally updated keys of file '/Users/frankreiff/Dropbox/git/abfa4/Resources/PDC/de.lproj/Localizable.strings'.
2020-07-16 17:27:55.772: ℹ️  Incrementally updated keys of file '/Users/frankreiff/Dropbox/git/abfa4/Code/App/Base.lproj/Localizable.strings'.
2020-07-16 17:27:55.773: ✅  .:  Successfully updated strings file(s) of Code files.
Task 'Update Code' took 0.622 seconds.
Starting Task 'Code Transform' ...
Task 'Code Transform' took 0.255 seconds.
Starting Task 'Normalize' ...
2020-07-16 17:27:56.059: ⚠️  No source language files found.
Starting Task 'Translate' ...
2020-07-16 17:27:56.090: ⚠️  No input files found.

Indicating that the Localizable.strings files are found just fine, but then the 'Normalize' and 'Translate' tasks don't seem to be finding anything.

The ‘/Users/frankreiff/Dropbox/git/abfa4/Resources/Distribution/fr.lproj/Localizable.strings‘ file has hundreds of empty translations. All the other xib and .strings files are all already localized in French and German.

I'm sure I'm missing something.. just not sure what :-)

Any help would be appreciated.

Jeehut commented 4 years ago

@frankrei Thank you for reporting this issue. Please note that it's not recommended to use the "vanilla" configuration path. As stated in the README, it is a good idea to provide any path containing config options. Another question: I don't see any en.lproj folder, but you have en configured as your source language. Do you even have English localization strings?

frankrei commented 4 years ago

Hi,

I have played around with all the options ad-naseum before opening the ticket. While totally understandable, the lack of documentation/ samples meant that I wasn't really certain what is supposed to go into the paths variables: relative paths up to/or including the .lproj, with or without a / at the end, or an absolute path? I also played the defaultToBase and defaultToKeys flags.

In the end though, Bartycrouch finds all the string files with just the vanially config so I left that.

I do not have en.lproj. It is my understanding (possibly totally wrong, but certainly been working for years) that if you use a "base internationalization", you do not create string files for the development language and this role is taken over by the actual strings in the xib & Localizable.strings files.

In this particular case I had about a hundred pairs like:

/* status message */
" (%d items). " = "";

in the Localizable.strings file and I expected Bartycrouch to pick up the fact that the entries are untranslated and translate them.

I tried setting the defaultToBase and the defaultToKeys options, but I guess those only apply to their respective subtasks and the translate subtask relies on the presence of an en.lproj?

A further complicating factor is that the xcode project itself dates back over a decade since before Base internationalization became and option and was migrated by Xcode several releases ago. The assumptions of where assets are located have probably changed quite a lot since those days.

Any help would be appreciated.

Jeehut commented 4 years ago

While totally understandable, the lack of documentation/ samples meant that I wasn't really certain what is supposed to go into the paths variables: relative paths up to/or including the .lproj, with or without a / at the end, or an absolute path?

This is note entirely correct, the README.md does have documentation & examples for paths, see here:

e.g. ["App/Sources"] for codePaths or ["App/Supporting Files"] for supportedLanguageEnumPaths

But having that said, you may be right regarding if the .lproj folder should be included and if you have any suggestion on how we could improve the documentation here, feel free to post a PR with what would have helped you and I'll happily review and merge it. Note that the paths should not include the .lproj path as this way it would be impossible for BartyCrouch to find multiple locales within that specified path.

Next:

I do not have en.lproj. It is my understanding (possibly totally wrong, but certainly been working for years) that if you use a "base internationalization", you do not create string files for the development language and this role is taken over by the actual strings in the xib & Localizable.strings files.

Yes, I think you were certainly using Base internationalization the wrong way the whole time and it just happened to be working, at least regarding any documentation I have seen from Apple. See also the discussion in https://github.com/Flinesoft/BartyCrouch/issues/144 and this thread and links in there on the Apple Developer forums. Base internationalization is only for Storyboards & XIB files and if you support English, you should have a en.lproj folder with a strings file in there. Think about it conceptually: Leaving English translations in the Base.lproj within Storyboard/XIB files will make your translators/wording correctors directly work on those files, but they should only work on strings files.

More specifically, Base doesn't make any sense for Localizable.strings file (or at least it was never designed to be used in those) as "Base" isn't an official language and Apple could change the fact that it's working as a fallback language at any time (and there might already be situations where this assumption is just not working, because it's not it's purpose).

So I suggest you replace any Strings files within the Base.lproj folders with en.lproj and only use Base for Storyboards & XIB files and even for those, you add a en.lproj translation for the English localization. You can view the Base file language as "non-language", so you could even use pseudo-localization values there to check for any length issues within Interface Builder.

Note that as a hacky workaround you could actually specify "Base" as your development language on BartyCrouch, but then while translating BartyCrouch would send "Base" as the language identifier to Microsoft Translator as it can't know what the actual language is and things would not work.

I hope this helps.

frankrei commented 4 years ago

Hi,

I'm sorry if I came across as aggressive; it was certainly not my intention. I very much appreciate both the effort put into Bartycrouch and you taking the time to respond to my bug report. I had totally overlooked the paths examples. My bad.

I'm well aware of my lack of expertise in localization. I only started shipping localizations a few years back and do the translation work to German and French myself. At the time, Apple was promoting its new XLIFF workflow, so that's what I adopted. This involves exporting the entire project's localizable resources to a huge file hierarchy of which in reality only the single xml XLIFF file is particulaly relevant.

My workflow is simply to export the XLIFF, use the POEdit XLIFF editor with built-in automatic translation support and multi-project translation database to do the translation, then re-import the XLIFF. Xcode takes care of the entire string extraction from xib, storyboards (which I don't use), tables and in-code localizable strings, then puts them back where they belong on re-import. Despite the fact that the XLIFF export always exports the development language as well as one or more languages to be translated, it does not on re-import create any en.lproj folders.

I realize that this is very different from directly working with .strings files and even more different from using semantic names, tables or BartyCrouch.translate.

I would be quite happy with my XLIFF workflow if it weren't for the fact that Xcode is incredibly buggy in that regard. For a long time, Xcode crashed on re-import (a bug in the diff view that the import creates) and there is currently no way of exporting anything short of the entire project.. so if you have 4 targets (as in this project) that "share" the same strings, you get the same string 4 times in the exported XLIFF which is pretty tiresome as well.

Most of all though, while the XLIFF export is worth it when there are big changes, it is complete overkill when all you want to do is add a single new localized string. Importing, exporting & editing for two languages easily takes 10 minutes of busiwork.. when it works.. thus my interest in Bartycrouch.

I got it to do the basic .strings file generation which is really the important part for me. I have experimentally added an English localization and re-run the translation step and gotten as far as the Microya.JsonApiError error 1 that is mentioned in another bug report.

I can get a translation by using

curl -X POST "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=es" \
     -H "Ocp-Apim-Subscription-Key:<my secret>" \
     -H "Ocp-Apim-Subscription-Region:westeurope" \
     -H "Content-Type: application/json" \
     -d "[{'Text':'Hello, what is your name?'}]"

but it won't work unless I pass in the subscription region with Ocp-Apim-Subscription-Region.

Do I need to go back and create a different Azure trial with a North American region? or is there some way of passing the region parameter into the translate subtask?

Your help is very much appreciated.

frankrei commented 4 years ago

I have spent an hour or so experimenting and trying to understand what's going.

I have "hacked" the region identifier into the MicrosoftTranslatorApi.swift file:

    var headers: [String: String] {
        switch self {
        case let .translate(_, _, _, microsoftSubscriptionKey):
            return [
                "Ocp-Apim-Subscription-Key": microsoftSubscriptionKey,
                "Ocp-Apim-Subscription-Region": "westeurope",
                "Content-Type": "application/json"
            ]
        }
    }

and the translation is now working for me. Even though I haven't ever worked with any of the packages that you use, I can pretty much see how to add a "region" option. I haven't ever contributed to an open-source project and I'm not sure how any of that works, but if you'd like I can have a go at contributing passing this additional parameter in; I think there's another open bug report (#191) that would be resolved by this.

If I understand correctly you do not currently support automatically adding keys to tables when a call to NSLocalizedStringFromTable is made. Or I have overlooked or misunderstood something else..

Leaving philosophical choices aside, for my purposes, it would be ideal if I could configure bartycrouch so that:

1) I can define a new NSLocalizedString( "My English Message", comment: "don't care") in my code 2) automatically have "My English Message" = "My English Message"; added in en.lproj/Localizable.strings 3) and automatically have the translated "My English Message" = "Mon message en Anglais"; added to fr.lproj/Localizable.strings

Experimenting with the configuration options, I can either get the string title to appear in all Localized.strings, or all of them to have an empty "My English Message" = ""; entry.

I presume that this is because title != message, so that you can support "message.title" = "My English Message";, but if there is a way around this that I've overlooked, please do point it out.

Have a nice weekend.

Jeehut commented 4 years ago

@frankrei Feel free to post a PR with your additional option, would be great if it also resolved #191.

Yes, BartyCrouch currently doesn't seem to support NSLocalizedStringFromTable as far as I know, we're simply using some of Apples tools and I think they don't support it IIRC (but I never tried myself). Please have a look at https://github.com/Flinesoft/BartyCrouch/issues/20 which is related, I think.

Regarding your configuration wish, did you alredy read the linked blog post or the Localization Workflow section in the README? That's the approach BartyCrouch was designed for. But if you really want to stick to your approach, again I can only ask you if you actually read the README because the "Options for code" toggle in the Update section states that you have this option which does what you want, I think:

defaultToKeys: Add new keys both as key and value.

Please consider actually reading the documentation next time before reporting issues and if you have questions, consider asking them on a question & answer site like StackOverflow. This is not a Q&A forum. See for example this thread which I answered there and is I think related to this one also, explaining stuff around the Base localization.

I'm closing this issue as it's mostly about usage and the only actual issue here is that the documentation wasn't optimized for your use case but is rather more general.

Thank you for your understanding and for any PRs that improve this repo for everyone! For any other request, please create a separate issue so it's easier to find for others.

noah-nuebling commented 2 years ago

Yes, I think you were certainly using Base internationalization the wrong way the whole time and it just happened to be working, at least regarding any documentation I have seen from Apple. See also the discussion in https://github.com/FlineDev/BartyCrouch/issues/144 and this thread and links in there on the Apple Developer forums. Base internationalization is only for Storyboards & XIB files and if you support English, you should have a en.lproj folder with a strings file in there. Think about it conceptually: Leaving English translations in the Base.lproj within Storyboard/XIB files will make your translators/wording correctors directly work on those files, but they should only work on strings files.

Are we absolutely certain about this?

Note the bold sections from this Apple Documentation:

... Xcode modifies your project folder according to the selections you make in this dialog. Xcode creates a Base.lproj folder in your project folder and adds to it the resource files you select. Xcode creates a language folder for the development language but only adds resources that need translation to the folder. For example, if you select English as the development language, Xcode inserts the resource file in the Base.lproj project folder but not the en.lproj folder because the resource is already in English.

I'm new to localization and just throwing this out there, but wouldn't this suggest that you have the translation for the development language directly in the .xib/.storyboard files inside the Base.lproj folder?

I feel like that would make so much more sense from a workflow perspective as well.

You build the interface files in a real language - the development language. And then any further languages you add via .strings files.

Instead of building the UI in some pseudo 'base' language, and then having the extra effort of translating from the pseudo language to the development language via a strings file.

Jeehut commented 2 years ago

@noah-nuebling The section you quote has a screenshot right before it where you can see only a .storyboard and a .xib file, there's no .strings file involved and I think this has a reason. Of course, you could technically put a Localizable.strings file into the Base.lproj folder and act like it's in en.lproj – but what's the point? You could just as well move it to en.lproj and make it more explicit that it's the English language.

The reason "Base localization" was introduced is right the opposite: Without Base.lproj, you would have to put your .storyboard and .xib files into your development language folder, e.g. en.lproj – and this had some downsides. For example: Imagine you're working in a team of developers (from the tech team) and translators (from the marketing team) and the translators have learned to edit .strings files (which are easy to learn the syntax of) – but then the translator wants to adjust a wording in the English language – now they have to open this complicated .storyboard or .xib file, they need to know how to edit the texts, they need to make sure not to change anything else and they need to have Xcode installed in the first place (.strings is just a text file).

In other words: There's no need for a "pseudo language" to be involved, even when the developers develop in English, the final user-facing Strings might still need to be checked and adjusted by someone else who's not a developer. Or from a tool that integrates with your project that uploads all Strings to a website where they can be edited etc.

Apples is also pretty clear on this right at the start of the documentation site you quoted:

Base internationalization separates user-facing strings from .storyboard and .xib files. It relieves localizers of the need to modify .storyboard and .xib files in Interface Builder.

I hope this helps. If you have more questions, feel free to ask.

noah-nuebling commented 2 years ago

Thanks a lot for your clarification @Jeehut!

I went the route of using the "Base" language interface builder files directly when the apps' language is set to English by just not providing an English .strings file for them. And then for Localizable.strings and Localizable.stringsdict I have an English version for each.

The strucure is a little weird but it alleviates me of some extra work.

So far it all seems to be working fine and bartycrouch has been very handy in my workflow.

Do you think I'll run into any issues with this approach?

Jeehut commented 2 years ago

@noah-nuebling Well, BartyCrouch's linting and normalizing features won't work properly without a .strings or .stringsdict file in the source language, the same is true for ReMafoX as well (at least for now). So I would recommend adding an English .strings file instead of using Base as English, BartyCrouch/ReMafoX will make sure to keep the English strings file updated with the Storyboard/XIB file anyways, so there should be no additional work.

noah-nuebling commented 2 years ago

I haven't noticed issues so far with keeping the .strings files in sync with the IB files. But I will keep this in mind if I have any issues. Thanks!