I've built an application and now it's time to translate it to different languages. But some labels that I think I have localised don't get displayed in different languages with the different values.
Image one: So first, I've added the Swedish language inside
"Project -> Localizations"
Than I went inside the "LaunchScreen.storyboard" and edited the file.
This one:
Edited to this:
After that I made sure my launch screen supports the language like this:
And for the last step.. I edited the application language to Swedish like this:
But when I run it on the simulator, the text is not getting translated. It still remains the same text, and I've no idea how to solve it. Do you? Do not hesitate to help me out, I would really appreciate it!
So, I'm not sure if I've to add some code or whatever to make this work, but I followed a guide, and they don't. That's one of the reasons why I'm so confused. And thanks in advance!
BEFORE DISLIKE: At least give me a reason for it, so I can improve it in the future!
Since storyboards elements ids changes and they are not managable to be used as "keys" in your localized "key-value" pair one better solution when localizing storyboards (UI) elements could be to provide a custom User Define Attribute defined as a Locale Key to be used for that UIView.
Then you can define in your Storyboard Attribute Inspector for a specific UI View an input field that will be filled with the localized key defined in your Localizable.strings files (one for English one will be for Swedish, both should have the same keys but with different values - in the English one will have english translations in values, and in Swedish one the opposite.
1) For example since you want to localize a UI View UILabel than you can have this in a swift file Localizable.swift for example (the code makes possible:
import UIKit
// MARK: Localizable
public protocol Localizable {
var localized: String { get }
}
extension String: Localizable {
public var localized: String {
return NSLocalizedString(self, comment: "")
}
}
// MARK: XIBLocalizable
public protocol XIBLocalizable {
var localeKey: String? { get set }
}
extension UILabel: XIBLocalizable {
#IBInspectable public var localeKey: String? {
get { return nil }
set(key) {
text = key?.localized
}
}
}
2) You can uncheck your storyboard translations so you will endup having 1 storyboard without duplicating the storyboard into multiple translated version.
3) You can create now a Localizable.strings file containing your keys and translations and localize this file in order to have your translations (as you did with the storyboard, but instead of localizing the storyboard into multiple translated version, you will localize the Localizable.strings file (so you will see two files after you localize that in both languages, one will be called Localizable.strings (English) and the other Localizable.strings (Swedish).
Your Localizable.strings (English) could look like this for example:
"main_page_title_label" = "Main Page Title in English";
Your Localizable.strings (Swedish) could look like this for example:
"main_page_title_label" = "Huvudsida Titel på engelska";
4) Now go to your LaunchScreen.storyboard which you should have un-localized so you only have one version of it and not two like in your example pictures (English, Swedish). Look for the UILabel you want to localize and under the Attribute Inspector you will see a new input field called Locale Key here you can put as a value main_page_title_label. And now you have just localized a UILabel in your storyboard.
5) To test it from the simulator you have to change the language in Edit Scheme > Run > Options > Application Language and after you save it you can now run the app in the simulator and it will simulate that your simulator OS system will be set to Swedish language, so the label set with that key will show the Swedish value for that key.
Supporting more UI Views (UIButton, UITextField, UISearchBar..)
If you want to be able to localize more UI View, and not just UI View of type UILabel than you can add more support to the Localizable.swift file.
If you want to be able to localize also a UI View of type Button you can add this to Localizable.swift:
extension UIButton: XIBLocalizable {
#IBInspectable public var localeKey: String? {
get { return nil }
set(key) {
setTitle(key?.localized, for: .normal)
}
}
}
To support localization in storyboards for UITextField and UISearchBar placeholder text add this to your Localizable.swift:
// MARK: Special protocol to localizaze UI's placeholder
public protocol UIPlaceholderXIBLocalizable {
var localePlaceholderKey: String? { get set }
}
extension UITextField: UIPlaceholderXIBLocalizable {
#IBInspectable public var localePlaceholderKey: String? {
get { return nil }
set(key) {
placeholder = key?.localized
}
}
}
extension UISearchBar: UIPlaceholderXIBLocalizable {
#IBInspectable public var localePlaceholderKey: String? {
get { return nil }
set(key) {
placeholder = key?.localized
}
}
}
I like denis_lor's solution, but I thought that I first want to get working what should be working already (translation of the storyboard with a simple strings file) without having to re-implement that functionality in a similar way.
My problem was similar to what is described in the question. Translations were already working to a point, but manually added and some changed translations didn't get translated at all.
The solution I found was recreating the strings file using ibtool:
ibtool AppName/Base.lproj/Main.storyboard --generate-strings-file tmp.strings
After backing up my current (just partly working) Main.strings file (AppName/de.lproj/Main.strings) I overwrote it with the newly generated tmp.strings
mv tmp.strings AppName/de.lproj/Main.strings
And then I manually changed the values of that file to the backed up values.
Now everything gets translated correctly again. I think the problem could have had something to do with encoding problems, because that strings files are UTF16 encoded.
According to Apple docs, the LaunchScreen.storyboard is static and shouldn't have texts because these texts will not be localized.
Avoid including text on your launch screen. Because launch screens are static, any displayed text won’t be localized.
source: https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/launch-screen/
Related
I'm trying to change the text for the "Identity management" menu item which is added up in the ABP framework somewhere. Is it possible find the localization key somewhere to add it to my en.json file or do I have to plunk through the context.Menu.GetAdministration() menu items in my MenuContributor and hack it there? I've tried a bunch of different variations in the en.json file to no avail.
Thanks.
See Abp Localization Docs
Extend an existing resource JSON file
zh-Hans
{
"culture": "zh-Hans",
"texts": {
"Menu:IdentityManagement": "身份管理"
}
}
en
{
"culture": "en",
"texts": {
"Menu:IdentityManagement": "Identity"
}
}
Extending IdentityResource
options.Resources
.Get<IdentityResource>()
.AddVirtualJson("/Localization/LocalizeModuleTest");
Effect is as follows
I'm currently trying to add a custom font to my project, but I somehow won't work.
I've already added the .otf file to a font folder, checked that it targets to my project and added Fonts provided by application to Info.plist.
struct ContentView: View {
var body: some View {
Text("CLOSEST")
.foregroundColor(Color("primary"))
.font(Font.custom("HelveticaNowDisplayBold", size: 60))
}
}
If you have made sure that the Info.plist is using the correct filename:
Note if you are using Xcode 13 you may not have an Info.plist where you expect. This SO answer explains where you can find it.
That the font is available in the app's target.
You also need to make sure that you are accessing the font by the correct name.
An easy way to check the font's name is to add the following to your AppDelegate in the didFinishLaunchingWithOptions before the return true. Or if you are using the new SwiftUI lifecycle you can add it to an .onAppear.
for family in UIFont.familyNames.sorted() {
let names = UIFont.fontNames(forFamilyName: family)
print("Family: \(family) Font names: \(names)")
}
This will list all the fonts by family and name.
Just remember to remove it once you have finished using it as you don't need to unnecessarily print to the console.
When I do it for my fonts (I have added the same font as you) I find the following in the console in the list of available fonts (see the above screenshot) :
Family: Helvetica Now Display Font names: ["HelveticaNowDisplay-Bold"]
Your font may have a different name to mine, and it is important to note that the font name may not be the same as the filename. This is what trips up a lot of people, as they try using the filename when they need to use the font name.
The following test code produces:
struct ContentView: View {
var body: some View {
Text("Hello")
.foregroundColor(.blue)
.font(Font.custom("HelveticaNowDisplay-Bold", size: 60))
}
}
For more information about adding custom fonts see Apple's documentation.
Dynamic Type in SwiftUI
If you are using a custom font then you should consider setting it up so that it will scale with dynamic type.
iOS 14
iOS 14 introduces a new modifier that allows you to scale a font relative to a Dynamic Font Type.
Text("Hello")
.font(.custom("HelveticaNowDisplay-Bold", size: 60, relativeTo: .body))
iOS 13
If you are using iOS 13 that requires a bit more effort to get the same effect.
You first need to create a ViewModifier. This view modifier listens to the size category from the environment (it doesn't actually use it but having it here makes sure the view modifier is updated whenever the size category is updated).
struct ScaledFont: ViewModifier {
#Environment(\.sizeCategory) var sizeCategory
var name: String
var size: CGFloat
func body(content: Content) -> some View {
let scaledSize = UIFontMetrics.default.scaledValue(for: size)
return content.font(.custom(name, size: scaledSize))
}
}
extension View {
func scaledFont(name: String, size: CGFloat) -> some View {
return self.modifier(ScaledFont(name: name, size: size))
}
}
It is then used in the following way:
Text("Hello")
.scaledFont(name: "HelveticaNowDisplay-Bold", size: 60)
For a really good write up check out this post on Hacking With Swift.
For those people who no longer have a app/scene delegate to put this in. In your <Your_App_Name>App.swift file
init() {
for family in UIFont.familyNames.sorted() {
let names = UIFont.fontNames(forFamilyName: family)
print("Family: \(family) Font names: \(names)")
}
}
Will work
You need to include the extension of the font (for example .ttf) in the info.plist . I saw a couple of tutorials on youtube, which do not included it, but for me, it does not work without it.
I had the exact same problem. The following steps fixed it for me. I'm currently using Xcode 11.4.1
Create a storyboard, add a label and select your font in the inspector as font for the label.
Build your App in the Simulator
Check the installed fonts with:
for family in UIFont.familyNames.sorted() {
let names = UIFont.fontNames(forFamilyName: family)
print("Family: \(family) Font names: \(names)")
}
If it's appearing you can use it also programmatically
Here is also a list of Common mistakes with adding custom fonts
I had the same issue,
it worked for me when I omitted the "-Regular",
but in the info.plist I wrote it with it.
Sometimes font file name and the font actual name are different.
In my case my file name was SCRIPTIN.ttf but the actual font name was Scriptina.
I found this by running the following function in App.swift file.
init() {
for family in UIFont.familyNames.sorted() {
let names = UIFont.fontNames(forFamilyName: family)
print("Family: \(family) Font names: \(names)")
}
}
Just put this file after this function in App.swift file and run the app. You will get a list of all loaded files and their names including the newly added font.
var body: some Scene {
WindowGroup {
ContentView()
}
}
Thank you.
Xcode 13 Press on target, then info, type Fonts provided by the application and add item font name as like image
Jonas Deichelmann's answer (to list the fonts available to app) was very helpful to fine tune the Info.plist entries.
Contrary to Apple's example at https://developer.apple.com/documentation/swiftui/applying-custom-fonts-to-text that shows the Fonts provided by application entries to include a relative path to the font files including a subdirectory, I had to provide exclusively the font file name (e.g. Mulish-Regular.ttf with the file extension but without the subdirectory into which the file is stored)
Xcode 14.2
When I'm binding text to an input on iOS, my setter is called each time a character is added, on iOS, but not on Android.
If I put a breakpoint on a property that is binded to a TextField in iOS, each time a character is entered, the property setter will be called, but not on an Android EditText.
It makes more complex ViewModels with several input attached to getter/setter tested on iOS completely useless on Android since it cannot be used.
Is there a way to make the "MvxBind="Text SomeProperty" acting like iOS on Android?
Events like "AfterTextChanged" (any binding to a command) aren't property-friendly, and would break my ViewModel. I don't want to have a platform-dependent workaround.
[Edit]
// Droid. It calls the TotalAmount setter once the editing is done.
<EditText local:MvxBind="Text TotalAmount,
Mode=OneWayToSource; Text TotalAmountString, Mode=OneWay" />
// Touch. It calls the TotalAmount setter on key press.
set.Bind(MyTotalAmountTextField)
.For(v => v.Text)
.To(vm => vm.TotalAmount).OneWayToSource();
set.Bind(MyTotalAmountTextField)
.For(v => v.Text)
.To(vm => vm.TotalAmountString).OneWay();
By the way, the displayed property is always formatted with a dollar sign, that's why I'm using an half-duplex approach for binding.
Appart from this live (iOS) versus after-edit (Droid) problem, the bindings are working well.
The default behaviour for TwoWay Text binding on both Android and iOS is to do per character binding.
You can see this behaviour in, for example, the N=0 video at 18:43 - http://youtu.be/_DHDMNB_IeY?t=18m43s
If you are not seeing this behaviour in your EditText then I guess it might be down in some way to your app or perhaps to a bug (e.g. perhaps in OneWayToSource binding somehow - this certainly isn't as commonly used as other binding modes).
To workaround this, I can only think to suggest:
Log it as an issue with a reproducible case (github repo) on GitHub/MvvmCross - someone there might be able to help - or you might be able to fix it yourself.
Try TwoWay binding instead
Try creating your own custom binding or your own custom control - this is actually very easy to do - see the tutorials N=28 and n=18 on http://mvvmcross.blogspot.com - for example you could try inheriting from EditText to create something like;
public class MyEditText : EditText {
public MyEditText(Context c, IAttributeSet a) {
this.AfterTextChanged += (s,e) => MyTextChanged.Raise(this);
}
public event EventHandler MyTextChanged;
public string MyText {
get { return Text; }
set { Text = value; }
}
}
i'm working in Multilingual grails application (English and arbaic) , i want when the user chooses Arabic language the view's labels will be on the right side of the page and in English on the left side , how this can be achieved ?
thanks
You can use internationalization in grails through messages.properties file, you can define message signature in files and and they can be accessed through ?lang=es on the URL, you may need to have two files one for english and another for Arabic.
for example define in the messages.properties:
vendor.link.dashboardLink = Vendor Dashboard
and on the GSP page you can access it like:
<g:message code="vendor.link.dashboardLink" />
you can find more about internalization at grails doc have a look at http://grails.org/doc/2.2.1/guide/i18n.html
If the views have differences beyond simple string substitution, I would recommend using a different set of views based on locale:
Example controller code:
import org.springframework.web.servlet.support.RequestContextUtils as RCU
class ExampleController {
final static String englishLanguageCode = new Locale('en').getLanguage()
final static String arabicLanguageCode = new Locale('ar').getLanguage()
def differentViews() {
def currentLocale = RCU.getLocale(request)
switch(currentLocale.language) {
case englishLanguageCode:
render view: 'englishView'
break
case arabicLanguageCode:
render view: 'arabicView'
break
default:
// pick a default view or error page, etc.
}
}
}
I need to switch Locate in my app between Arabic and English.
I have the following code to switch locale:
if (Locale.getDefault() == Locale.get(Locale.LOCALE_ar, null)) {
Locale.setDefault(Locale.get(Locale.LOCALE_en, null));
} else {
Locale.setDefault(Locale.get(Locale.LOCALE_ar, null));
}
And in my app I have the following resource files:
appName.rrh
appName.rrc
appName_ar.rrc
appName_en.rrc
And I have a button which uses a localized string as follows:
subscribeButton = new ButtonField(res.getString(LANG), ButtonField.CONSUME_CLICK);
My problem is when the locale is changed to Arabic, the UI flips (Arabic is right to left), and switching it again to English flips it again, but all without the text in the button changing. Please guide me on what I'm doing wrong.
Its because, you have created the button field with the text which was relevant for that locale. Once the locale changes, you will have to re set the buttonField text as
subscribeButton.setLabel(res.getString(LANG));