ios本地化_本地化101 iOS与Android

ios本地化

Localization is the process of making your app support multiple languages.

本地化是使您的应用支持多种语言的过程。

The first language handled by your app is more likely the primary language of the market you are trying to get in.

您的应用处理的第一语言很可能是您尝试进入的市场的主要语言。

There are many cases where you might be motivated to localize your app, here are a few:

在很多情况下,您可能会被激励去本地化应用程序,以下是一些示例:

  • You want to make your app available for another market (eg: French market), therefore you will need to add French as supported language on your app

    您想使您的应用程序可用于其他市场 (例如:法语市场),因此您需要在应用程序中添加法语作为支持的语言

  • You want to make you app available for others languages spoken in your country.

    您想使您的应用程序可用于您所在国家/地区使用的其他语言

  • In the US, you might want to add Spanish as other supported language

    在美国,您可能想将西班牙语添加为其他受支持的语言
  • In Switzerland, it may also be Italian, German and French.

    在瑞士,也可能是意大利语,德语和法语。

This is also valuable for your business goals. The more languages your app handle, the more users you can attract.

这对于您的业务目标也很有价值。 您的应用处理的语言越多,您吸引的用户越多。

iOS和Android的感觉如何 (What is it like for iOS and Android)

Well, The process of localizing your app is a bit tedious. 😑

好吧,本地化应用程序的过程有点繁琐。 😑

So the sooner you start localizing your app, the easier/faster it will be ready when the business needs it!

因此,您越早开始本地化应用程序,就越容易/更快地在业务需要时准备就绪!

iOS本地化 (Localization for iOS)

Fortunately or unfortunately, not a lot has changed with iOS since its beginning.

幸运的是,自iOS诞生以来,它并没有发生太大变化。

(Yeah back Objective-C non-ARC time kind of beginning 👴🏻)

(是的,返回Objective-C非ARC时间有点kind)

For iOS, localized content are located in two separate type of file:

对于iOS ,本地化内容位于两种单独的文件类型中:

  • File extending with strings will gather single strings and formats.

    使用字符串扩展的文件将收集单个字符串和格式。

  • File extending with stringsdict will gather plurals/contextualized variables. (Technically an Apple version of xml)

    使用stringdict扩展的文件将收集复数/上下文变量。 (技术上是xml的Apple版本)

Image for post

Android的本地化 (Localization for Android)

For Android, it’s in a xml format which can contains both plurals and single strings.

对于Android ,它采用xml格式,可以同时包含复数形式和单个字符串。

Image for post

Android is actually a bit smoother than iOS (and that’s coming from an iOS guy uh).

Android实际上比iOS更加流畅 (这是来自iOS家伙)。

Why? Android build system automatically generate helper functions/variables to access the localized resources strings via R.

为什么? Android构建系统会自动生成辅助函数/变量以通过R访问本地化资源字符串。

At contrary to iOS, where there are no code generation for the resources. You have to do it yourself. SwiftGen can generate those resources on iOS for you.

与iOS相反​​,iOS没有为资源生成代码。 你必须自己做。 SwiftGen可以在iOS上为您生成这些资源。

演示版 (Demo)

让我们从一个例子开始 (Let’s start with an example)

I have a screen I want to translate to French.

我有一个屏幕想要翻译成法语。

Image for post

Just by looking at the design, I can clearly there are about 4 texts.A title, a description, a call to action button text and a dismiss button text.

仅看一下设计,我就能清楚地看到大约4个文本,分别是标题,描述,号召性用语和关闭按钮。

Title: “Playlist of the day”Description: “Hello Morgan, there are 42 of your favorite songs in the playlist Party2020. Would you like to listen to it?”

标题: “每日播放列表” 说明 :“ Hello Morgan,Party2020播放列表中有42首您喜欢的歌曲。 您想听吗?”

We can can see the content has few parameters:

我们可以看到内容几个参数

  • username

    用户名
  • number of songs (plural)

    歌曲数(复数)
  • playlist name

    播放清单名称

This end up a format like such:

最终的格式如下:

  • “Hello [username], there are [number of songs] in the playlist [playlist name]. Would you like to listen to it?”

    “您好[用户名] ,播放列表[播放列表名称]中[歌曲数 ] 。 您想听吗?”

Call to action: “Listen”Call to action dismissing: “dismiss”

号召性用语: “听”取消号召性用语: “解雇”

现在,让我们创建这些翻译文件 (Now, let’s create those translation files)

的iOS (iOS)

For iOS you will need to create two directories within your project where the resources files will be located per language. In this example, my default language is English (identifier: “en”).

对于iOS,您需要在项目中创建两个目录,每种语言将在其中找到资源文件。 在此示例中,我的默认语言是英语(标识符:“ en”)。

The directory should be composed such as: [Language identifier].lprojSo in our case, it would look like this:

该目录的组成应为: [语言标识符] .lproj因此,在我们的例子中,它看起来像这样:

  • en.lproj/

    en.lproj /

  • fr.lproj/

    fr.lproj /

This can be located anywhere in your project. I personally put it under a directory named localization.

它可以位于项目中的任何位置。 我个人将其放在名为localization的目录下。

The first file I’m creating is the .strings file, I will put in there 3 of my texts and one format string for the 4th text.

我创建的第一个文件是.strings文件,我将在其中放置3个文本,并为第4个文本输入一个格式字符串。

en.lproj / Popup.strings (en.lproj/Popup.strings)

// Singles
"popup.title" = "Playlist of the day";
"popup.cta" = "Listen";
"popup.cta_dismiss" = "dismiss";
// Formats
"popup.description" = "Hello %@, there are %@ in the playlist %@. Would you like to listen to it?";

fr.lproj / Popup.strings (fr.lproj/Popup.strings)

// Singles
"popup.title" = "Playlist du jour";
"popup.cta" = "Écouter";
"popup.cta_dismiss" = "retour";
// Formats
"popup.description" = "Bonjour %@, la playlist %@ contient %@. Souhaitez-vous l'écouter?";

The .stringsdict file will have the plural variable. In this case, the variable numberOfSongs. This is an integer (d) variable, where I preset the values for one and other cases. Other will be used if the content should be pluralized else, it will choose one since it’s singular. (this is the case for English)

.stringsdict文件将具有复数变量。 在这种情况下,变量numberOfSongs 。 这是一个整数(d)变量,在其中我为一种其他情况预设了值。 如果内容应该是复数形式,则将使用“其他”,否则将选择一个,因为它是单数形式。 (英语就是这种情况)

zh.lproj / Popup.stringsdict (en.lproj/Popup.stringsdict)

<plist version="1.0">
<dict>
    <key>popup.description.numberOfSongs</key>
    <dict>
        <key>NSStringLocalizedFormatKey</key>
        <string>%#@numberOfSongs@</string>
        <key>numberOfSongs</key>
        <dict>
            <key>NSStringFormatSpecTypeKey</key>
            <string>NSStringPluralRuleType</string>
            <key>NSStringFormatValueTypeKey</key>
            <string>d</string>
            <key>one</key>
            <string>one of your favorite song</string>
            <key>other</key>
            <string>%d of your favorite songs</string>
        </dict>
    </dict>
</dict>
</plist>

fr.lproj / Popup.stringsdict (fr.lproj/Popup.stringsdict)

<plist version="1.0">
<dict>
    <key>popup.description.numberOfSongs</key>
    <dict>
        <key>NSStringLocalizedFormatKey</key>
        <string>%#@numberOfSongs@</string>
        <key>numberOfSongs</key>
        <dict>
            <key>NSStringFormatSpecTypeKey</key>
            <string>NSStringPluralRuleType</string>
            <key>NSStringFormatValueTypeKey</key>
            <string>d</string>
            <key>one</key>
            <string>%d chanson favorite</string>
            <key>other</key>
            <string>%d chansons favorites</string>
        </dict>
    </dict>
</dict>
</plist>

安卓系统 (Android)

For Android, you will need to add your resource files under the directory src/{module}/res.

对于Android,您需要将资源文件添加到目录src / {module} / res下

Watch-out for single quotes or special character, they will need to escaped.

当心单引号或特殊字符,他们将需要 逃脱了

The single strings are define under a <string> xml wrapper. Plurals are under <plurals>

单个字符串在<string> xml包装器下定义。 复数<复数>下

src / main / res / values-en / popup.xml (src/main/res/values-en/popup.xml)

<resources>
    <string name="popup_title">Playlist of the day</string>
    <string name="popup_cta">Listen</string>
    <string name="popup_cta_dismiss">dismiss</string>
    <plurals name="popup_description_numberofsongs">
        <item quantity="one">one of your favorite song</item>
        <item quantity="other">%d of your favorite songs</item>
    </plurals>
    <string name="popup_description">Hello %s, there are %s in the playlist %s. Would you like to listen to it?</string>
</resources>

src / main / res / values-fr / popup.xml (src/main/res/values-fr/popup.xml)

<resources>
    <string name="popup_title">Playlist du jour</string>
    <string name="popup_cta">Écouter</string>
    <string name="popup_cta_dismiss">retour</string>
    <string name="popup_description_zero">aucune chanson favorite</string>
    <plurals name="popup_description_numberofsongs">
        <item quantity="one">%d chanson favorite</item>
        <item quantity="other">%d chansons favorite</item>
    </plurals>
    <string name="popup_description">Bonjour %s, la playlist %s contient %s. Souhaitez-vous l\'écouter?</string>
</resources>

现在我们如何实现本地化内容? (Now how do we implement the localized content?)

First, let’s create an helper struct to help us do the heavy-lifting to get the localizations on iOS.

首先,让我们创建一个辅助结构,以帮助我们进行繁重的工作来获取iOS上的本地化版本。

// Dumb class to be accessible for Bundle search
private class PopupLocalized { }


struct PopupStrings {
    // File name where the localized strings are located
    static let tableName: String = "Popup"


    static func localize(_ key: String) -> String {
        return NSLocalizedString(key, tableName: tableName, bundle: Bundle(for: PopupLocalized.self), comment: "")
    }


    static func localizeParameter(key: Keys, parameter: SubKeys, value: CVarArg) -> String {
        let format: String = localize(key.withParameter(parameter))
        return String.localizedStringWithFormat(format, value)
    }


    enum Keys: String {
        case ctaDismiss = "popup.cta_dismiss"
        case title = "popup.title"
        case description = "popup.description"
        case cta = "popup.cta"
    
        func withParameter(_ parameter: SubKeys) -> String {
            return [rawValue, parameter.rawValue].joined(separator: ".")
        }
    }


    enum SubKeys: String {
        case numberOfSongs
    }


    static let title: String = localize(Keys.title.rawValue)
    static func description(userName: String, numberOfSongs: Int, playlistName: String) -> String {
        let format: String = localize(Keys.description.rawValue)
        let numberOfSongsResult: String = localizeParameter(key: Keys.description, parameter: SubKeys.numberOfSongs, value: numberOfSongs)
        let result: String = String.localizedStringWithFormat(format, userName, numberOfSongsResult, playlistName)
        return result
    }


    static let cta: String = localize(Keys.cta.rawValue)
    static let ctaDismiss: String = localize(Keys.ctaDismiss.rawValue)
}

Then the implementation in the view:

然后在视图中执行:

@IBOutlet private weak var containerView: UIView!
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var descriptionLabel: UILabel!
@IBOutlet private weak var ctaButton: UIButton!
@IBOutlet private weak var dismissButton: UIButton!


override func viewDidLoad() {
    super.viewDidLoad()
    configureView()
}


func configureViewText() {
    let name = "Morgan"
    let numberOfSongs = 42
    let playlistName = "Party2020"


    titleLabel.text = PopupStrings.title
    descriptionLabel.text = PopupStrings.description(userName: name, numberOfSongs: numberOfSongs, playlistName: playlistName)
    ctaButton.setTitle(PopupStrings.cta, for: .normal)
    dismissButton.setTitle(PopupStrings.ctaDismiss, for: .normal)
}

I used the function NSLocalizedString(_ key: String, tableName: String, bundle: Bundle, comment: String) for a very specific reason.

我出于特殊原因使用了函数NSLocalizedString(_ key:String,tableName:String,bundle:Bundle,comment:String)

Your code might move into a different location than the main project (eg: framework / resource bundle), using this function will give you less or no refactoring to do to when you decide to move those strings away. As long as the swift file is in the same bundle of the resources used, it will find them.

您的代码可能会移到与主项目不同的位置(例如:框架/资源包),使用此功能将使您在决定将这些字符串移开时没有或很少需要进行重构。 只要swift文件位于所用资源的同一捆绑中,它将找到它们。

On Android, it’s pretty straightforward and you’ll be able to use the resources if its shared with your component (aka module).

Android上 ,这非常简单,如果资源与您的组件(即模块)共享,则可以使用这些资源。

val title = resources.getString(R.string.popup_title)


// dummy values
val numberOfSongs = 42
val username = "Morgan"
val playlistName = "Party2020"


/* 
  The first numberOfSongs act as qualifier item (will select the variants based on the quantity passed) 
  The second as parameter will be to print if there is a %d in the variant string (see other option)
*/
val numberOfSongsString = resources.getQuantityString(R.plurals.popup_description_numberofsongs, numberOfSongs, numberOfSongs)


val combined = resources.getString(R.string.popup_description, username, numberOfSongsString, playlistName)

R is generated via the build system. It allows you to access resources in your project.

R是通过构建系统生成的。 它允许您访问项目中的资源。

现在,让我们尝试将语言从英语更改为法语 (Now, let’s try to change the language from English to French)

On iOS, this can be done pretty quickly by editing your app scheme:

在iOS上,可以通过编辑您的应用方案来快速完成此操作:

  • Edit Scheme > Run > Options

    编辑方案>运行>选项

Modify the value of Application Language to French.

应用程序语言的值修改为法语。

Image for post

Android, you have two possibilities to see your app with a different language.

Android ,您有两种可能性可以用其他语言查看您的应用。

  • The first possibility would be to use the preview panel to see one or multiple views with the language you select on the dropdown menu.

    第一种可能性是使用预览面板以您在下拉菜单上选择的语言查看一个或多个视图。

  • The second possibility would be to change the language on the emulator directly. After changing language, if you launch back the app, it will automatically update with the newly selected language if supported by your project of course.

    第二种可能性是直接在模拟器上更改语言 。 更改语言后,如果您重新启动该应用程序,则当然会在您的项目支持的情况下以新选择的语言自动更新。

五月天,我们遇到了一个问题先生! (Mayday, we got a problem monsieur!)

Image for post
Image for post

Pardon my French, but something doesn’t look right.It looks like our French translation got mixed up in translation.

请原谅我的法语,但看起来有些不对劲。看来我们的法语翻译在翻译中有些混乱

The parameter orders in the French format is different than the English one.

法语格式的参数顺序不同于英语格式。

  • English format order: 1. userName, 2. numberOfSongs, 3. playlistName

    英文格式顺序 :1. userName,2. numberOfSongs,3. playlistName

  • French format order: 1. userName, 2. playlistName, 3. numberOfSongs

    法语格式顺序 :1. userName,2. playlistName,3. numberOfSongs

The code gives the order as below:

代码给出的顺序如下:

  1. userName

    用户名

  2. numberOfSong

    歌曲数

  3. playlistName

    播放清单名称

To fix this issue, it’s actually easy. You need to tell the order of the parameters within the format.

要解决此问题,实际上很容易。 您需要告知格式中参数的顺序。

的iOS (iOS)

en.lproj / Popup.strings (en.lproj/Popup.strings)

"popup.description" = "Hello %1$@, there are %2$@ in the playlist %3$@. Would you like to listen to it?";

fr.lproj / Popup.strings (fr.lproj/Popup.strings)

"popup.description" = "Bonjour %1$@, la playlist %3$@ contient %2$@. Souhaitez-vous l'écouter?";

安卓系统 (Android)

values-zh-CN / popup.xml (values-en/popup.xml)

<string name="popup_description">Hello %1$s, there are %2$s in the playlist %3$s. Would you like to listen to it?</string>

values-fr / popup.xml (values-fr/popup.xml)

<string name="popup_description">Bonjour %1$s, la playlist %3$s contient %2$s. Souhaitez-vous l\'écouter?</string>

瞧! (Voila!)

The content is now localized in French and ordered correctly.

现在,该内容已以法语进行了本地化并正确排序。

Image for post
Image for post

结论 (Conclusion)

It is up to you to start preparing your app to be localized in the eventuality of supporting multiple languages. Something I would recommend when starting a new project is to always put your contents into your system language files (eg: strings / stringsdict / xml), so later when you have to localize for another languages, you will only need to add the system language files for the new language you want to support.

您可以自行决定是否准备支持多种语言,以准备将其本地化。 我建议在启动新项目时始终将内容放入系统语言文件中(例如:strings / stringsdict / xml) ,因此以后当您必须本地化为其他语言时,只需添加系统语言您要支持的新语言的文件。

The contrary would add you a lot of refactoring work you sincerely don’t want.

相反,这将为您增加很多您不希望的重构工作。

Time is 💰

时间是💰

注意事项 (Things to watch out)

本地化期间很少 遵循以下 规则 (Few rules are also to follow during localization:)

Never use business logic to dictate the plural strings.

切勿使用业务逻辑来规定复数字符串

For example, your user has a list of products. You may want to show a specific text if the users has no account, one, multiple…

例如,您的用户有一个产品列表。 如果用户没有帐户,一个或多个,则可能要显示特定的文本。

  • zero: “Open an account”

    零:“开设帐户”
  • other: “Open another account”

    其他:“开设另一个帐户”

This works with iOS (surprisingly), not for Android due to CLDR Unicode language restrictions.

由于CLDR Unicode语言的限制,它可以在iOS上运行(出人意料),不适用于Android

  • In the case of English on Android, zero variants are discarded due to be same has many. Other is then took as default.

    在Android上为英文的情况下,由于有很多相同的变量,因此会丢弃零个变量。 然后将其他作为默认值。
  • In the case of French on Android, zero variants are replaced by the one variant if passed since they are the same in French.

    对于Android上的法语,如果通过,则零变种将被一个变种替换,因为它们在法语中是相同的。

Avoid static data representation in your strings.

避免在字符串中使用静态数据表示

Example, you have a single string like this: “Register for only $5.99 per month”

例如,您有一个这样的字符串:“每月仅注册$ 5.99”

You may want to have the amount portion to be a parameter since the formatting of the price could change based on different countries.

您可能希望将金额部分作为参数,因为价格格式可能会因国家/地区而异。

  • US: “$5.99”

    美国:“ $ 5.99”
  • FR: “5,99€”

    FR:“ 5,99€”
  • UK: “£5.99”

    英国:“ 5.99英镑”

Same idea for dates in a text like this: “Register by 10/21/2020”

在这样的文本中对日期也有相同的想法:“在10/21/2020前注册”

Like the price, date formatting is also depending on the location.

像价格一样,日期格式也取决于位置。

  • US: “10/21/2020”

    美国:“ 10/21/2020”
  • FR: “21/10/2020”

    FR:“ 21/10/2020”
  • UK: “21/10/2020”

    英国:“ 21/10/2020”

The process to format those components into their proper locale is called Internationalization. (i18n)

将这些组件格式化为适当语言环境的过程称为国际化 。 (i18n)

词汇表 (Glossary)

Android:

Android:

iOS:

iOS:

Check out the sample project on my Github.

在我的Github上查看示例项目

If you have any questions, feel free to let me know in the comments or on Twitter!

如果您有任何疑问,请随时在评论中或Twitter上告诉我!

翻译自: https://medium.com/swlh/localization-101-ios-vs-android-150de23a3af3

ios本地化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值