jiritrecak / laurine Goto Github PK
View Code? Open in Web Editor NEWLaurine - Localization code generator written in Swift. Sweet!
License: MIT License
Laurine - Localization code generator written in Swift. Sweet!
License: MIT License
I need to map error codes to localized strings which currently does not seem to be possible with Laurine (or am I missing something?)
I would like to be able to write something like
let errorMessage = Foo.Bar.ErrorCodes(100500)
or
let errorMessage = Foo.Bar.ErrorCodes[100500]
I read that struct is kept in the memory, so if you have many strings that it might be problematic.
Am I right?
Hi,
Actually the code does not support frameworks properly, the generated String extension:
private extension String {
var localized: String {
return NSLocalizedString(self, tableName: nil, bundle: NSBundle.mainBundle(), value: "", comment: "")
}
func localizedWithComment(comment:String) -> String {
return NSLocalizedString(self, tableName: nil, bundle: NSBundle.mainBundle(), value: "", comment: comment)
}
}
It's using _NSBundle.mainBundle()_, as a result if you call a localized var from the App target, the system will load the localized string from the localization file of that target and not from the framework.
I'm testing a little hack:
private extension String {
var localized: String {
return NSLocalizedString(self, tableName: nil, bundle: NSBundle(forClass: LocalizationsBundleClass.self), value: "", comment: "")
}
func localizedWithComment(comment:String) -> String {
return NSLocalizedString(self, tableName: nil, bundle: NSBundle(forClass: LocalizationsBundleClass.self), value: "", comment: comment)
}
}
internal class LocalizationsBundleClass {}
I created an internal dummy class in order to fetch the correct bundle via:
NSBundle(forClass: LocalizationsBundleClass.self)
The name of this dummy class should be based on the values in the script e.g:
OUTPUT_PATH="$BASE_PATH/Path_To_Output/_Localizations_.swift"
Although I'm not sure if this is the more optimal solution.
Hello,
Your library seems to be very handy and I really consider it to use it in my projects. What I would appreciate is to somehow help me to substitute all my current NSLocalizedString()
in my code into your approach. Because manually changing all my strings is pretty big task and it would definitely be able to do it just with one command/script.
Thanks.
Best,
Patrik
Hi! I just got turned onto this library from the Awesome iOS mailing list. I think this library could help my current projects a ton!
One question, does Laurine handle fallbacks gracefully? For instance, if the phone's language is set to German, and a requested key is not in the German Localizable.strings
file, will it fall back to using the Base language's value?
My Localizable.strings contains
"Message" = "WTF\r\nWTF";
Laurine generates
/// Base translation: WTF
WTF
static var Message: String = "Message".localized
Swift Compiler Error Localizations.swift:64:1: Expected declaration
Since Apple is releasing Swift 4 sometime this fall, I will be going ahead and preparing a new version for it. This will also be a considerable overhaul of Laurine as I have few improvements in mind, most notably unit tests and rework of the script so it can be edited and worked on more easily, also performance updates for large files. The goal though is to preserve input or output, giving you no extra work, just replacing the Laurine file. Next version of Laurine should support both S3 and S4.
The important part is that it will also be reworked in a way where I can easily adapt it to support other file types as well (say, image assets) - My goal is to create few more projects based on Laurine where everything can use dot notation in a similar fashion, type safe, quickly.
I will be extending the list of stuff that needs to be done - if you have something you would like to see to be done over the holidays, don't hesitate to drop me a line here.
Sincerely,
Jiri
TBD:
Performance and extendability core rewrite
Unit Tests (File Parsing / Loading & CMD Line input)
Unit Tests (Properties)
Unit Tests (Method generation)
S3 Support
S4 Support
Add automatic support for frameworks
Fix issues with nesting and duplicities
Add debug log options
Pods Support
Carthage Support
I get this error when running in Xcode 10.2 - Swift 4.2
0. Program arguments: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -interpret /Users/emil/Virksomheder/Projekter/sense-ios/Sense/Sense/Libraries/LaurineGenerator.swift -enable-objc-interop -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk -module-name LaurineGenerator -- -i /Users/emil/Virksomheder/Projekter/sense-ios/Sense/Sense/Assets/en.lproj/Localizable.strings -o /Users/emil/Virksomheder/Projekter/sense-ios/Sense/Sense/Helpers/Localizations.swift -c true 0 swift 0x00000001080cfee3 PrintStackTraceSignalHandler(void*) + 51 1 swift 0x00000001080cf6bc SignalHandler(int) + 348 2 libsystem_platform.dylib 0x00007fff71114b5d _sigtramp + 29 3 libsystem_platform.dylib 000000000000000000 _sigtramp + 2398008512 4 libswiftCore.dylib 0x00007fff70959c09 _swift_updateClassMetadataImpl(swift::TargetClassMetadata<swift::InProcess>*, swift::ClassLayoutFlags, unsigned long, swift::TypeLayout const* const*, unsigned long*, bool) + 265 5 libswiftCore.dylib 0x00007fff70959c4b swift_updateClassMetadata2 + 27 6 libswiftCore.dylib 0x000000010b1d14ce swift_updateClassMetadata2 + 2592569502 7 libswiftCore.dylib 0x00007fff70961b77 swift::MetadataCacheEntryBase<(anonymous namespace)::SingletonMetadataCacheEntry, int>::doInitialization(swift::ConcurrencyControl&, swift::MetadataCompletionQueueEntry*, swift::MetadataRequest) + 215 8 libswiftCore.dylib 0x00007fff709573f3 swift_getSingletonMetadata + 579 9 libswiftCore.dylib 0x000000010b1b01af swift_getSingletonMetadata + 2592444415 10 libswiftCore.dylib 0x000000010b1b013c swift_getSingletonMetadata + 2592444300 11 swift 0x000000010496719d llvm::MCJIT::runFunction(llvm::Function*, llvm::ArrayRef<llvm::GenericValue>) + 365 12 swift 0x000000010496d572 llvm::ExecutionEngine::runFunctionAsMain(llvm::Function*, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&, char const* const*) + 1090 13 swift 0x0000000103f365d1 performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*) + 58913 14 swift 0x0000000103f246de swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 6862 15 swift 0x0000000103ec27be main + 1246 16 libdyld.dylib 0x00007fff70f2f3d5 start + 1 /Users/emil/Library/Developer/Xcode/DerivedData/Sense-amrxbuahpzprwaepprxlnxrnivph/Build/Intermediates.noindex/Sense.build/Debug-iphoneos/Sense.build/Script-FD357AEE20B807A500F1DB2E.sh: line 27: 6297 Segmentation fault: 11 "$LAURINE_PATH" -i "$SOURCE_PATH" -o "$OUTPUT_PATH" -c true Command PhaseScriptExecution failed with a nonzero exit code
If I understand the usage correctly, you need to remove instances of NSLocalizedString
and its variations from your code, and then replace them with the corresponding generated Localizations.…
properties and variables.
But then, this means that genstrings
command won't be able to extract these strings again, as it won't find them. How is then further maintenance of Localizable.strings
supposed to happen?
What happens if a string is no longer used in the code? it will live forever in Localizable.strings
and Localizations.swift
unless manually removed, is that correct?
Hello Jiri,
I coincidentally bumped into a problem where I used a key YES
and apparently yes is also a keyword in Objective-C so a generated code could not compile. I suggest to use a key but with a prefix. Do you have any other/better solution?
Best,
Patrik
Just tried to migrate a project to Swift 4 and it gives me an error on the Laurine build phase. Should probably be updated for Swift 4.
Any plans to update the project to swift 3?
If you need a hand just create a branch and I could help.
Hi, First of all I must to say this Lib is Just AWESOME!..
One thing is that in my project I'm using a framework to share functionality between extensions and the App, said that I want to keep every single localization string within the framework so it can be used by all the extensions and the App itself.
But at the moment it's not working because the generated code are not public.
Hello,
currently I have one language supported in app but soon I have to add another. How this script should be configured to support both languages without changes in code?
Is there any way to get this to work with the Xcode "Export for localization" functionality?
When I create a custom Localizable.strings file to use for Laurine (which rocks by the way), the content of that file does not get exported to my xliff files.
Hi!
Laurine, for some reason, just doesn't see this localization string: "Error.Title.General"
. It doesn't even create a top-most struct
for the Error
. Is it intentional behaviour?
Thanks in advance.
Spent some time today figuring out why out of 200+ strings Laurine generated localizations only for 17. Turned out it was caused by an illegal character at the end of one line which looks like normal space (I asked a colleague with Windows machine to translate the file for me):
let bad = " "
let good = " "
bad == good // false
print(bad.utf8.map { String($0, radix: 16) }) // ["c2", "a0"]
print(good.utf8.map { String($0, radix: 16) }) // ["20"]
I have no idea what this thing is, but it breaks NSDictionary(contentsOfFile:)
on which Laurine relies to read the input file.
If the character is in the middle of string the result is nil
, if it's in the end NSDictionary
quietly fails to read further and returns what was read so far.
I used the latest version from master
.
Version 7.3 beta (7D129n)
/usr/local/bin/LaurineGenerator.swift:47:2: error: unknown attribute 'exported'
@exported import Darwin
~: brew install jiritrecak/laurine/laurine
Warning: jiritrecak/laurine/laurine-0.2.0 already installed
Running LaurineGenerator.swift
in the run phase takes about 5 seconds with my localization file (containing 460 strings).
When first compiling the script to an executable, the generation time only takes 0.25 seconds.
I know in the example project, the output is only generated if existing output is not newer than the localization input file. For the sake of having a clean and reproducible build, I prefer to skip that check and delete the generated files every time before running the script.
How could this project provide binaries?
xcrun -sdk macosx swiftc LaurineGenerator.swift -o LaurineGenerator
What do you think?
It would be great if Laurine could help finding unused keys to make sure the Localizable.strings
file can be kept clean over time. Before using Laurine I used a script to make sure keys not appearing in any NSLocalizedString
would be deleted. But doing something similar with Laurine isn't possible as the command varies quite a lot (I'm using the dot-syntax leading to names like Localizations.Model.User.FirstName
as an example – the key reads MODEL.USER.FIRST_NAME
).
I'm not sure how this could be achieved, though I hope someone can come up with an idea. Maybe we could rework the resulting Swift file so that Xcode marks unused lines automatically with a warning (similar to unused variables)? Not sure if this is even possible. Would have to further investigate.
Any ideas?
I have a code a bit like that
let count = liste.count
let formattedAmount = formatter.string(from: montant as NSNumber)!
let formattedDepense = formatter.string(from: depense as NSNumber)!
let formattedrevenu = formatter.string(from: revenu as NSNumber)!
let count = selectedRow.count
let str = Localizations.Listeoperation.Info(count)
in the file Localizable.strings (English)
"ListeOperation.info" = "%d opérations Dépenses : \(formattedDepense) Revenus : \(formattedrevenu) Solde : \(formattedAmount)";
and file Localizations.swift
public struct Listeoperation {
/// Base translation: %d opérations Dépenses : (formattedDepense) Revenus : (formattedrevenu) Solde : (formattedAmount)
public static func Info(_ value1 : Int) -> String {
return String(format: NSLocalizedString("ListeOperation.info", comment: ""), value1)
}
}
it does not work how could I do ??
"\(variable)" is not taken updated
I'm unable to run the package under El Capitan :(
getting this error:
<unknown>:0: error: no such file or directory: 'LaurineGenerator.swift'
Hello everyone,
I was thinking about making the same generator as Laurine for the image assets as well since the base is solid and resource generators that are available do not seem to do the job properly. It would work the same way, with similar options, through command-line or as build script - with a primary goal to generate nested result that you would use similarly as Laurine:
Localizations.Profile.Header
vs Images.Profile.HeaderBar
There would also be a convenience to directly instantiate images (.image
) or something similar, to get rid of UIImage(named: Images.Name)
altogether.
What are your thoughts about that? Would you like something like this?
Have a great day,
Jiri
Nested Structures don't seem to be working in the latest version on github for Swift.
I tried both of these:
"PROFILE-NAVIGATION_BAR-ITEMS-DONE" = "Done" // -c -d -
"PROFILE.NAVIGATION_BAR.ITEMS.DONE" = "Done" // -c -d .
I tried specifying and leaving the defaults, neither worked. I even tried adding a new string to the example and that didn't work.
It changed it to /// Base translation: Thanks everyone! You rock!
public static var ContributorsFooter : String = "Contributors.Footer".localized
Warning: Calling Formula.sha1 is deprecated!
Use Formula.sha256 instead.
/usr/local/Library/Taps/jiritrecak/homebrew-laurine/laurine.rb:4:in `<class:Laurine>'
Please report this to the jiritrecak/laurine tap!
Warning: Calling SoftwareSpec#sha1 is deprecated!
Use SoftwareSpec#sha256 instead.
/usr/local/Library/Taps/jiritrecak/homebrew-laurine/laurine.rb:4:in `<class:Laurine>'
Please report this to the jiritrecak/laurine tap!
Warning: Calling Resource#sha1 is deprecated!
Use Resource#sha256 instead.
/usr/local/Library/Taps/jiritrecak/homebrew-laurine/laurine.rb:4:in `<class:Laurine>'
Please report this to the jiritrecak/laurine tap!
Hi,
When I try to create the localization for Obj-C the script generates the following method:
/// Base translation: GO TO %@
- (NSString *(^)(NSString *))BenefitsGoToButtonTitle;
Is this the desired behaviour?
Best regards.
Unit tests would be awesome. Any idea how to integrate them best? Create an OSX Xcode project with a test target? Or use Swift Package Manager?
Introduction & My approach
Hello guys, I am glad you are enjoying this project so much - it is trending, people write about it, and I really strive to make it the ultimate solution for translations in the application.
Now, the base is already great, but what is really missing is support for Plurals. You probably know how it is done in the iOS - instead of writing it to .strings, you generate .stringsdict (plist, really) that contains rules for how the word should change with different number of X.
I am not the biggest fan of that approach, but it is what it is and we have to work with what we have (plus there is probably not better solution, I know). I would like to know feedback about this topic. My idea is following:
The output would then look like this:
Localizations.Profile.Friends(1) // 1 Friend
Localizations.Profile.Friends(2) // 2 Friends
Localizations.Profile.Friends(10) // 10 Friends
What do you say? What are your opinions about this?
Possible enhancement for future version
Now as I mentioned, I am really not the biggest fan of .stringdict file - it is complicated to write. So I was thinking that it could be possible to write plural localizations directly from strings. Obviously, I would never want to build that decision engine, that is way out of scope of this project and would be completely pointless, but, imagine following. Instead of writing everything to .stringdict, write it to .strings as following:
"Localizations.Profile.Friends[one]" = "%d Friend";
"Localizations.Profile.Friends[few]" = "%d Friends";
"Localizations.Profile.Friends[many]" = "%d Friends";
key in [] is the same key that you would use in dictionary to write rule. Except you would not need the dictionary, because that would be built dynamically by the system and then fed to iOS localization engine (if that is possible, which I am, so far, unsure of.
Do you think we could change the base format of the localizations and do you think it is viable, or it seems like too much over-engineering and we should just stick with .stringdict?
Thank you for your feedback!
Not issue but add some warning would be good.
Localizable.strings
"profile.phone" = "Some title"
"profile.phone.number" = "Your phone number!"
Laurine will create without any warnings
struct phone {
/// Base translation: Your phone number!
static var number: String = "profile.phone.number".localized
}
I just started using Laurine in a project that also uses SwiftLint to ensure code conventions. It appears that Laurines produced code does not comply to some conventions, for example I get this:
warning: Colon Violation: Colons should be next to the identifier when specifying a type. (colon)
Could we make the generated code comply to the Swift conventions expressed by https://github.com/realm/SwiftLint?
Hi,
Regarding Git the script becomes a little annoying because of the timestamp added to the documentation header:
//
// Autogenerated by Laurine - by Jiri Trecak ( http://jiritrecak.com, @JiriTrecak )
// Do not change this file manually!
//
// 2015-12-14 at 5:48 PM
//
That makes the file to be included on every commit just because of the date time update.
I think there are two options to deal with this:
if Localizable.strings.modificationDate > Localization.swift.modificationDate {
do rebuild
} else {
ignore since there is no changes
}
What do you think?
Currently when Laurine generates the static vars, there is no comment:
public struct Localizations {
public struct Project {
public struct Example {
/// Base translation: An example project
public static var Title : String = NSLocalizedString("Project.Example.Title", comment: "")
}
}
}
In my case, I use a BartyCrouch script after the Laurine script, which uses the comments to populate all my other translation files (everything is in 24 languages in my app).
They end up with a useless comment on every key:
/* No comment provided by engineer. */
"Project.Regional.Title" = "";
I'd really like to have the option to tell Laurine to use the Base translation as the comment, so to get a better result:
public struct Localizations {
public struct Project {
public struct Example {
/// Base translation: An example project
public static var Title : String = NSLocalizedString("Project.Example.Title", comment: "An example project")
}
}
}
/* An example project */
"Project.Regional.Title" = "";
Hi,
In my localization file I have an string like this:
reset-pass-label = "Enter your email address below and we'll \r\n send you a new password";
I used _\r\n_ because I need to break the line at that point, the problem is that the generated Swift file looks like this:
/// Base translation: Enter your email address below and we'll
send you a new password
public static var ResetPassLabel : String = "reset-pass-label".localized
Somehow the break line is being applied to the source code as well, as a result the file does not compile.
Localized String Identifiers can have spaces in them.
"This is my localizable string" = "This is my localizable string"
This will result in the following generated code:
public static var This is my localizable string : String = "This is my localizable string".localized
Laurine should replace this whitespace characters with some safe character, like an underscore, or just remove it.
Hi,
I've updated Laurine to the latest version in my project. However when I set up the script (inside Xcode build phases) then I've got this message:
PROJECT/PROJECT/Localization/Localizations.swift -c -d -
Invalid value(s) for option -d, --delimiter:
Usage: /Users/xxx/PROJECT/PROJECT/Localization/LaurineGenerator.swift [options]
-i, --input:
Required | String | Path to the localization file
-o, --outpu:
Optional | String | Path to output file (.swift or .m, depending on your configuration. If you are using ObjC, header will be created on that location. If ommited, output will be sent to stdout instead.
-l, --langu:
Optional | String | [swift | objc] | Specifies language of generated output files | Defaults to [swift]
-d, --delim:
Optional | String | String delimiter to separate segments of each string | Defaults to [.]
-c, --capit:
Optional | Bool | When enabled, name of all structures / methods / properties are automatically CamelCased | Defaults to false
-b, --baseC:
Optional | String | Name of the base class | Defaults to "Localizations"
-t, --strin:
Optional | String | Name of strings table | Defaults to nil
-s, --custo:
Optional | String | A custom superclass name | Defaults to NSObject (only applicable in ObjC)
Command /bin/sh failed with exit code 64
This is my command to run Laurine:
"$LAURINE_PATH" -i "$SOURCE_PATH" -o "$OUTPUT_PATH" -c -d "-"
It does not work with -d "-"
neither with -d -
.
Do you know how to fix it?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.