How to Specify Japanese Encoding for a UIlabel

How to specify Japanese encoding for a UILabel?

I found an extremely hacky solution that seems to work. However, it seems absurd that there's no way to simply set the locale of a label, so if anyone finds something I missed, please post an answer.


The trick relies on the fact that the Hiragino font displays kanji using Japanese encoding rather than Chinese encoding by default. However, the font looks like shit for English text, so I have to search every string in every label for Japanese substrings and manually change the font using NSMutableAttributedString. The font is also completely broken so I had to find another workaround to fix that.

[assembly: ExportRenderer(typeof(Label), typeof(RingotanLabelRenderer))]
namespace MyApp
{
public class MyLabelRenderer : LabelRenderer
{
private readonly UIFont HIRAGINO_FONT = UIFont.FromName("HiraginoSans-W6", 1); // Size gets updated later

protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);

// BUGFIX: Chinese encoding is shown by default. Switch to Hiragino font, which correctly shows Japanese characters
// Taken from https://stackoverflow.com/a/71045204/238419
if (Control?.Text != null && e.PropertyName == "Text")
{
var kanjiRanges = GetJapaneseRanges(Control.Text).ToList();
if (kanjiRanges.Count > 0)
{
var font = HIRAGINO_FONT.WithSize((nfloat)Element.FontSize);
var attributedString = Control.AttributedText == null
? new NSMutableAttributedString(Control.Text)
: new NSMutableAttributedString(Control.AttributedText);

// Search through string for all instances of Japanese characters and update the font
foreach (var (start, end) in kanjiRanges)
{
int length = end - start + 1;
var range = new NSRange(start, length);
attributedString.AddAttribute(UIStringAttributeKey.Font, font, range);

// Bugfix: Hiragino font is broken (https://stackoverflow.com/a/44397572/238419) so needs to be adjusted upwards
// jesus christ Apple
attributedString.AddAttribute(UIStringAttributeKey.BaselineOffset, (NSNumber)(Element.FontSize/10), range);
}

Control.AttributedText = attributedString;
}
}
}

// Returns all (start,end) ranges in the string which contain only Japanese strings
private IEnumerable<(int,int)> GetJapaneseRanges(string str)
{
for (int i = 0; i < str.Length; i++)
{
if (IsJapanese(str[i]))
{
int start = i;
while (i < str.Length - 1 && KanjiHelper.IsJapanese(str[i]))
{
i++;
}
int end = i;
yield return (start, end);
}
}
}

private static bool IsJapanese(char character)
{
// An approximation. See https://github.com/caguiclajmg/WanaKanaSharp/blob/792f45a27d6e543d1b484d6825a9f22a803027fd/WanaKanaSharp/CharacterConstants.cs#L110-L118
// for a more accurate version
return character >= '\u3000' && character <= '\u9FFF'
|| character >= '\uFF00';
}
}
}

How to specify Japanese encoding for a Label?

A solution involving a custom renderer:

Android project:

[assembly: ExportRenderer(typeof(Label), typeof(JapaneseLabelRenderer))]
namespace MyProject.Controls
{
public class JapaneseLabelRenderer : LabelRenderer
{
public JapaneseLabelRenderer(Context context) : base(context) { }

protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
Control.TextLocale = Locale.Japan;
}
}
}

Japanese character encoding problem on iPhone

That's because what you have is not an "ASCII representation". Those ("成による") are known as XML or HTML character entity references. As such, they only work if you parse them in an HTML context (like a web view).

What you need to do is either use a UIWebView for your labels, or parse the character entity references to turn them in to a normal NSString.

objective c UTF8String not working with japanese

Your source file is in UTF-8, but the compiler you are using thinks it's ISO-Latin 1. What you think is the string @"你好" is actually the string @"你好". But when you ask NSString* to give you this back as ISO-Latin 1, and treat it as UTF-8, you've reversed the process the compiler took and you end up with the original string.

One solution that you can use here is to tell your compiler what encoding your source file is in. There is a compiler flag (for GCC it's -finput-charset=UTF-8, not sure about clang) that will tell the compiler what encoding to use. Curiously, UTF-8 should be the default already, but perhaps you're overriding this with a locale.

A more portable solution is to use only ASCII in your source file. You can accomplish this by replacing the non-ASCII chars with a string escape using \u1234 or \U12345678. In your case, you'd use

NSString *strValue=@"\u4F60\u597D";

Of course, once you get your string constant to be correct, you can ditch the whole encoding stuff and just use strValue directly.

How to underline a UILabel in swift?

You can do this using NSAttributedString

Example:

let underlineAttribute = [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.thick.rawValue]
let underlineAttributedString = NSAttributedString(string: "StringWithUnderLine", attributes: underlineAttribute)
myLabel.attributedText = underlineAttributedString

EDIT

To have the same attributes for all texts of one UILabel, I suggest you to subclass UILabel and overriding text, like that:

Swift 5

Same as Swift 4.2 but: You should prefer the Swift initializer NSRange over the old NSMakeRange, you can shorten to .underlineStyle and linebreaks improve readibility for long method calls.

class UnderlinedLabel: UILabel {

override var text: String? {
didSet {
guard let text = text else { return }
let textRange = NSRange(location: 0, length: text.count)
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttribute(.underlineStyle,
value: NSUnderlineStyle.single.rawValue,
range: textRange)
// Add other attributes if needed
self.attributedText = attributedText
}
}
}

Swift 4.2

class UnderlinedLabel: UILabel {

override var text: String? {
didSet {
guard let text = text else { return }
let textRange = NSMakeRange(0, text.count)
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttribute(NSAttributedString.Key.underlineStyle , value: NSUnderlineStyle.single.rawValue, range: textRange)
// Add other attributes if needed
self.attributedText = attributedText
}
}
}

Swift 3.0

class UnderlinedLabel: UILabel {

override var text: String? {
didSet {
guard let text = text else { return }
let textRange = NSMakeRange(0, text.characters.count)
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttribute(NSUnderlineStyleAttributeName , value: NSUnderlineStyle.styleSingle.rawValue, range: textRange)
// Add other attributes if needed
self.attributedText = attributedText
}
}
}

And you put your text like this :

@IBOutlet weak var label: UnderlinedLabel!

override func viewDidLoad() {
super.viewDidLoad()

label.text = "StringWithUnderLine"
}

OLD:

Swift (2.0 to 2.3):

class UnderlinedLabel: UILabel {

override var text: String? {
didSet {
guard let text = text else { return }
let textRange = NSMakeRange(0, text.characters.count)
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttribute(NSUnderlineStyleAttributeName, value:NSUnderlineStyle.StyleSingle.rawValue, range: textRange)
// Add other attributes if needed

self.attributedText = attributedText
}
}
}

Swift 1.2:

class UnderlinedLabel: UILabel {

override var text: String! {
didSet {
let textRange = NSMakeRange(0, count(text))
let attributedText = NSMutableAttributedString(string: text)
attributedText.addAttribute(NSUnderlineStyleAttributeName, value:NSUnderlineStyle.StyleSingle.rawValue, range: textRange)
// Add other attributes if needed

self.attributedText = attributedText
}
}
}


Related Topics



Leave a reply



Submit