使用我的Swift国际化字符串只有一个运算符定义

时间:2017-07-17 13:08:18

标签: generics swift3 autocomplete localization operators

在我过去几年使用的所有框架中,我一直对代码中的本地化读取方式感到不满。定义一个"键"或者过于复杂。 (如在Swing中)或​​感觉太脆(如在Rails中)。此外,工具支持(尤其是自动完成等)总是有很多不足之处。

所以我开始看看我能在Swift中实现目标的程度。我大致试图实现以下目标:

  • 我的代码中的编译时检查了键常量,而不是使用字符串参数或甚至宏
  • 的过程调用
  • 能够在保持编译时安全的同时以最小的语法定义新键,
  • 能够从不同的.strings文件中获取本地化,而不仅仅是Localizable.strings
  • 能够以最小的语法开销调用localisable字符串,同时在Xcode中维护自动完成
  • 允许表示现有转换表(可能在其键中包含空格)

以下是我提出的内容(在Playground中,所以我只发出NSLocalizedString次调用,而不是在代码中实际调用它。)

import UIKit
import Foundation

protocol Localizable {
    var key : String { get }
    func localized() -> String
}

extension Localizable {
    func localized() -> String {
        return "NSLocalizedString(\(key))"
    }
}

prefix operator §

enum Default : String, Localizable {
    case hello
    case world = "this planet"

    var key : String { get { return rawValue } }

    static prefix func §(str: Default) -> String {
        return str.localized()
    }
}

Default.hello.localized()
§.hello
§.world

适用于Localizable.strings并对其他文件中的字符串进行如下扩展:

protocol TabLocalizable : Localizable {
    var table: String { get }
}

extension TabLocalizable {
    func localized() -> String {
        return "NSLocalizedString(\(key), table:\(table))"
    }
}

enum WorldTable : String, TabLocalizable {
    case earth
    case proximaCentauriB = "Proxima Centauri B"

    var key : String { get { return rawValue } }
    var table : String { get { return "worldTable" } }

    static prefix func §(str: WorldTable) -> String {
        return str.localized()
    }
}

WorldTable.proximaCentauriB.localized()
§.earth
§.proximaCentauriB

我定义keys的主要方式是enums,因为它们显然需要最少量的语法来定义新密钥。一个简单的

case newKey

就足够了,您唯一需要的就是.strings文件中的相应定义。对于您的密钥中可能包含空格的现有.strings文件,您必须有点冗长,但

case spacedKey = "Spaced Key"
我认为

并不算太糟糕。定义键很容易,并使用值

Default.world.localized()

仍感觉不如NSLocalizedString("world“)那么脆弱。但是,它并不能完全满足我对“最小化”的定义。事实证明,我们可以通过使用前缀运算符来进一步缩短代码。因为这些必须从一个"奇怪的"我选择使用§作为我的操作符,因为它在语言中似乎没有特定的用途,尽管它在大多数键盘布局上都很容易获得,它暗示"与语言有关" 34。

这导致

§.world

作为本地化的字符串引用,在简洁方面很难被击败。所以现在我所有的密钥都经过了编译时间测试,自动完成功能可以找到现有密钥。

但是我对此有一点小问题:为了使自动完成工作,我必须将(几乎相同的)运算符定义放入每个enum定义中。这似乎是一个很小的代价,它可能是,但我仍然想问一下是否有人知道只需要定义一次。

static prefix func §(str: Localizable) -> String {
    return str.localized()
}

进入Localizable协议的定义起初看起来是个好主意,但是

§.hello

将失败

error: type 'Localizable' has no member 'hello'

甚至是电话

§Default.hello

将失败

error: generic parameter 'Self' could not be inferred

这指向了我可能会'是一种在正确的位置使用聪明的prefix func §参数定义Self的方法,但我看不出它是如何完成的。我希望Swift仍然有一些微妙之处,允许这种类型的"泛型运算符"以某种方式。

如果你在某些方面找到更多的通用性,那么如果你在评论中指出这一点会更好。

0 个答案:

没有答案