4

这是一个场景:

  1. 我使用 NSLocalizedString 编写了一个 iPhone 应用程序,以防我决定在不同的国家发布它。
  2. 我决定在法国发布应用程序。
  3. 翻译人员接受了我的翻译,Localized.strings并且做得很好
  4. 我更新了应用程序,需要更多的翻译。

我正在使用 genstrings,它覆盖了翻译人员所做的出色工作,有没有一种简单的方法可以让我通过 App 版本管理我的翻译?

4

5 回答 5

5

在 GitHub 上查看这个项目,它提供了一个genstrings更智能的 python 脚本。

由于我不喜欢仅链接的答案(链接可能会消失),因此我还将把 python 脚本放在这里(所有学分都归链接项目的作者所有)

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Localize.py - Incremental localization on XCode projects
# João Moreno 2009
# http://joaomoreno.com/

# Modified by Steve Streeting 2010 http://www.stevestreeting.com
# Changes
# - Use .strings files encoded as UTF-8
#   This is useful because Mercurial and Git treat UTF-16 as binary and can't
#   diff/merge them. For use on iPhone you can run an iconv script during build to
#   convert back to UTF-16 (Mac OS X will happily use UTF-8 .strings files).
# - Clean up .old and .new files once we're done

# Modified by Pierre Dulac 2012 http://friendcashapp.com
# Changes
# - use logging instead of print
# Adds
# - MIT Licence
# - the first parameter in the command line to specify the path of *.lproj directories
# - an optional paramter to control the debug level (set to info by default)
# Fixes
# - do not convert a file if it is already in utf-8
# - allow multiline translations generated by genstrings by modifing the re_translation regex
# - 

# MIT Licence
#
# Copyright (C) 2012 Pierre Dulac
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
# associated documentation files (the "Software"), to deal in the Software without restriction, 
# including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substantial 
# portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from sys import argv
from codecs import open
from re import compile
from copy import copy
import os
import shutil
import optparse
import logging
logging.getLogger().level = logging.INFO

__version__ = "0.1"
__license__ = "MIT"

USAGE = "%prog [options] <url>"
VERSION = "%prog v" + __version__

re_translation = compile(r'^"((?:[^"]|\\")+)" = "((?:[^"]|\\")+)";(?:\n)?$')
re_comment_single = compile(r'^/\*.*\*/$')
re_comment_start = compile(r'^/\*.*$')
re_comment_end = compile(r'^.*\*/$')

class LocalizedString():
    def __init__(self, comments, translation):
        self.comments, self.translation = comments, translation
        self.key, self.value = re_translation.match(self.translation).groups()

    def __unicode__(self):
        return u'%s%s\n' % (u''.join(self.comments), self.translation)

class LocalizedFile():
    def __init__(self, fname=None, auto_read=False):
        self.fname = fname
        self.reset()

        if auto_read:
            self.read_from_file(fname)

    def reset(self):
        self.strings = []
        self.strings_d = {}

    def read_from_file(self, fname=None):
        self.reset()

        fname = self.fname if fname == None else fname
        try:
            #f = open(fname, encoding='utf_8', mode='r')
            f = open(fname, encoding='utf_8', mode='r')
        except:
            print 'File %s does not exist.' % fname
            exit(-1)

        try:
            line = f.readline()
            logging.debug(line)
        except:
            logging.error("Can't read line for file: %s" % fname)
            raise

        i = 1
        while line:
            comments = [line]

            if not re_comment_single.match(line):
                while line and not re_comment_end.match(line):
                    line = f.readline()
                    comments.append(line)

            line = f.readline()
            i += 1

            # handle multi lines
            while len(line) > 1 and line[-2] != u';':
                line += f.readline()
                i += 1

            logging.debug("%d %s" % (i, line.rstrip('\n')))
            if line and re_translation.match(line):
                translation = line
            else:
                logging.error("Line %d of file '%s' raising the exception: %s" % (i, self.fname, line))
                raise Exception('invalid file')

            line = f.readline()
            i += 1
            while line and line == u'\n':
                line = f.readline()
                i += 1

            string = LocalizedString(comments, translation)
            self.strings.append(string)
            self.strings_d[string.key] = string

        f.close()

    def save_to_file(self, fname=None):
        fname = self.fname if fname == None else fname
        try:
            f = open(fname, encoding='utf_8', mode='w')
        except:
            print 'Couldn\'t open file %s.' % fname
            exit(-1)

        # sort by key
        self.strings.sort(key=lambda item: item.key)

        for string in self.strings:
            f.write(string.__unicode__())

        f.close()

    def merge_with(self, new):
        merged = LocalizedFile()

        for string in new.strings:
            if self.strings_d.has_key(string.key):
                new_string = copy(self.strings_d[string.key])
                new_string.comments = string.comments
                string = new_string

            merged.strings.append(string)
            merged.strings_d[string.key] = string

        return merged

    def update_with(self, new):
        for string in new.strings:
            if not self.strings_d.has_key(string.key):
                self.strings.append(string)
                self.strings_d[string.key] = string

def merge(merged_fname, old_fname, new_fname):
    try:
        old = LocalizedFile(old_fname, auto_read=True)
        new = LocalizedFile(new_fname, auto_read=True)
        merged = old.merge_with(new)
        merged.save_to_file(merged_fname)
    except Exception, inst:
        logging.error('Error: input files have invalid format.')
        raise

STRINGS_FILE = 'Localizable.strings'

def localize(path, excluded_paths):
    languages = [os.path.join(path,name) for name in os.listdir(path) if name.endswith('.lproj') and os.path.isdir(os.path.join(path,name))]
    print "languages found", languages

    for language in languages:
        original = merged = language + os.path.sep + STRINGS_FILE
        old = original + '.old'
        new = original + '.new'

        if os.path.isfile(original):
            try:
                open(original, encoding='utf_8', mode='r').read()
                os.rename(original, old)
            except:
                os.system('iconv -f UTF-16 -t UTF-8 "%s" > "%s"' % (original, old))

            # gen
            os.system('find %s -name \*.m -not -path "%s" | xargs genstrings -q -o "%s"' % (path, excluded_paths, language))

            try:
                open(original, encoding='utf_8', mode='r').read()
                shutil.copy(original, new)
            except:
                os.system('iconv -f UTF-16 -t UTF-8 "%s" > "%s"' % (original, new))

            # merge  
            merge(merged, old, new)
            logging.info("Job done for language: %s" % language)
        else:
            os.system('genstrings -q -o "%s" `find %s -name "*.m" -not -path "%s"`' % (language, path, excluded_paths))
            os.rename(original, old)
            try:
                open(old, encoding='utf_8', mode='r').read()
            except:
                os.system('iconv -f UTF-16 -t UTF-8 "%s" > "%s"' % (old, original))

        if os.path.isfile(old):
            os.remove(old)
        if os.path.isfile(new):
            os.remove(new)

def parse_options():
    """parse_options() -> opts, args

    Parse any command-line options given returning both
    the parsed options and arguments.
    """

    parser = optparse.OptionParser(usage=USAGE, version=VERSION)

    parser.add_option("-d", "--debug",
            action="store_true", default=False, dest="debug",
            help="Set to DEBUG the logging level (default to INFO)")

    parser.add_option("-p", "--path",
            action="store", type="str", default=os.getcwd(), dest="path",
            help="Path (relative or absolute) to use for searching for *.lproj directories")

    parser.add_option("-e", "--exclude",
            action="store", type="str", default=None, dest="excluded_paths",
            help="Regex for paths to exclude ex. ``./Folder1/*``")

    opts, args = parser.parse_args()
    return opts, args

if __name__ == '__main__':
    opts, args = parse_options()
    if opts.debug:
        logging.getLogger().level = logging.DEBUG
    if opts.path:
        opts.path = os.path.realpath(opts.path)
    if opts.excluded_paths:
        opts.excluded_paths = os.path.realpath(opts.excluded_paths)
    logging.info("Running the script on path %s" % opts.path)
    localize(opts.path, opts.excluded_paths)
于 2013-06-27T11:35:08.717 回答
3

我用:

http://www.loc-suite.com

只翻译新的部分

于 2013-06-27T12:08:53.120 回答
0

我实际上开始使用一个名为 PhraseApp https://phraseapp.com/projects的工具

如果您必须本地化应用程序,则值得研究!

于 2015-06-03T13:17:36.983 回答
0

我遇到了类似的问题。我为我的 NSLocalizedString 宏更改了很多键,并且害怕我会运送缺少翻译的应用程序(不想手动运行整个应用程序并检查是否一切都在那里......)。

我尝试了 Gabriella Petronella 发布的 github 项目,但我对它并不满意,所以我编写了自己的 python 模块来完成我想做的事情。(我不会在这里发布代码,因为它是一个完整的模块,而不仅仅是一个脚本:D)

于 2015-10-07T12:30:21.950 回答
0

以下是您可以选择的几个选项:

  • 您可以使用一些像上面提到的脚本这样的手写解决方案,它不会完全重写旧文件,同时向它们添加最近翻译的字符串。
  • 您还可以创建一个额外的 strings.h 文件,该文件将包含您拥有的所有字符串,因此您无需一直重写它们,只需在一个地方即可。所以不再需要genstrings了。然而,使用它有一个缺点:string.h 文件将是非结构化的,这对于大型项目可能不方便。

感谢使用 NSLocalizedString 的最佳实践

// In strings.h

 #define YOUR_STRING_KEY NSLocalizedString(@"Cancel", nil)

// Somewhere else in you code

NSLog(@"%@", YOUR_STRING_KEY);
于 2017-04-27T14:08:43.040 回答