From 504f3548ac7b1523449029e52e238cdc1469ab84 Mon Sep 17 00:00:00 2001 From: greshilov Date: Fri, 30 Mar 2018 20:53:03 +0300 Subject: Add plural localizations --- lib/twine.rb | 1 + lib/twine/formatters/abstract.rb | 20 ++++++++++- lib/twine/formatters/android.rb | 7 ++++ lib/twine/formatters/apple.rb | 4 +++ lib/twine/formatters/apple_plural.rb | 68 ++++++++++++++++++++++++++++++++++++ lib/twine/output_processor.rb | 8 +++++ lib/twine/twine_file.rb | 35 ++++++++++++++++--- 7 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 lib/twine/formatters/apple_plural.rb diff --git a/lib/twine.rb b/lib/twine.rb index ab02f79..c08499b 100644 --- a/lib/twine.rb +++ b/lib/twine.rb @@ -31,6 +31,7 @@ module Twine require 'twine/formatters/abstract' require 'twine/formatters/android' require 'twine/formatters/apple' + require 'twine/formatters/apple_plural' require 'twine/formatters/django' require 'twine/formatters/flash' require 'twine/formatters/gettext' diff --git a/lib/twine/formatters/abstract.rb b/lib/twine/formatters/abstract.rb index 4bd8c09..37c5f15 100644 --- a/lib/twine/formatters/abstract.rb +++ b/lib/twine/formatters/abstract.rb @@ -3,6 +3,7 @@ require 'fileutils' module Twine module Formatters class Abstract + SUPPORTS_PLURAL = false attr_accessor :twine_file attr_accessor :options @@ -132,7 +133,13 @@ module Twine end def format_definition(definition, lang) - [format_comment(definition, lang), format_key_value(definition, lang)].compact.join + formatted_definition = [format_comment(definition, lang)] + if self.class::SUPPORTS_PLURAL && definition.is_plural? + formatted_definition << format_plural(definition, lang) + else + formatted_definition << format_key_value(definition, lang) + end + formatted_definition.compact.join end def format_comment(definition, lang) @@ -143,10 +150,21 @@ module Twine key_value_pattern % { key: format_key(definition.key.dup), value: format_value(value.dup) } end + def format_plural(definition, lang) + plural_hash = definition.plural_translation_for_lang(lang) + if plural_hash + format_plural_keys(definition.key.dup, plural_hash) + end + end + def key_value_pattern raise NotImplementedError.new("You must implement key_value_pattern in your formatter class.") end + def format_plural_keys(key, plural_hash) + raise NotImplementedError.new("You must implement format_plural_keys in your formatter class.") + end + def format_key(key) key end diff --git a/lib/twine/formatters/android.rb b/lib/twine/formatters/android.rb index e8f0642..5ad23ab 100644 --- a/lib/twine/formatters/android.rb +++ b/lib/twine/formatters/android.rb @@ -7,6 +7,7 @@ module Twine class Android < Abstract include Twine::Placeholders + SUPPORTS_PLURAL = true LANG_CODES = Hash[ 'zh' => 'zh-Hans', 'zh-CN' => 'zh-Hans', @@ -110,6 +111,12 @@ module Twine "\t%{value}" end + def format_plural_keys(key, plural_hash) + result = "\t\n" + result += plural_hash.map{|quantity,value| "\t#{' ' * 2}#{value}"}.join("\n") + result += "\n\t" + end + def gsub_unless(text, pattern, replacement) text.gsub(pattern) do |match| match_start_position = Regexp.last_match.offset(0)[0] diff --git a/lib/twine/formatters/apple.rb b/lib/twine/formatters/apple.rb index 5233cb1..5a655df 100644 --- a/lib/twine/formatters/apple.rb +++ b/lib/twine/formatters/apple.rb @@ -86,6 +86,10 @@ module Twine def format_value(value) escape_quotes(value) end + + def should_include_definition(definition, lang) + return !definition.is_plural? && super + end end end end diff --git a/lib/twine/formatters/apple_plural.rb b/lib/twine/formatters/apple_plural.rb new file mode 100644 index 0000000..c53f02b --- /dev/null +++ b/lib/twine/formatters/apple_plural.rb @@ -0,0 +1,68 @@ +module Twine + module Formatters + class ApplePlural < Apple + SUPPORTS_PLURAL = true + + def format_name + 'apple-plural' + end + + def extension + '.stringsdict' + end + + def default_file_name + 'Localizable.stringsdict' + end + + def format_footer(lang) + footer = "\n" + end + + def format_file(lang) + result = super + result += format_footer(lang) + end + + def format_header(lang) + header = "\n" + header += "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n" + header += "\n" + header += "\n" + end + + def format_section_header(section) + "\n" + end + + def format_plural_keys(key, plural_hash) + result = "#{tab(2)}#{key}\n" + result += "#{tab(2)}\n" + result += "#{tab(4)}NSStringLocalizedFormatKey\n#{tab(4)}\%\#@value@\n" + result += "#{tab(4)}value\n#{tab(4)}\n" + result += "#{tab(6)}NSStringFormatSpecTypeKey\n#{tab(6)}NSStringPluralRuleType\n" + result += "#{tab(6)}NSStringFormatValueTypeKey\n#{tab(6)}d\n" + result += plural_hash.map{|quantity,value| "#{tab(6)}#{quantity}\n#{tab(6)}#{value}"}.join("\n") + result += "\n#{tab(4)}\n#{tab(2)}\n" + end + + def format_comment(definition, lang) + "\n" if definition.comment + end + + def read(io, lang) + raise NotImplementedError.new("Reading \".stringdict\" files not implemented yet") + end + + def tab(level) + ' ' * level + end + + def should_include_definition(definition, lang) + return definition.is_plural? && definition.plural_translation_for_lang(lang) + end + end + end +end + +Twine::Formatters.formatters << Twine::Formatters::ApplePlural.new diff --git a/lib/twine/output_processor.rb b/lib/twine/output_processor.rb index 8924ad1..9d96d4c 100644 --- a/lib/twine/output_processor.rb +++ b/lib/twine/output_processor.rb @@ -42,6 +42,14 @@ module Twine new_definition = definition.dup new_definition.translations[language] = value + if definition.is_plural? + # If definition is plural, but no translation found -> create + # Then check 'other' key + if !(new_definition.plural_translations[language] ||= {}).key? 'other' + new_definition.plural_translations[language]['other'] = value + end + end + new_section.definitions << new_definition result.definitions_by_key[new_definition.key] = new_definition end diff --git a/lib/twine/twine_file.rb b/lib/twine/twine_file.rb index b000180..4ffca5e 100644 --- a/lib/twine/twine_file.rb +++ b/lib/twine/twine_file.rb @@ -1,9 +1,13 @@ module Twine class TwineDefinition + PLURAL_KEYS = %w(zero one two few many other) + attr_reader :key attr_accessor :comment attr_accessor :tags attr_reader :translations + attr_reader :plural_translations + attr_reader :is_plural attr_accessor :reference attr_accessor :reference_key @@ -12,6 +16,7 @@ module Twine @comment = nil @tags = nil @translations = {} + @plural_translations = {} end def comment @@ -50,6 +55,16 @@ module Twine return translation end + + def plural_translation_for_lang(lang) + if @plural_translations.has_key? lang + @plural_translations[lang].dup + end + end + + def is_plural? + !@plural_translations.empty? + end end class TwineSection @@ -137,11 +152,12 @@ module Twine parsed = true end else - match = /^([^=]+)=(.*)$/.match(line) + match = /^([^:=]+)(?::([^=]+))?=(.*)$/.match(line) if match key = match[1].strip - value = match[2].strip - + plural_key = match[2].to_s.strip + value = match[3].strip + value = value[1..-2] if value[0] == '`' && value[-1] == '`' case key @@ -155,7 +171,18 @@ module Twine if !@language_codes.include? key add_language_code(key) end - current_definition.translations[key] = value + # Providing backward compatibility + # for formatters without plural support + if plural_key.empty? || plural_key == 'other' + current_definition.translations[key] = value + end + if !plural_key.empty? + if !TwineDefinition::PLURAL_KEYS.include? plural_key + warn("Unknown plural key #{plural_key}") + next + end + (current_definition.plural_translations[key] ||= {})[plural_key] = value + end end parsed = true end -- cgit v1.2.3