2
\$\begingroup\$

I've written and used this code in my apps whenever needed to convert any Western Arabic numerals into Eastern Arabic (or better say, Persian/Farsi) numerals in any Persian text.

It works fine as expected, but I'm having a look at it now to reuse it, and I'm wondering if I could optimize this to reduced the repeated lines of code, and if so, how?

The header file, FarsiNumerals.h:


@interface FarsiNumerals : NSObject

+(NSString *)convertNumeralsToFarsi:(NSString *)englishNumeralString;

@end

and the implementation file, FarsiNumerals.m:


@implementation FarsiNumerals

+(NSString *)convertNumeralsToFarsi:(NSString *)englishNumeralString
{
    NSString *farsiNumeral;
    farsiNumeral = [englishNumeralString stringByReplacingOccurrencesOfString:@"1" withString:@"۱"];
    farsiNumeral = [farsiNumeral stringByReplacingOccurrencesOfString:@"2" withString:@"۲"];
    farsiNumeral = [farsiNumeral stringByReplacingOccurrencesOfString:@"3" withString:@"۳"];
    farsiNumeral = [farsiNumeral stringByReplacingOccurrencesOfString:@"4" withString:@"۴"];
    farsiNumeral = [farsiNumeral stringByReplacingOccurrencesOfString:@"5" withString:@"۵"];
    farsiNumeral = [farsiNumeral stringByReplacingOccurrencesOfString:@"6" withString:@"۶"];
    farsiNumeral = [farsiNumeral stringByReplacingOccurrencesOfString:@"7" withString:@"۷"];
    farsiNumeral = [farsiNumeral stringByReplacingOccurrencesOfString:@"8" withString:@"۸"];
    farsiNumeral = [farsiNumeral stringByReplacingOccurrencesOfString:@"9" withString:@"۹"];
    farsiNumeral = [farsiNumeral stringByReplacingOccurrencesOfString:@"0" withString:@"۰"];
    return farsiNumeral;
}

@end

It's a simple piece of code as you see; I'd call the method wherever needed and it'd replace the numerals with the correct ones for me for optimized text display purposes, and I'd like to know if I can somehow make this even shorter?

\$\endgroup\$
4
  • \$\begingroup\$ Is englishNumeralString a string with a (single) number, or is it arbitrary text with some numbers embedded? It is not clear (to me) from your description “convert any Western Arabic numerals into Eastern Arabic ... numerals in any Persian text.” \$\endgroup\$
    – Martin R
    Jan 20, 2022 at 8:48
  • \$\begingroup\$ Concretely: Can the input string be something like “My phone number is 0049 1234 5678” or “The price for 10 items is 123 $”? \$\endgroup\$
    – Martin R
    Jan 20, 2022 at 9:11
  • \$\begingroup\$ Hey @MartinR. Thanks for the comments. So I had been using this after parsing and displaying text. So let's say I have a newspaper article, all in Persian, and then there are some numbers, similar to what you've mentioned in your comment below. Those numbers would appear in EN rather than FA. So this converter would convert those to FA within the string/text. So basically the text would be: "شماره تلفن من 00491234 5678" and the converter would make it look like this: "شماره تلفن من ۰۰۴۹ ۱۲۳۴ ۵۶۷۸" \$\endgroup\$
    – Neeku
    Jan 20, 2022 at 13:43
  • \$\begingroup\$ There's also the bidirectional issue as you see when the text is FA and numerals are EN, but when they're converted to FA, it retains the RTL without having to enforce RLM or so. \$\endgroup\$
    – Neeku
    Jan 20, 2022 at 13:44

2 Answers 2

1
\$\begingroup\$

Simple transliteration may be insufficient. For example, I understand that the American English 10,000.00 should be ۱۰٬۰۰۰٫۰۰, but you probably do not want to replace all , and . throughout the text, but rather only within the context of numbers. (And, if we shift to other languages, the problem becomes even more complicated, because they might not even use the grouping separator every three digits.)

You might want to write a routine that does a regex search for Latin numbers (e.g., matching 10,000.00 for example, not the individual characters), then use a number formatter to convert the English number, and another number formatter to convert it to Farsi.

This is how one would use a number formatter:

NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"fa"];

NSNumber *number = @(1234567890);
NSString *string = [formatter stringFromNumber:number]; // ۱۲۳۴۵۶۷۸۹۰

And, if you omit the locale altogether, it defaults to the user’s current locale (so Japanese users see Japanese, Chinese users see Chinese, etc.). This also handles thousandths and decimal separators unique to particular locales (e.g. with numberStyle of NSNumberFormatterDecimalStyle, for 1234.56, English users see “1,234.56” and German users see “1.234,56”.

FYI, you can change the numberStyle property to display it in other formats, too, e.g. NSNumberFormatterSpellOutStyle.

Formatters have all sorts of additional properties to control the format (e.g. minimum and maximum number of fraction digits, etc.). See the documentation for more information.

\$\endgroup\$
8
  • \$\begingroup\$ As I understand the question, the input string can be something like ”The price for 10 items is 123 $” (or a phone number with leading zeros) and the expectation is that only the digits are converted. But I may be completely wrong, so I have asked the author for clarification. \$\endgroup\$
    – Martin R
    Jan 20, 2022 at 8:59
  • 1
    \$\begingroup\$ I was going to suggest -[NSString stringByApplyingTransform], but I did not manage to create Farsi digits (۰۱۲۳۴۵۶۷۸۹, EXTENDED ARABIC-INDIC DIGIT nnn) with that method, only Arabic digits (٠١٢٣٤٥٦٧٨٩, ARABIC-INDIC DIGIT nnn). \$\endgroup\$
    – Martin R
    Jan 20, 2022 at 9:06
  • \$\begingroup\$ Looks like you can do it with a custom transform, e.g. [:Digit:]; fa;. \$\endgroup\$
    – Rob
    Jan 21, 2022 at 20:40
  • \$\begingroup\$ That's one of the things that I tried :) But (for example) "4" becomes "٤" (U+0664, ARABIC-INDIC DIGIT FOUR) and not "۴" (U+06F4, EXTENDED ARABIC-INDIC DIGIT FOUR) \$\endgroup\$
    – Martin R
    Jan 21, 2022 at 20:50
  • 1
    \$\begingroup\$ Yes, in my tests, NSNumberFormatter does it right, but stringByApplyingTransform does it wrong. Looks like a bug to me. \$\endgroup\$
    – Martin R
    Jan 21, 2022 at 21:04
0
\$\begingroup\$

One recommendation I would have for this case is perhaps some equivalent of a dictionary (or hash map) for each definition of translation between an arabic numeral and a farsi numeral.

Looping through each element in either of those from there would certainly make the code more concise, albeit, I'm not entirely sure whether it would reduce any used space.

In general, turning this simple matter of translation into a hash map could have the advantage of making it clear what the code is doing and how it works.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.