commit
1e1b73fda2
32 changed files with 686 additions and 670 deletions
95
README.md
95
README.md
|
@ -1,6 +1,6 @@
|
|||
# Twine
|
||||
|
||||
Twine is a command line tool for managing your strings and their translations. These strings are all stored in a master text file and then Twine uses this file to import and export strings in a variety of file types, including iOS and Mac OS X `.strings` files, Android `.xml` files, gettext `.po` files, and [jquery-localize][jquerylocalize] `.json` files. This allows individuals and companies to easily share strings across multiple projects, as well as export strings in any format the user wants.
|
||||
Twine is a command line tool for managing your strings and their translations. These are all stored in a master text file and then Twine uses this file to import and export localization files in a variety of types, including iOS and Mac OS X `.strings` files, Android `.xml` files, gettext `.po` files, and [jquery-localize][jquerylocalize] `.json` files. This allows individuals and companies to easily share translations across multiple projects, as well as export localization files in any format the user wants.
|
||||
|
||||
## Install
|
||||
|
||||
|
@ -21,19 +21,19 @@ You can also run Twine directly from source. However, it requires [rubyzip][ruby
|
|||
|
||||
Make sure you run the `twine` executable at the root of the project as it properly sets up your Ruby library path. The `bin/twine` executable does not.
|
||||
|
||||
## String File Format
|
||||
## Twine File Format
|
||||
|
||||
Twine stores all of its strings in a single file. The format of this file is a slight variant of the [Git][git] config file format, which itself is based on the old [Windows INI file][INI] format. The entire file is broken up into sections, which are created by placing the section name between two pairs of square brackets. Sections are optional, but they are a recommended way of breaking your strings into smaller, more manageable chunks.
|
||||
Twine stores everything in a single file, the Twine data file. The format of this file is a slight variant of the [Git][git] config file format, which itself is based on the old [Windows INI file][INI] format. The entire file is broken up into sections, which are created by placing the section name between two pairs of square brackets. Sections are optional, but they are the recommended way of grouping your definitions into smaller, more manageable chunks.
|
||||
|
||||
Each grouping section contains N string definitions. These string definitions start with the string key placed within a single pair of square brackets. This string definition then contains a number of key-value pairs, including a comment, a comma-separated list of tags (which are used by Twine to select a subset of strings), and all of the translations.
|
||||
Each grouping section contains N definitions. These definitions start with the key placed within a single pair of square brackets. It then contains a number of key-value pairs, including a comment, a comma-separated list of tags and all of the translations.
|
||||
|
||||
### Placeholders
|
||||
|
||||
Twine supports [`printf` style placeholders](https://en.wikipedia.org/wiki/Printf_format_string) with one peculiarity: `@` is used for strings instead of `s`. This is because Twine started out as a tool for iOS and OS X projects.
|
||||
Twine supports [`printf` style placeholders][printf] with one peculiarity: `@` is used for strings instead of `s`. This is because Twine started out as a tool for iOS and OS X projects.
|
||||
|
||||
### Tags
|
||||
|
||||
Tags are used by Twine as a way to only work with a subset of your strings at any given point in time. Each string can be assigned zero or more tags which are separated by commas. Tags are optional, though highly recommended. You can get a list of all strings currently missing tags by executing the `validate-strings-file` command.
|
||||
Tags are used by Twine as a way to only work with a subset of your definitions at any given point in time. Each definition can be assigned zero or more tags which are separated by commas. Tags are optional, though highly recommended. You can get a list of all definitions currently missing tags by executing the [`validate-twine-file`](#validate-twine-file) command.
|
||||
|
||||
### Whitespace
|
||||
|
||||
|
@ -41,7 +41,7 @@ Whitepace in this file is mostly ignored. If you absolutely need to put spaces a
|
|||
|
||||
### References
|
||||
|
||||
If you want a key to inherit the values of another key, you can use a reference. Any property not specified for a key will be taken from the reference.
|
||||
If you want a definition to inherit the values of another definition, you can use a reference. Any property not specified for a definition will be taken from the reference.
|
||||
|
||||
### Example
|
||||
|
||||
|
@ -83,7 +83,7 @@ If you want a key to inherit the values of another key, you can use a reference.
|
|||
|
||||
## Supported Output Formats
|
||||
|
||||
Twine currently supports the following formats for outputting strings:
|
||||
Twine currently supports the following output formats:
|
||||
|
||||
* [iOS and OS X String Resources][applestrings] (format: apple)
|
||||
* [Android String Resources][androidstrings] (format: android)
|
||||
|
@ -92,67 +92,67 @@ Twine currently supports the following formats for outputting strings:
|
|||
* [Django PO Files][djangopo] (format: django)
|
||||
* [Tizen String Resources][tizen] (format: tizen)
|
||||
|
||||
If you would like to enable twine to create language files in another format, create an appropriate formatter in `lib/twine/formatters`.
|
||||
If you would like to enable Twine to create localization files in another format, read the wiki page on how to create an appropriate formatter.
|
||||
|
||||
## Usage
|
||||
|
||||
Usage: twine COMMAND STRINGS_FILE [INPUT_OR_OUTPUT_PATH] [--lang LANG1,LANG2...] [--tags TAG1,TAG2,TAG3...] [--format FORMAT]
|
||||
Usage: twine COMMAND TWINE_FILE [INPUT_OR_OUTPUT_PATH] [--lang LANG1,LANG2...] [--tags TAG1,TAG2,TAG3...] [--format FORMAT]
|
||||
|
||||
### Commands
|
||||
|
||||
#### `generate-string-file`
|
||||
#### `generate-localization-file`
|
||||
|
||||
This command creates an Apple or Android strings file from the master strings data file. If the output file would not contain any translations, twine will exit with an error.
|
||||
This command creates a localization file from the Twine data file. If the output file would not contain any translations, Twine will exit with an error.
|
||||
|
||||
$ twine generate-string-file /path/to/strings.txt values-ja.xml --tags common,app1
|
||||
$ twine generate-string-file /path/to/strings.txt Localizable.strings --lang ja --tags mytag
|
||||
$ twine generate-string-file /path/to/strings.txt all-english.strings --lang en
|
||||
$ twine generate-localization-file /path/to/twine.txt values-ja.xml --tags common,app1
|
||||
$ twine generate-localization-file /path/to/twine.txt Localizable.strings --lang ja --tags mytag
|
||||
$ twine generate-localization-file /path/to/twine.txt all-english.strings --lang en
|
||||
|
||||
#### `generate-all-string-files`
|
||||
#### `generate-all-localization-files`
|
||||
|
||||
This command is a convenient way to call `generate-string-file` multiple times. It uses standard Mac OS X, iOS, and Android conventions to figure out exactly which files to create given a parent directory. For example, if you point it to a parent directory containing `en.lproj`, `fr.lproj`, and `ja.lproj` subdirectories, Twine will create a `Localizable.strings` file of the appropriate language in each of them. However, files that would not contain any translations will not be created; instead warnings will be logged to `stderr`. This is often the command you will want to execute during the build phase of your project.
|
||||
This command is a convenient way to call [`generate-localization-file`](#generate-localization-file) multiple times. It uses standard conventions to figure out exactly which files to create given a parent directory. For example, if you point it to a parent directory containing `en.lproj`, `fr.lproj`, and `ja.lproj` subdirectories, Twine will create a `Localizable.strings` file of the appropriate language in each of them. However, files that would not contain any translations will not be created; instead warnings will be logged to `stderr`. This is often the command you will want to execute during the build phase of your project.
|
||||
|
||||
$ twine generate-all-string-files /path/to/strings.txt /path/to/project/locales/directory --tags common,app1
|
||||
$ twine generate-all-localization-files /path/to/twine.txt /path/to/project/locales/directory --tags common,app1
|
||||
|
||||
#### `consume-string-file`
|
||||
#### `consume-localization-file`
|
||||
|
||||
This command slurps all of the strings from a `.strings` or `.xml` file and incorporates the translated text into the master strings data file. This is a simple way to incorporate any changes made to a single file by one of your translators. It will only identify strings that already exist in the master data file.
|
||||
This command slurps all of the translations from a localization file and incorporates the translated strings into the Twine data file. This is a simple way to incorporate any changes made to a single file by one of your translators. It will only identify definitions that already exist in the data file.
|
||||
|
||||
$ twine consume-string-file /path/to/strings.txt fr.strings
|
||||
$ twine consume-string-file /path/to/strings.txt Localizable.strings --lang ja
|
||||
$ twine consume-string-file /path/to/strings.txt es.xml
|
||||
$ twine consume-localization-file /path/to/twine.txt fr.strings
|
||||
$ twine consume-localization-file /path/to/twine.txt Localizable.strings --lang ja
|
||||
$ twine consume-localization-file /path/to/twine.txt es.xml
|
||||
|
||||
#### `consume-all-string-files`
|
||||
#### `consume-all-localization-files`
|
||||
|
||||
This command reads in a folder containing many `.strings` or `.xml` files. These files should be in a standard folder hierarchy so that twine knows the language of each file. When combined with the `--developer-language`, `--consume-comments`, and `--consume-all` flags, this command is a great way to create your initial strings data file from an existing iOS or Android project. Just make sure that you create a blank strings.txt file, first!
|
||||
This command reads in a folder containing many localization files. These files should be in a standard folder hierarchy so that Twine knows the language of each file. When combined with the `--developer-language`, `--consume-comments`, and `--consume-all` flags, this command is a great way to create your initial Twine data file from an existing project. Just make sure that you create a blank Twine data file first!
|
||||
|
||||
$ twine consume-all-string-files strings.txt Resources/Locales --developer-language en --consume-all --consume-comments
|
||||
$ twine consume-all-localization-files twine.txt Resources/Locales --developer-language en --consume-all --consume-comments
|
||||
|
||||
#### `generate-loc-drop`
|
||||
|
||||
This command is a convenient way to generate a zip file containing files created by the `generate-string-file` command. If a file would not contain any translated strings, it is skipped and a warning is logged to `stderr`. This command can be used to create a single zip containing a large number of strings in all languages which you can then hand off to your translation team.
|
||||
This command is a convenient way to generate a zip file containing files created by the [`generate-localization-file`](#generate-localization-file) command. If a file would not contain any translated strings, it is skipped and a warning is logged to `stderr`. This command can be used to create a single zip containing a large number of translations in all languages which you can then hand off to your translation team.
|
||||
|
||||
$ twine generate-loc-drop /path/to/strings.txt LocDrop1.zip
|
||||
$ twine generate-loc-drop /path/to/strings.txt LocDrop2.zip --lang en,fr,ja,ko --tags common,app1
|
||||
$ twine generate-loc-drop /path/to/twine.txt LocDrop1.zip
|
||||
$ twine generate-loc-drop /path/to/twine.txt LocDrop2.zip --lang en,fr,ja,ko --tags common,app1
|
||||
|
||||
#### `consume-loc-drop`
|
||||
|
||||
This command is a convenient way of taking a zip file and executing the `consume-string-file` command on each file within the archive. It is most often used to incorporate all of the changes made by the translation team after they have completed work on a localization drop.
|
||||
This command is a convenient way of taking a zip file and executing the [`consume-localization-file`](#consume-localization-file) command on each file within the archive. It is most often used to incorporate all of the changes made by the translation team after they have completed work on a localization drop.
|
||||
|
||||
$ twine consume-loc-drop /path/to/strings.txt LocDrop2.zip
|
||||
$ twine consume-loc-drop /path/to/twine.txt LocDrop2.zip
|
||||
|
||||
#### `validate-strings-file`
|
||||
#### `validate-twine-file`
|
||||
|
||||
This command validates that the strings file can be parsed, contains no duplicate keys, and that all strings have at least one tag. It will exit with a non-zero status code if any of those criteria are not met.
|
||||
This command validates that the Twine data file can be parsed, contains no duplicate keys, and that no key contains invalid characters. It will exit with a non-zero status code if any of those criteria are not met.
|
||||
|
||||
$ twine validate-strings-file /path/to/strings.txt
|
||||
$ twine validate-twine-file /path/to/twine.txt
|
||||
|
||||
## Creating Your First strings.txt File
|
||||
## Creating Your First Twine Data File
|
||||
|
||||
The easiest way to create your first strings.txt file is to run the `consume-all-string-files` command. The one caveat is to first create a blank strings.txt file to use as your starting point. Then, just point the `consume-all-string-files` command at a directory in your project containing all of your iOS, OS X, or Android strings files.
|
||||
The easiest way to create your first Twine data file is to run the [`consume-all-localization-files`](#consume-all-localization-files) command. The one caveat is to first create a blank file to use as your starting point. Then, just point the `consume-all-localization-files` command at a directory in your project containing all of your localization files.
|
||||
|
||||
$ touch strings.txt
|
||||
$ twine consume-all-string-files strings.txt Resources/Locales --developer-language en --consume-all --consume-comments
|
||||
$ touch twine.txt
|
||||
$ twine consume-all-localization-files twine.txt Resources/Locales --developer-language en --consume-all --consume-comments
|
||||
|
||||
## Twine and Your Build Process
|
||||
|
||||
|
@ -161,12 +161,12 @@ The easiest way to create your first strings.txt file is to run the `consume-all
|
|||
It is easy to incorporate Twine right into your iOS and OS X app build processes.
|
||||
|
||||
1. In your project folder, create all of the `.lproj` directories that you need. It does not really matter where they are. We tend to put them in `Resources/Locales/`.
|
||||
2. Run the `generate-all-string-files` command to create all of the string files you need in these directories. For example,
|
||||
2. Run the [`generate-all-localization-files`](#generate-all-localization-files) command to create all of the `.strings` files you need in these directories. For example,
|
||||
|
||||
$ twine generate-all-string-files strings.txt Resources/Locales/ --tags tag1,tag2
|
||||
$ twine generate-all-localization-files twine.txt Resources/Locales/ --tags tag1,tag2
|
||||
|
||||
Make sure you point Twine at your strings data file, the directory that contains all of your `.lproj` directories, and the tags that describe the strings you want to use for this project.
|
||||
3. Drag the `Resources/Locales/` directory to the Xcode project navigator so that Xcode knows to include all of these strings files in your build.
|
||||
Make sure you point Twine at your data file, the directory that contains all of your `.lproj` directories, and the tags that describe the definitions you want to use for this project.
|
||||
3. Drag the `Resources/Locales/` directory to the Xcode project navigator so that Xcode knows to include all of these `.strings` files in your build.
|
||||
4. In Xcode, navigate to the "Build Phases" tab of your target.
|
||||
5. Click on the "Add Build Phase" button and select "Add Run Script".
|
||||
6. Drag the new "Run Script" build phase up so that it runs earlier in the build process. It doesn't really matter where, as long as it happens before the resources are copied to your bundle.
|
||||
|
@ -178,8 +178,8 @@ Now, whenever you build your application, Xcode will automatically invoke Twine
|
|||
|
||||
Add the following task at the top level in app/build.gradle:
|
||||
```
|
||||
task generateStrings {
|
||||
String script = 'if hash twine 2>/dev/null; then twine generate-string-file strings.txt ./src/main/res/values/generated_strings.xml; fi'
|
||||
task generateLocalizations {
|
||||
String script = 'if hash twine 2>/dev/null; then twine generate-localization-file twine.txt ./src/main/res/values/generated_strings.xml; fi'
|
||||
exec {
|
||||
executable "sh"
|
||||
args '-c', script
|
||||
|
@ -187,12 +187,12 @@ task generateStrings {
|
|||
}
|
||||
```
|
||||
|
||||
Now every time you build your app the strings are generated from the twine file.
|
||||
Now every time you build your app the localization files are generated from the Twine file.
|
||||
|
||||
|
||||
## User Interface
|
||||
|
||||
* [Twine TextMate 2 Bundle](https://github.com/mobiata/twine.tmbundle) — This [TextMate 2](https://github.com/textmate/textmate) bundle will make it easier for you to work with Twine strings files. In particular, it lets you use code folding to easily collapse and expand both strings and sections.
|
||||
* [Twine TextMate 2 Bundle](https://github.com/mobiata/twine.tmbundle) — This [TextMate 2](https://github.com/textmate/textmate) bundle will make it easier for you to work with Twine files. In particular, it lets you use code folding to easily collapse and expand both definitions and sections.
|
||||
* [twine_ui](https://github.com/Daij-Djan/twine_ui) — A user interface for Twine written by [Dominik Pich](https://github.com/Daij-Djan/). Consider using this if you would prefer to use Twine without dropping to a command line.
|
||||
|
||||
## Plugin Support
|
||||
|
@ -229,7 +229,7 @@ Many thanks to all of the contributors to the Twine project, including:
|
|||
* [Kevin Wood](https://github.com/kwood)
|
||||
* [Mohammad Hejazi](https://github.com/MohammadHejazi)
|
||||
* [Robert Guo](http://www.robertguo.me/)
|
||||
* [sebastianludwig](https://github.com/sebastianludwig)
|
||||
* [Sebastian Ludwig](https://github.com/sebastianludwig)
|
||||
* [Sergey Pisarchik](https://github.com/SergeyPisarchik)
|
||||
* [Shai Shamir](https://github.com/pichirichi)
|
||||
|
||||
|
@ -243,3 +243,4 @@ Many thanks to all of the contributors to the Twine project, including:
|
|||
[jquerylocalize]: https://github.com/coderifous/jquery-localize
|
||||
[djangopo]: https://docs.djangoproject.com/en/dev/topics/i18n/translation/
|
||||
[tizen]: https://developer.tizen.org/documentation/articles/localization
|
||||
[printf]: https://en.wikipedia.org/wiki/Printf_format_string
|
||||
|
|
|
@ -23,7 +23,7 @@ module Twine
|
|||
|
||||
require 'twine/plugin'
|
||||
require 'twine/cli'
|
||||
require 'twine/stringsfile'
|
||||
require 'twine/twine_file'
|
||||
require 'twine/encoding'
|
||||
require 'twine/output_processor'
|
||||
require 'twine/placeholders'
|
||||
|
|
121
lib/twine/cli.rb
121
lib/twine/cli.rb
|
@ -3,57 +3,57 @@ require 'optparse'
|
|||
module Twine
|
||||
module CLI
|
||||
NEEDED_COMMAND_ARGUMENTS = {
|
||||
'generate-string-file' => 3,
|
||||
'generate-all-string-files' => 3,
|
||||
'consume-string-file' => 3,
|
||||
'consume-all-string-files' => 3,
|
||||
'generate-localization-file' => 3,
|
||||
'generate-all-localization-files' => 3,
|
||||
'consume-localization-file' => 3,
|
||||
'consume-all-localization-files' => 3,
|
||||
'generate-loc-drop' => 3,
|
||||
'consume-loc-drop' => 3,
|
||||
'validate-strings-file' => 2
|
||||
'validate-twine-file' => 2
|
||||
}
|
||||
|
||||
def self.parse(args)
|
||||
options = { include: :all }
|
||||
|
||||
parser = OptionParser.new do |opts|
|
||||
opts.banner = 'Usage: twine COMMAND STRINGS_FILE [INPUT_OR_OUTPUT_PATH] [--lang LANG1,LANG2...] [--tags TAG1,TAG2,TAG3...] [--format FORMAT]'
|
||||
opts.banner = 'Usage: twine COMMAND TWINE_FILE [INPUT_OR_OUTPUT_PATH] [--lang LANG1,LANG2...] [--tags TAG1,TAG2,TAG3...] [--format FORMAT]'
|
||||
opts.separator ''
|
||||
opts.separator 'The purpose of this script is to convert back and forth between multiple data formats, allowing us to treat our strings (and translations) as data stored in a text file. We can then use the data file to create drops for the localization team, consume similar drops returned by the localization team, and create formatted string files to ship with your products.'
|
||||
opts.separator 'The purpose of this script is to convert back and forth between multiple data formats, allowing us to treat our strings (and translations) as data stored in a text file. We can then use the data file to create drops for the localization team, consume similar drops returned by the localization team, and create formatted localization files to ship with your products.'
|
||||
opts.separator ''
|
||||
opts.separator 'Commands:'
|
||||
opts.separator ''
|
||||
opts.separator '- generate-string-file'
|
||||
opts.separator ' Generates a string file in a certain LANGUAGE given a particular FORMAT. This script will attempt to guess both the language and the format given the filename and extension. For example, "ko.xml" will generate a Korean language file for Android.'
|
||||
opts.separator '- generate-localization-file'
|
||||
opts.separator ' Generates a localization file in a certain LANGUAGE given a particular FORMAT. This script will attempt to guess both the language and the format given the filename and extension. For example, "ko.xml" will generate a Korean language file for Android.'
|
||||
opts.separator ''
|
||||
opts.separator '- generate-all-string-files'
|
||||
opts.separator ' Generates all the string files necessary for a given project. The parent directory to all of the locale-specific directories in your project should be specified as the INPUT_OR_OUTPUT_PATH. This command will most often be executed by your build script so that each build always contains the most recent strings.'
|
||||
opts.separator '- generate-all-localization-files'
|
||||
opts.separator ' Generates all the localization files necessary for a given project. The parent directory to all of the locale-specific directories in your project should be specified as the INPUT_OR_OUTPUT_PATH. This command will most often be executed by your build script so that each build always contains the most recent translations.'
|
||||
opts.separator ''
|
||||
opts.separator '- consume-string-file'
|
||||
opts.separator ' Slurps all of the strings from a translated strings file into the specified STRINGS_FILE. If you have some files returned to you by your translators you can use this command to incorporate all of their changes. This script will attempt to guess both the language and the format given the filename and extension. For example, "ja.strings" will assume that the file is a Japanese iOS strings file.'
|
||||
opts.separator '- consume-localization-file'
|
||||
opts.separator ' Slurps all of the translations from a localization file into the specified TWINE_FILE. If you have some files returned to you by your translators you can use this command to incorporate all of their changes. This script will attempt to guess both the language and the format given the filename and extension. For example, "ja.strings" will assume that the file is a Japanese iOS strings file.'
|
||||
opts.separator ''
|
||||
opts.separator '- consume-all-string-files'
|
||||
opts.separator ' Slurps all of the strings from a directory into the specified STRINGS_FILE. If you have some files returned to you by your translators you can use this command to incorporate all of their changes. This script will attempt to guess both the language and the format given the filename and extension. For example, "ja.strings" will assume that the file is a Japanese iOS strings file.'
|
||||
opts.separator '- consume-all-localization-files'
|
||||
opts.separator ' Slurps all of the translations from a directory into the specified TWINE_FILE. If you have some files returned to you by your translators you can use this command to incorporate all of their changes. This script will attempt to guess both the language and the format given the filename and extension. For example, "ja.strings" will assume that the file is a Japanese iOS strings file.'
|
||||
opts.separator ''
|
||||
opts.separator '- generate-loc-drop'
|
||||
opts.separator ' Generates a zip archive of strings files in any format. The purpose of this command is to create a very simple archive that can be handed off to a translation team. The translation team can unzip the archive, translate all of the strings in the archived files, zip everything back up, and then hand that final archive back to be consumed by the consume-loc-drop command.'
|
||||
opts.separator ' Generates a zip archive of localization files in a given format. The purpose of this command is to create a very simple archive that can be handed off to a translation team. The translation team can unzip the archive, translate all of the strings in the archived files, zip everything back up, and then hand that final archive back to be consumed by the consume-loc-drop command.'
|
||||
opts.separator ''
|
||||
opts.separator '- consume-loc-drop'
|
||||
opts.separator ' Consumes an archive of translated files. This archive should be in the same format as the one created by the generate-loc-drop command.'
|
||||
opts.separator ''
|
||||
opts.separator '- validate-strings-file'
|
||||
opts.separator ' Validates that the given strings file is parseable, contains no duplicates, and that every string has a tag. Exits with a non-zero exit code if those criteria are not met.'
|
||||
opts.separator '- validate-twine-file'
|
||||
opts.separator ' Validates that the given Twine file is parseable, contains no duplicates, and that no key contains invalid characters. Exits with a non-zero exit code if those criteria are not met.'
|
||||
opts.separator ''
|
||||
opts.separator 'General Options:'
|
||||
opts.separator ''
|
||||
opts.on('-l', '--lang LANGUAGES', Array, 'The language code(s) to use for the specified action.') do |l|
|
||||
options[:languages] = l
|
||||
end
|
||||
opts.on('-t', '--tags TAG1,TAG2,TAG3', Array, 'The tag(s) to use for the specified action. Only strings with that tag will be processed. Omit this option to match',
|
||||
' all strings in the strings data file.') do |t|
|
||||
opts.on('-t', '--tags TAG1,TAG2,TAG3', Array, 'The tag(s) to use for the specified action. Only definitions with that tag will be processed. Omit this option to match',
|
||||
' all definitions in the Twine data file.') do |t|
|
||||
options[:tags] = t
|
||||
end
|
||||
opts.on('-u', '--[no-]untagged', 'If you have specified tags using the --tags flag, then only those tags will be selected. If you also want to select',
|
||||
' all strings that are untagged, then you can specify this option to do so.') do |u|
|
||||
' all definitions that are untagged, then you can specify this option to do so.') do |u|
|
||||
options[:untagged] = u
|
||||
end
|
||||
formats = Formatters.formatters.map(&:format_name).map(&:downcase)
|
||||
|
@ -61,36 +61,36 @@ module Twine
|
|||
" Additional formatters can be placed in the formats/ directory.") do |f|
|
||||
options[:format] = f
|
||||
end
|
||||
opts.on('-a', '--[no-]consume-all', 'Normally, when consuming a string file, Twine will ignore any string keys that do not exist in your master file.') do |a|
|
||||
opts.on('-a', '--[no-]consume-all', 'Normally, when consuming a localization file, Twine will ignore any translation keys that do not exist in your Twine file.') do |a|
|
||||
options[:consume_all] = true
|
||||
end
|
||||
opts.on('-i', '--include SET', [:all, :translated, :untranslated],
|
||||
"This flag will determine which strings are included when generating strings files. It's possible values:",
|
||||
" all: All strings both translated and untranslated for the specified language are included. This is the default value.",
|
||||
" translated: Only translated strings are included.",
|
||||
" untranslated: Only untranslated strings are included.") do |i|
|
||||
"This flag will determine which definitions are included when generating localization files. It's possible values are:",
|
||||
" all: All definitions both translated and untranslated for the specified language are included. This is the default value.",
|
||||
" translated: Only definitions with translation for the specified language are included.",
|
||||
" untranslated: Only definitions without translation for the specified language are included.") do |i|
|
||||
options[:include] = i
|
||||
end
|
||||
opts.on('-o', '--output-file OUTPUT_FILE', 'Write the new strings database to this file instead of replacing the original file. This flag is only useful when',
|
||||
' running the consume-string-file or consume-loc-drop commands.') do |o|
|
||||
opts.on('-o', '--output-file OUTPUT_FILE', 'Write a new Twine file at this location instead of replacing the original file. This flag is only useful when',
|
||||
' running the consume-localization-file or consume-loc-drop commands.') do |o|
|
||||
options[:output_path] = o
|
||||
end
|
||||
opts.on('-n', '--file-name FILE_NAME', 'When running the generate-all-string-files command, this flag may be used to overwrite the default file name of the format.') do |n|
|
||||
opts.on('-n', '--file-name FILE_NAME', 'When running the generate-all-localization-files command, this flag may be used to overwrite the default file name of the format.') do |n|
|
||||
options[:file_name] = n
|
||||
end
|
||||
opts.on('-r', '--[no-]create-folders', "When running the generate-all-string-files command, this flag may be used to create output folders for all languages,",
|
||||
opts.on('-r', '--[no-]create-folders', "When running the generate-all-localization-files command, this flag may be used to create output folders for all languages,",
|
||||
" if they don't exist yet. As a result all languages will be exported, not only the ones where an output folder already",
|
||||
" exists.") do |r|
|
||||
options[:create_folders] = r
|
||||
end
|
||||
opts.on('-d', '--developer-language LANG', 'When writing the strings data file, set the specified language as the "developer language". In practice, this just',
|
||||
' means that this language will appear first in the strings data file. When generating files this language will be',
|
||||
' used as default language and its translations will be used if a key is not localized for the output language.') do |d|
|
||||
opts.on('-d', '--developer-language LANG', 'When writing the Twine data file, set the specified language as the "developer language". In practice, this just',
|
||||
' means that this language will appear first in the Twine data file. When generating files this language will be',
|
||||
' used as default language and its translations will be used if a definition is not localized for the output language.') do |d|
|
||||
options[:developer_language] = d
|
||||
end
|
||||
opts.on('-c', '--[no-]consume-comments', 'Normally, when consuming a string file, Twine will ignore all comments in the file. With this flag set, any comments',
|
||||
' encountered will be read and parsed into the strings data file. This is especially useful when creating your first',
|
||||
' strings data file from an existing project.') do |c|
|
||||
opts.on('-c', '--[no-]consume-comments', 'Normally, when consuming a localization file, Twine will ignore all comments in the file. With this flag set, any comments',
|
||||
' encountered will be read and parsed into the Twine data file. This is especially useful when creating your first',
|
||||
' Twine data file from an existing project.') do |c|
|
||||
options[:consume_comments] = c
|
||||
end
|
||||
opts.on('-e', '--encoding ENCODING', 'Twine defaults to encoding all output files in UTF-8. This flag will tell Twine to use an alternate encoding for these',
|
||||
|
@ -99,10 +99,10 @@ module Twine
|
|||
' UTF-16LE or UTF16-BE.') do |e|
|
||||
options[:output_encoding] = e
|
||||
end
|
||||
opts.on('--[no-]validate', 'Validate the strings file before formatting it.') do |validate|
|
||||
opts.on('--[no-]validate', 'Validate the Twine file before formatting it.') do |validate|
|
||||
options[:validate] = validate
|
||||
end
|
||||
opts.on('-p', '--[no-]pedantic', 'When validating a strings file, perform additional checks that go beyond pure validity (like presence of tags).') do |p|
|
||||
opts.on('-p', '--[no-]pedantic', 'When validating a Twine file, perform additional checks that go beyond pure validity (like presence of tags).') do |p|
|
||||
options[:pedantic] = p
|
||||
end
|
||||
opts.on('-h', '--help', 'Show this message.') do |h|
|
||||
|
@ -116,13 +116,13 @@ module Twine
|
|||
opts.separator ''
|
||||
opts.separator 'Examples:'
|
||||
opts.separator ''
|
||||
opts.separator '> twine generate-string-file strings.txt ko.xml --tags FT'
|
||||
opts.separator '> twine generate-all-string-files strings.txt Resources/Locales/ --tags FT,FB'
|
||||
opts.separator '> twine consume-string-file strings.txt ja.strings'
|
||||
opts.separator '> twine consume-all-string-files strings.txt Resources/Locales/ --developer-language en --tags DefaultTag1,DefaultTag2'
|
||||
opts.separator '> twine generate-loc-drop strings.txt LocDrop5.zip --tags FT,FB --format android --lang de,en,en-GB,ja,ko'
|
||||
opts.separator '> twine consume-loc-drop strings.txt LocDrop5.zip'
|
||||
opts.separator '> twine validate-strings-file strings.txt'
|
||||
opts.separator '> twine generate-localization-file twine.txt ko.xml --tags FT'
|
||||
opts.separator '> twine generate-all-localization-files twine.txt Resources/Locales/ --tags FT,FB'
|
||||
opts.separator '> twine consume-localization-file twine.txt ja.strings'
|
||||
opts.separator '> twine consume-all-localization-files twine.txt Resources/Locales/ --developer-language en --tags DefaultTag1,DefaultTag2'
|
||||
opts.separator '> twine generate-loc-drop twine.txt LocDrop5.zip --tags FT,FB --format android --lang de,en,en-GB,ja,ko'
|
||||
opts.separator '> twine consume-loc-drop twine.txt LocDrop5.zip'
|
||||
opts.separator '> twine validate-twine-file twine.txt'
|
||||
end
|
||||
begin
|
||||
parser.parse! args
|
||||
|
@ -136,6 +136,19 @@ module Twine
|
|||
exit false
|
||||
end
|
||||
|
||||
# TODO: Remove this mapping of deprecated commands some time in the future - added on 31.03.
|
||||
deprecated_command_mappings = {
|
||||
'generate-string-file' => 'generate-localization-file',
|
||||
'generate-all-string-file' => 'generate-all-localization-files',
|
||||
'consume-string-file' => 'consume-localization-file',
|
||||
'consume-all-string-files' => 'consume-all-localization-files'
|
||||
}
|
||||
mapped_command = deprecated_command_mappings[args[0]]
|
||||
if mapped_command
|
||||
Twine::stderr.puts "WARNING: Twine commands names have changed. `#{args[0]}` is now `#{mapped_command}`. The old command is deprecated will soon stop working. For more information please check the documentation at https://github.com/mobiata/twine"
|
||||
args[0] = mapped_command
|
||||
end
|
||||
|
||||
number_of_needed_arguments = NEEDED_COMMAND_ARGUMENTS[args[0]]
|
||||
unless number_of_needed_arguments
|
||||
raise Twine::Error.new "Invalid command: #{args[0]}"
|
||||
|
@ -143,9 +156,9 @@ module Twine
|
|||
options[:command] = args[0]
|
||||
|
||||
if args.length < 2
|
||||
raise Twine::Error.new 'You must specify your strings file.'
|
||||
raise Twine::Error.new 'You must specify your twine file.'
|
||||
end
|
||||
options[:strings_file] = args[1]
|
||||
options[:twine_file] = args[1]
|
||||
|
||||
if args.length < number_of_needed_arguments
|
||||
raise Twine::Error.new 'Not enough arguments.'
|
||||
|
@ -154,19 +167,19 @@ module Twine
|
|||
end
|
||||
|
||||
case options[:command]
|
||||
when 'generate-string-file'
|
||||
when 'generate-localization-file'
|
||||
options[:output_path] = args[2]
|
||||
if options[:languages] and options[:languages].length > 1
|
||||
raise Twine::Error.new 'Please only specify a single language for the generate-string-file command.'
|
||||
raise Twine::Error.new 'Please only specify a single language for the generate-localization-file command.'
|
||||
end
|
||||
when 'generate-all-string-files'
|
||||
when 'generate-all-localization-files'
|
||||
options[:output_path] = args[2]
|
||||
when 'consume-string-file'
|
||||
when 'consume-localization-file'
|
||||
options[:input_path] = args[2]
|
||||
if options[:languages] and options[:languages].length > 1
|
||||
raise Twine::Error.new 'Please only specify a single language for the consume-string-file command.'
|
||||
raise Twine::Error.new 'Please only specify a single language for the consume-localization-file command.'
|
||||
end
|
||||
when 'consume-all-string-files'
|
||||
when 'consume-all-localization-files'
|
||||
options[:input_path] = args[2]
|
||||
when 'generate-loc-drop'
|
||||
options[:output_path] = args[2]
|
||||
|
@ -175,7 +188,7 @@ module Twine
|
|||
end
|
||||
when 'consume-loc-drop'
|
||||
options[:input_path] = args[2]
|
||||
when 'validate-strings-file'
|
||||
when 'validate-twine-file'
|
||||
end
|
||||
|
||||
return options
|
||||
|
|
|
@ -3,11 +3,11 @@ require 'fileutils'
|
|||
module Twine
|
||||
module Formatters
|
||||
class Abstract
|
||||
attr_accessor :strings
|
||||
attr_accessor :twine_file
|
||||
attr_accessor :options
|
||||
|
||||
def initialize
|
||||
@strings = StringsFile.new
|
||||
@twine_file = TwineFile.new
|
||||
@options = {}
|
||||
end
|
||||
|
||||
|
@ -30,47 +30,47 @@ module Twine
|
|||
def set_translation_for_key(key, lang, value)
|
||||
value = value.gsub("\n", "\\n")
|
||||
|
||||
if @strings.strings_map.include?(key)
|
||||
row = @strings.strings_map[key]
|
||||
reference = @strings.strings_map[row.reference_key] if row.reference_key
|
||||
if @twine_file.definitions_by_key.include?(key)
|
||||
definition = @twine_file.definitions_by_key[key]
|
||||
reference = @twine_file.definitions_by_key[definition.reference_key] if definition.reference_key
|
||||
|
||||
if !reference or value != reference.translations[lang]
|
||||
row.translations[lang] = value
|
||||
definition.translations[lang] = value
|
||||
end
|
||||
elsif @options[:consume_all]
|
||||
Twine::stderr.puts "Adding new string '#{key}' to strings data file."
|
||||
current_section = @strings.sections.find { |s| s.name == 'Uncategorized' }
|
||||
Twine::stderr.puts "Adding new definition '#{key}' to twine file."
|
||||
current_section = @twine_file.sections.find { |s| s.name == 'Uncategorized' }
|
||||
unless current_section
|
||||
current_section = StringsSection.new('Uncategorized')
|
||||
@strings.sections.insert(0, current_section)
|
||||
current_section = TwineSection.new('Uncategorized')
|
||||
@twine_file.sections.insert(0, current_section)
|
||||
end
|
||||
current_row = StringsRow.new(key)
|
||||
current_section.rows << current_row
|
||||
current_definition = TwineDefinition.new(key)
|
||||
current_section.definitions << current_definition
|
||||
|
||||
if @options[:tags] && @options[:tags].length > 0
|
||||
current_row.tags = @options[:tags]
|
||||
current_definition.tags = @options[:tags]
|
||||
end
|
||||
|
||||
@strings.strings_map[key] = current_row
|
||||
@strings.strings_map[key].translations[lang] = value
|
||||
@twine_file.definitions_by_key[key] = current_definition
|
||||
@twine_file.definitions_by_key[key].translations[lang] = value
|
||||
else
|
||||
Twine::stderr.puts "Warning: '#{key}' not found in strings data file."
|
||||
Twine::stderr.puts "Warning: '#{key}' not found in twine file."
|
||||
end
|
||||
if !@strings.language_codes.include?(lang)
|
||||
@strings.add_language_code(lang)
|
||||
if !@twine_file.language_codes.include?(lang)
|
||||
@twine_file.add_language_code(lang)
|
||||
end
|
||||
end
|
||||
|
||||
def set_comment_for_key(key, comment)
|
||||
return unless @options[:consume_comments]
|
||||
|
||||
if @strings.strings_map.include?(key)
|
||||
row = @strings.strings_map[key]
|
||||
if @twine_file.definitions_by_key.include?(key)
|
||||
definition = @twine_file.definitions_by_key[key]
|
||||
|
||||
reference = @strings.strings_map[row.reference_key] if row.reference_key
|
||||
reference = @twine_file.definitions_by_key[definition.reference_key] if definition.reference_key
|
||||
|
||||
if !reference or comment != reference.raw_comment
|
||||
row.comment = comment
|
||||
definition.comment = comment
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -88,35 +88,35 @@ module Twine
|
|||
end
|
||||
|
||||
def format_file(lang)
|
||||
output_processor = Processors::OutputProcessor.new(@strings, @options)
|
||||
processed_strings = output_processor.process(lang)
|
||||
output_processor = Processors::OutputProcessor.new(@twine_file, @options)
|
||||
processed_twine_file = output_processor.process(lang)
|
||||
|
||||
return nil if processed_strings.strings_map.empty?
|
||||
return nil if processed_twine_file.definitions_by_key.empty?
|
||||
|
||||
header = format_header(lang)
|
||||
result = ""
|
||||
result += header + "\n" if header
|
||||
result += format_sections(processed_strings, lang)
|
||||
result += format_sections(processed_twine_file, lang)
|
||||
end
|
||||
|
||||
def format_header(lang)
|
||||
end
|
||||
|
||||
def format_sections(strings, lang)
|
||||
sections = strings.sections.map { |section| format_section(section, lang) }
|
||||
def format_sections(twine_file, lang)
|
||||
sections = twine_file.sections.map { |section| format_section(section, lang) }
|
||||
sections.compact.join("\n")
|
||||
end
|
||||
|
||||
def format_section_header(section)
|
||||
end
|
||||
|
||||
def should_include_row(row, lang)
|
||||
row.translated_string_for_lang(lang)
|
||||
def should_include_definition(definition, lang)
|
||||
return !definition.translation_for_lang(lang).nil?
|
||||
end
|
||||
|
||||
def format_section(section, lang)
|
||||
rows = section.rows.select { |row| should_include_row(row, lang) }
|
||||
return if rows.empty?
|
||||
definitions = section.definitions.select { |definition| should_include_definition(definition, lang) }
|
||||
return if definitions.empty?
|
||||
|
||||
result = ""
|
||||
|
||||
|
@ -125,22 +125,22 @@ module Twine
|
|||
result += "\n#{section_header}" if section_header
|
||||
end
|
||||
|
||||
rows.map! { |row| format_row(row, lang) }
|
||||
rows.compact! # remove nil entries
|
||||
rows.map! { |row| "\n#{row}" } # prepend newline
|
||||
result += rows.join
|
||||
definitions.map! { |definition| format_definition(definition, lang) }
|
||||
definitions.compact! # remove nil definitions
|
||||
definitions.map! { |definition| "\n#{definition}" } # prepend newline
|
||||
result += definitions.join
|
||||
end
|
||||
|
||||
def format_row(row, lang)
|
||||
[format_comment(row, lang), format_key_value(row, lang)].compact.join
|
||||
def format_definition(definition, lang)
|
||||
[format_comment(definition, lang), format_key_value(definition, lang)].compact.join
|
||||
end
|
||||
|
||||
def format_comment(row, lang)
|
||||
def format_comment(definition, lang)
|
||||
end
|
||||
|
||||
def format_key_value(row, lang)
|
||||
value = row.translated_string_for_lang(lang)
|
||||
key_value_pattern % { key: format_key(row.key.dup), value: format_value(value.dup) }
|
||||
def format_key_value(definition, lang)
|
||||
value = definition.translation_for_lang(lang)
|
||||
key_value_pattern % { key: format_key(definition.key.dup), value: format_value(value.dup) }
|
||||
end
|
||||
|
||||
def key_value_pattern
|
||||
|
|
|
@ -37,7 +37,7 @@ module Twine
|
|||
path_arr = path.split(File::SEPARATOR)
|
||||
path_arr.each do |segment|
|
||||
if segment == 'values'
|
||||
return @strings.language_codes[0]
|
||||
return @twine_file.language_codes[0]
|
||||
else
|
||||
# The language is defined by a two-letter ISO 639-1 language code, optionally followed by a two letter ISO 3166-1-alpha-2 region code (preceded by lowercase "r").
|
||||
# see http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources
|
||||
|
@ -105,7 +105,7 @@ module Twine
|
|||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Android Strings File -->\n<!-- Generated by Twine #{Twine::VERSION} -->\n<!-- Language: #{lang} -->"
|
||||
end
|
||||
|
||||
def format_sections(strings, lang)
|
||||
def format_sections(twine_file, lang)
|
||||
result = '<resources>'
|
||||
|
||||
result += super + "\n"
|
||||
|
@ -117,8 +117,8 @@ module Twine
|
|||
"\t<!-- SECTION: #{section.name} -->"
|
||||
end
|
||||
|
||||
def format_comment(row, lang)
|
||||
"\t<!-- #{row.comment.gsub('--', '—')} -->\n" if row.comment
|
||||
def format_comment(definition, lang)
|
||||
"\t<!-- #{definition.comment.gsub('--', '—')} -->\n" if definition.comment
|
||||
end
|
||||
|
||||
def key_value_pattern
|
||||
|
@ -137,7 +137,7 @@ module Twine
|
|||
# 4) escape non resource identifier @ signs (http://developer.android.com/guide/topics/resources/accessing-resources.html#ResourcesFromXml)
|
||||
resource_identifier_regex = /@(?!([a-z\.]+:)?[a-z+]+\/[a-zA-Z_]+)/ # @[<package_name>:]<resource_type>/<resource_name>
|
||||
value.gsub!(resource_identifier_regex, '\@')
|
||||
# 5) replace beginning and end spaces with \0020. Otherwise Android strips them.
|
||||
# 5) replace beginning and end spaces with \u0020. Otherwise Android strips them.
|
||||
value.gsub(/\A *| *\z/) { |spaces| '\u0020' * spaces.length }
|
||||
end
|
||||
|
||||
|
|
|
@ -73,8 +73,8 @@ module Twine
|
|||
"\"%{key}\" = \"%{value}\";\n"
|
||||
end
|
||||
|
||||
def format_comment(row, lang)
|
||||
"/* #{row.comment.gsub('*/', '* /')} */\n" if row.comment
|
||||
def format_comment(definition, lang)
|
||||
"/* #{definition.comment.gsub('*/', '* /')} */\n" if definition.comment
|
||||
end
|
||||
|
||||
def format_key(key)
|
||||
|
|
|
@ -63,7 +63,7 @@ module Twine
|
|||
end
|
||||
|
||||
def format_file(lang)
|
||||
@default_lang = @strings.language_codes[0]
|
||||
@default_lang = @twine_file.language_codes[0]
|
||||
result = super
|
||||
@default_lang = nil
|
||||
result
|
||||
|
@ -77,12 +77,12 @@ module Twine
|
|||
"#--------- #{section.name} ---------#\n"
|
||||
end
|
||||
|
||||
def format_row(row, lang)
|
||||
[format_comment(row, lang), format_base_translation(row), format_key_value(row, lang)].compact.join
|
||||
def format_definition(definition, lang)
|
||||
[format_comment(definition, lang), format_base_translation(definition), format_key_value(definition, lang)].compact.join
|
||||
end
|
||||
|
||||
def format_base_translation(row)
|
||||
base_translation = row.translations[@default_lang]
|
||||
def format_base_translation(definition)
|
||||
base_translation = definition.translations[@default_lang]
|
||||
"# base translation: \"#{base_translation}\"\n" if base_translation
|
||||
end
|
||||
|
||||
|
@ -91,8 +91,8 @@ module Twine
|
|||
"msgstr \"%{value}\"\n"
|
||||
end
|
||||
|
||||
def format_comment(row, lang)
|
||||
"#. #{escape_quotes(row.comment)}\n" if row.comment
|
||||
def format_comment(definition, lang)
|
||||
"#. #{escape_quotes(definition.comment)}\n" if definition.comment
|
||||
end
|
||||
|
||||
def format_key(key)
|
||||
|
|
|
@ -44,7 +44,7 @@ module Twine
|
|||
end
|
||||
end
|
||||
|
||||
def format_sections(strings, lang)
|
||||
def format_sections(twine_file, lang)
|
||||
super + "\n"
|
||||
end
|
||||
|
||||
|
@ -56,8 +56,8 @@ module Twine
|
|||
"## #{section.name} ##\n"
|
||||
end
|
||||
|
||||
def format_comment(row, lang)
|
||||
"# #{row.comment}\n" if row.comment
|
||||
def format_comment(definition, lang)
|
||||
"# #{definition.comment}\n" if definition.comment
|
||||
end
|
||||
|
||||
def key_value_pattern
|
||||
|
|
|
@ -64,7 +64,7 @@ module Twine
|
|||
end
|
||||
|
||||
def format_file(lang)
|
||||
@default_lang = strings.language_codes[0]
|
||||
@default_lang = twine_file.language_codes[0]
|
||||
result = super
|
||||
@default_lang = nil
|
||||
result
|
||||
|
@ -78,25 +78,25 @@ module Twine
|
|||
"# SECTION: #{section.name}"
|
||||
end
|
||||
|
||||
def should_include_row(row, lang)
|
||||
super and row.translated_string_for_lang(@default_lang)
|
||||
def should_include_definition(definition, lang)
|
||||
super and !definition.translation_for_lang(@default_lang).nil?
|
||||
end
|
||||
|
||||
def format_comment(row, lang)
|
||||
"#. \"#{escape_quotes(row.comment)}\"\n" if row.comment
|
||||
def format_comment(definition, lang)
|
||||
"#. \"#{escape_quotes(definition.comment)}\"\n" if definition.comment
|
||||
end
|
||||
|
||||
def format_key_value(row, lang)
|
||||
value = row.translated_string_for_lang(lang)
|
||||
[format_key(row.key.dup), format_base_translation(row), format_value(value.dup)].compact.join
|
||||
def format_key_value(definition, lang)
|
||||
value = definition.translation_for_lang(lang)
|
||||
[format_key(definition.key.dup), format_base_translation(definition), format_value(value.dup)].compact.join
|
||||
end
|
||||
|
||||
def format_key(key)
|
||||
"msgctxt \"#{key}\"\n"
|
||||
end
|
||||
|
||||
def format_base_translation(row)
|
||||
"msgid \"#{row.translations[@default_lang]}\"\n"
|
||||
def format_base_translation(definition)
|
||||
"msgid \"#{definition.translations[@default_lang]}\"\n"
|
||||
end
|
||||
|
||||
def format_value(value)
|
||||
|
|
|
@ -48,8 +48,8 @@ module Twine
|
|||
"{\n#{super}\n}\n"
|
||||
end
|
||||
|
||||
def format_sections(strings, lang)
|
||||
sections = strings.sections.map { |section| format_section(section, lang) }
|
||||
def format_sections(twine_file, lang)
|
||||
sections = twine_file.sections.map { |section| format_section(section, lang) }
|
||||
sections.join(",\n\n")
|
||||
end
|
||||
|
||||
|
@ -57,11 +57,11 @@ module Twine
|
|||
end
|
||||
|
||||
def format_section(section, lang)
|
||||
rows = section.rows.dup
|
||||
definitions = section.definitions.dup
|
||||
|
||||
rows.map! { |row| format_row(row, lang) }
|
||||
rows.compact! # remove nil entries
|
||||
rows.join(",\n")
|
||||
definitions.map! { |definition| format_definition(definition, lang) }
|
||||
definitions.compact! # remove nil definitions
|
||||
definitions.join(",\n")
|
||||
end
|
||||
|
||||
def key_value_pattern
|
||||
|
|
|
@ -94,7 +94,7 @@ module Twine
|
|||
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Tizen Strings File -->\n<!-- Generated by Twine #{Twine::VERSION} -->\n<!-- Language: #{lang} -->"
|
||||
end
|
||||
|
||||
def format_sections(strings, lang)
|
||||
def format_sections(twine_file, lang)
|
||||
result = '<string_table Bversion="2.0.0.201311071819" Dversion="20120315">'
|
||||
|
||||
result += super + "\n"
|
||||
|
@ -106,8 +106,8 @@ module Twine
|
|||
"\t<!-- SECTION: #{section.name} -->"
|
||||
end
|
||||
|
||||
def format_comment(row, lang)
|
||||
"\t<!-- #{row.comment.gsub('--', '—')} -->\n" if row.comment
|
||||
def format_comment(definition, lang)
|
||||
"\t<!-- #{definition.comment.gsub('--', '—')} -->\n" if definition.comment
|
||||
end
|
||||
|
||||
def key_value_pattern
|
||||
|
|
|
@ -2,13 +2,13 @@ module Twine
|
|||
module Processors
|
||||
|
||||
class OutputProcessor
|
||||
def initialize(strings, options)
|
||||
@strings = strings
|
||||
def initialize(twine_file, options)
|
||||
@twine_file = twine_file
|
||||
@options = options
|
||||
end
|
||||
|
||||
def default_language
|
||||
@options[:developer_language] || @strings.language_codes[0]
|
||||
@options[:developer_language] || @twine_file.language_codes[0]
|
||||
end
|
||||
|
||||
def fallback_languages(language)
|
||||
|
@ -20,30 +20,30 @@ module Twine
|
|||
end
|
||||
|
||||
def process(language)
|
||||
result = StringsFile.new
|
||||
result = TwineFile.new
|
||||
|
||||
result.language_codes.concat @strings.language_codes
|
||||
@strings.sections.each do |section|
|
||||
new_section = StringsSection.new section.name
|
||||
result.language_codes.concat @twine_file.language_codes
|
||||
@twine_file.sections.each do |section|
|
||||
new_section = TwineSection.new section.name
|
||||
|
||||
section.rows.each do |row|
|
||||
next unless row.matches_tags?(@options[:tags], @options[:untagged])
|
||||
section.definitions.each do |definition|
|
||||
next unless definition.matches_tags?(@options[:tags], @options[:untagged])
|
||||
|
||||
value = row.translated_string_for_lang(language)
|
||||
value = definition.translation_for_lang(language)
|
||||
|
||||
next if value && @options[:include] == :untranslated
|
||||
|
||||
if value.nil? && @options[:include] != :translated
|
||||
value = row.translated_string_for_lang(fallback_languages(language))
|
||||
value = definition.translation_for_lang(fallback_languages(language))
|
||||
end
|
||||
|
||||
next unless value
|
||||
|
||||
new_row = row.dup
|
||||
new_row.translations[language] = value
|
||||
new_definition = definition.dup
|
||||
new_definition.translations[language] = value
|
||||
|
||||
new_section.rows << new_row
|
||||
result.strings_map[new_row.key] = new_row
|
||||
new_section.definitions << new_definition
|
||||
result.definitions_by_key[new_definition.key] = new_definition
|
||||
end
|
||||
|
||||
result.sections << new_section
|
||||
|
|
|
@ -8,42 +8,42 @@ module Twine
|
|||
def self.run(args)
|
||||
options = CLI.parse(args)
|
||||
|
||||
strings = StringsFile.new
|
||||
strings.read options[:strings_file]
|
||||
runner = new(options, strings)
|
||||
twine_file = TwineFile.new
|
||||
twine_file.read options[:twine_file]
|
||||
runner = new(options, twine_file)
|
||||
|
||||
case options[:command]
|
||||
when 'generate-string-file'
|
||||
runner.generate_string_file
|
||||
when 'generate-all-string-files'
|
||||
runner.generate_all_string_files
|
||||
when 'consume-string-file'
|
||||
runner.consume_string_file
|
||||
when 'consume-all-string-files'
|
||||
runner.consume_all_string_files
|
||||
when 'generate-localization-file'
|
||||
runner.generate_localization_file
|
||||
when 'generate-all-localization-files'
|
||||
runner.generate_all_localization_files
|
||||
when 'consume-localization-file'
|
||||
runner.consume_localization_file
|
||||
when 'consume-all-localization-files'
|
||||
runner.consume_all_localization_files
|
||||
when 'generate-loc-drop'
|
||||
runner.generate_loc_drop
|
||||
when 'consume-loc-drop'
|
||||
runner.consume_loc_drop
|
||||
when 'validate-strings-file'
|
||||
runner.validate_strings_file
|
||||
when 'validate-twine-file'
|
||||
runner.validate_twine_file
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(options = {}, strings = StringsFile.new)
|
||||
def initialize(options = {}, twine_file = TwineFile.new)
|
||||
@options = options
|
||||
@strings = strings
|
||||
@twine_file = twine_file
|
||||
end
|
||||
|
||||
def write_strings_data(path)
|
||||
def write_twine_data(path)
|
||||
if @options[:developer_language]
|
||||
@strings.set_developer_language_code(@options[:developer_language])
|
||||
@twine_file.set_developer_language_code(@options[:developer_language])
|
||||
end
|
||||
@strings.write(path)
|
||||
@twine_file.write(path)
|
||||
end
|
||||
|
||||
def generate_string_file
|
||||
validate_strings_file if @options[:validate]
|
||||
def generate_localization_file
|
||||
validate_twine_file if @options[:validate]
|
||||
|
||||
lang = nil
|
||||
lang = @options[:languages][0] if @options[:languages]
|
||||
|
@ -51,13 +51,13 @@ module Twine
|
|||
formatter, lang = prepare_read_write(@options[:output_path], lang)
|
||||
output = formatter.format_file(lang)
|
||||
|
||||
raise Twine::Error.new "Nothing to generate! The resulting file would not contain any strings." unless output
|
||||
raise Twine::Error.new "Nothing to generate! The resulting file would not contain any translations." unless output
|
||||
|
||||
IO.write(@options[:output_path], output, encoding: encoding)
|
||||
end
|
||||
|
||||
def generate_all_string_files
|
||||
validate_strings_file if @options[:validate]
|
||||
def generate_all_localization_files
|
||||
validate_twine_file if @options[:validate]
|
||||
|
||||
if !File.directory?(@options[:output_path])
|
||||
if @options[:create_folders]
|
||||
|
@ -76,7 +76,7 @@ module Twine
|
|||
|
||||
file_name = @options[:file_name] || formatter.default_file_name
|
||||
if @options[:create_folders]
|
||||
@strings.language_codes.each do |lang|
|
||||
@twine_file.language_codes.each do |lang|
|
||||
output_path = File.join(@options[:output_path], formatter.output_path_for_language(lang))
|
||||
|
||||
FileUtils.mkdir_p(output_path)
|
||||
|
@ -85,7 +85,7 @@ module Twine
|
|||
|
||||
output = formatter.format_file(lang)
|
||||
unless output
|
||||
Twine::stderr.puts "Skipping file at path #{file_path} since it would not contain any strings."
|
||||
Twine::stderr.puts "Skipping file at path #{file_path} since it would not contain any translations."
|
||||
next
|
||||
end
|
||||
|
||||
|
@ -107,7 +107,7 @@ module Twine
|
|||
file_path = File.join(output_path, file_name)
|
||||
output = formatter.format_file(lang)
|
||||
unless output
|
||||
Twine::stderr.puts "Skipping file at path #{file_path} since it would not contain any strings."
|
||||
Twine::stderr.puts "Skipping file at path #{file_path} since it would not contain any translations."
|
||||
next
|
||||
end
|
||||
|
||||
|
@ -121,18 +121,18 @@ module Twine
|
|||
|
||||
end
|
||||
|
||||
def consume_string_file
|
||||
def consume_localization_file
|
||||
lang = nil
|
||||
if @options[:languages]
|
||||
lang = @options[:languages][0]
|
||||
end
|
||||
|
||||
read_string_file(@options[:input_path], lang)
|
||||
output_path = @options[:output_path] || @options[:strings_file]
|
||||
write_strings_data(output_path)
|
||||
read_localization_file(@options[:input_path], lang)
|
||||
output_path = @options[:output_path] || @options[:twine_file]
|
||||
write_twine_data(output_path)
|
||||
end
|
||||
|
||||
def consume_all_string_files
|
||||
def consume_all_localization_files
|
||||
if !File.directory?(@options[:input_path])
|
||||
raise Twine::Error.new("Directory does not exist: #{@options[:output_path]}")
|
||||
end
|
||||
|
@ -140,19 +140,19 @@ module Twine
|
|||
Dir.glob(File.join(@options[:input_path], "**/*")) do |item|
|
||||
if File.file?(item)
|
||||
begin
|
||||
read_string_file(item)
|
||||
read_localization_file(item)
|
||||
rescue Twine::Error => e
|
||||
Twine::stderr.puts "#{e.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
output_path = @options[:output_path] || @options[:strings_file]
|
||||
write_strings_data(output_path)
|
||||
output_path = @options[:output_path] || @options[:twine_file]
|
||||
write_twine_data(output_path)
|
||||
end
|
||||
|
||||
def generate_loc_drop
|
||||
validate_strings_file if @options[:validate]
|
||||
validate_twine_file if @options[:validate]
|
||||
|
||||
require_rubyzip
|
||||
|
||||
|
@ -165,7 +165,7 @@ module Twine
|
|||
zipfile.mkdir('Locales')
|
||||
|
||||
formatter = formatter_for_format(@options[:format])
|
||||
@strings.language_codes.each do |lang|
|
||||
@twine_file.language_codes.each do |lang|
|
||||
if @options[:languages] == nil || @options[:languages].length == 0 || @options[:languages].include?(lang)
|
||||
file_name = lang + formatter.extension
|
||||
temp_path = File.join(temp_dir, file_name)
|
||||
|
@ -173,7 +173,7 @@ module Twine
|
|||
|
||||
output = formatter.format_file(lang)
|
||||
unless output
|
||||
Twine::stderr.puts "Skipping file #{file_name} since it would not contain any strings."
|
||||
Twine::stderr.puts "Skipping file #{file_name} since it would not contain any translations."
|
||||
next
|
||||
end
|
||||
|
||||
|
@ -201,7 +201,7 @@ module Twine
|
|||
FileUtils.mkdir_p(File.dirname(real_path))
|
||||
zipfile.extract(entry.name, real_path)
|
||||
begin
|
||||
read_string_file(real_path)
|
||||
read_localization_file(real_path)
|
||||
rescue Twine::Error => e
|
||||
Twine::stderr.puts "#{e.message}"
|
||||
end
|
||||
|
@ -209,28 +209,28 @@ module Twine
|
|||
end
|
||||
end
|
||||
|
||||
output_path = @options[:output_path] || @options[:strings_file]
|
||||
write_strings_data(output_path)
|
||||
output_path = @options[:output_path] || @options[:twine_file]
|
||||
write_twine_data(output_path)
|
||||
end
|
||||
|
||||
def validate_strings_file
|
||||
total_strings = 0
|
||||
def validate_twine_file
|
||||
total_definitions = 0
|
||||
all_keys = Set.new
|
||||
duplicate_keys = Set.new
|
||||
keys_without_tags = Set.new
|
||||
invalid_keys = Set.new
|
||||
valid_key_regex = /^[A-Za-z0-9_]+$/
|
||||
|
||||
@strings.sections.each do |section|
|
||||
section.rows.each do |row|
|
||||
total_strings += 1
|
||||
@twine_file.sections.each do |section|
|
||||
section.definitions.each do |definition|
|
||||
total_definitions += 1
|
||||
|
||||
duplicate_keys.add(row.key) if all_keys.include? row.key
|
||||
all_keys.add(row.key)
|
||||
duplicate_keys.add(definition.key) if all_keys.include? definition.key
|
||||
all_keys.add(definition.key)
|
||||
|
||||
keys_without_tags.add(row.key) if row.tags == nil or row.tags.length == 0
|
||||
keys_without_tags.add(definition.key) if definition.tags == nil or definition.tags.length == 0
|
||||
|
||||
invalid_keys << row.key unless row.key =~ valid_key_regex
|
||||
invalid_keys << definition.key unless definition.key =~ valid_key_regex
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -238,14 +238,14 @@ module Twine
|
|||
join_keys = lambda { |set| set.map { |k| " " + k }.join("\n") }
|
||||
|
||||
unless duplicate_keys.empty?
|
||||
errors << "Found duplicate string key(s):\n#{join_keys.call(duplicate_keys)}"
|
||||
errors << "Found duplicate key(s):\n#{join_keys.call(duplicate_keys)}"
|
||||
end
|
||||
|
||||
if @options[:pedantic]
|
||||
if keys_without_tags.length == total_strings
|
||||
errors << "None of your strings have tags."
|
||||
if keys_without_tags.length == total_definitions
|
||||
errors << "None of your definitions have tags."
|
||||
elsif keys_without_tags.length > 0
|
||||
errors << "Found strings without tags:\n#{join_keys.call(keys_without_tags)}"
|
||||
errors << "Found definitions without tags:\n#{join_keys.call(keys_without_tags)}"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -255,7 +255,7 @@ module Twine
|
|||
|
||||
raise Twine::Error.new errors.join("\n\n") unless errors.empty?
|
||||
|
||||
Twine::stdout.puts "#{@options[:strings_file]} is valid."
|
||||
Twine::stdout.puts "#{@options[:twine_file]} is valid."
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -274,7 +274,7 @@ module Twine
|
|||
|
||||
def determine_language_given_path(path)
|
||||
code = File.basename(path, File.extname(path))
|
||||
return code if @strings.language_codes.include? code
|
||||
return code if @twine_file.language_codes.include? code
|
||||
end
|
||||
|
||||
def formatter_for_format(format)
|
||||
|
@ -284,12 +284,12 @@ module Twine
|
|||
def find_formatter(&block)
|
||||
formatter = Formatters.formatters.find &block
|
||||
return nil unless formatter
|
||||
formatter.strings = @strings
|
||||
formatter.twine_file = @twine_file
|
||||
formatter.options = @options
|
||||
formatter
|
||||
end
|
||||
|
||||
def read_string_file(path, lang = nil)
|
||||
def read_localization_file(path, lang = nil)
|
||||
unless File.file?(path)
|
||||
raise Twine::Error.new("File does not exist: #{path}")
|
||||
end
|
||||
|
@ -317,7 +317,7 @@ module Twine
|
|||
raise Twine::Error.new "Unable to determine language for #{path}"
|
||||
end
|
||||
|
||||
@strings.language_codes << lang unless @strings.language_codes.include? lang
|
||||
@twine_file.language_codes << lang unless @twine_file.language_codes.include? lang
|
||||
|
||||
return formatter, lang
|
||||
end
|
||||
|
|
|
@ -1,15 +1,5 @@
|
|||
module Twine
|
||||
class StringsSection
|
||||
attr_reader :name
|
||||
attr_reader :rows
|
||||
|
||||
def initialize(name)
|
||||
@name = name
|
||||
@rows = []
|
||||
end
|
||||
end
|
||||
|
||||
class StringsRow
|
||||
class TwineDefinition
|
||||
attr_reader :key
|
||||
attr_accessor :comment
|
||||
attr_accessor :tags
|
||||
|
@ -37,7 +27,7 @@ module Twine
|
|||
# The user did not specify any tags. Everything passes.
|
||||
return true
|
||||
elsif @tags == nil
|
||||
# This row has no tags.
|
||||
# This definition has no tags.
|
||||
return reference ? reference.matches_tags?(tags, include_untagged) : include_untagged
|
||||
elsif @tags.empty?
|
||||
return include_untagged
|
||||
|
@ -48,18 +38,28 @@ module Twine
|
|||
return false
|
||||
end
|
||||
|
||||
def translated_string_for_lang(lang)
|
||||
def translation_for_lang(lang)
|
||||
translation = [lang].flatten.map { |l| @translations[l] }.first
|
||||
|
||||
translation = reference.translated_string_for_lang(lang) if translation.nil? && reference
|
||||
translation = reference.translation_for_lang(lang) if translation.nil? && reference
|
||||
|
||||
return translation
|
||||
end
|
||||
end
|
||||
|
||||
class StringsFile
|
||||
class TwineSection
|
||||
attr_reader :name
|
||||
attr_reader :definitions
|
||||
|
||||
def initialize(name)
|
||||
@name = name
|
||||
@definitions = []
|
||||
end
|
||||
end
|
||||
|
||||
class TwineFile
|
||||
attr_reader :sections
|
||||
attr_reader :strings_map
|
||||
attr_reader :definitions_by_key
|
||||
attr_reader :language_codes
|
||||
|
||||
private
|
||||
|
@ -73,7 +73,7 @@ module Twine
|
|||
|
||||
def initialize
|
||||
@sections = []
|
||||
@strings_map = {}
|
||||
@definitions_by_key = {}
|
||||
@language_codes = []
|
||||
end
|
||||
|
||||
|
@ -102,7 +102,7 @@ module Twine
|
|||
File.open(path, 'r:UTF-8') do |f|
|
||||
line_num = 0
|
||||
current_section = nil
|
||||
current_row = nil
|
||||
current_definition = nil
|
||||
while line = f.gets
|
||||
parsed = false
|
||||
line.strip!
|
||||
|
@ -115,20 +115,20 @@ module Twine
|
|||
if line.length > 4 && line[0, 2] == '[['
|
||||
match = /^\[\[(.+)\]\]$/.match(line)
|
||||
if match
|
||||
current_section = StringsSection.new(match[1])
|
||||
current_section = TwineSection.new(match[1])
|
||||
@sections << current_section
|
||||
parsed = true
|
||||
end
|
||||
elsif line.length > 2 && line[0, 1] == '['
|
||||
key = match_key(line)
|
||||
if key
|
||||
current_row = StringsRow.new(key)
|
||||
@strings_map[current_row.key] = current_row
|
||||
current_definition = TwineDefinition.new(key)
|
||||
@definitions_by_key[current_definition.key] = current_definition
|
||||
if !current_section
|
||||
current_section = StringsSection.new('')
|
||||
current_section = TwineSection.new('')
|
||||
@sections << current_section
|
||||
end
|
||||
current_section.rows << current_row
|
||||
current_section.definitions << current_definition
|
||||
parsed = true
|
||||
end
|
||||
else
|
||||
|
@ -141,16 +141,16 @@ module Twine
|
|||
|
||||
case key
|
||||
when 'comment'
|
||||
current_row.comment = value
|
||||
current_definition.comment = value
|
||||
when 'tags'
|
||||
current_row.tags = value.split(',')
|
||||
current_definition.tags = value.split(',')
|
||||
when 'ref'
|
||||
current_row.reference_key = value if value
|
||||
current_definition.reference_key = value if value
|
||||
else
|
||||
if !@language_codes.include? key
|
||||
add_language_code(key)
|
||||
end
|
||||
current_row.translations[key] = value
|
||||
current_definition.translations[key] = value
|
||||
end
|
||||
parsed = true
|
||||
end
|
||||
|
@ -163,9 +163,9 @@ module Twine
|
|||
end
|
||||
|
||||
# resolve_references
|
||||
@strings_map.each do |key, row|
|
||||
next unless row.reference_key
|
||||
row.reference = @strings_map[row.reference_key]
|
||||
@definitions_by_key.each do |key, definition|
|
||||
next unless definition.reference_key
|
||||
definition.reference = @definitions_by_key[definition.reference_key]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -180,26 +180,26 @@ module Twine
|
|||
|
||||
f.puts "[[#{section.name}]]"
|
||||
|
||||
section.rows.each do |row|
|
||||
f.puts "\t[#{row.key}]"
|
||||
section.definitions.each do |definition|
|
||||
f.puts "\t[#{definition.key}]"
|
||||
|
||||
value = write_value(row, dev_lang, f)
|
||||
if !value && !row.reference_key
|
||||
puts "Warning: #{row.key} does not exist in developer language '#{dev_lang}'"
|
||||
value = write_value(definition, dev_lang, f)
|
||||
if !value && !definition.reference_key
|
||||
puts "Warning: #{definition.key} does not exist in developer language '#{dev_lang}'"
|
||||
end
|
||||
|
||||
if row.reference_key
|
||||
f.puts "\t\tref = #{row.reference_key}"
|
||||
if definition.reference_key
|
||||
f.puts "\t\tref = #{definition.reference_key}"
|
||||
end
|
||||
if row.tags && row.tags.length > 0
|
||||
tag_str = row.tags.join(',')
|
||||
if definition.tags && definition.tags.length > 0
|
||||
tag_str = definition.tags.join(',')
|
||||
f.puts "\t\ttags = #{tag_str}"
|
||||
end
|
||||
if row.raw_comment and row.raw_comment.length > 0
|
||||
f.puts "\t\tcomment = #{row.raw_comment}"
|
||||
if definition.raw_comment and definition.raw_comment.length > 0
|
||||
f.puts "\t\tcomment = #{definition.raw_comment}"
|
||||
end
|
||||
@language_codes[1..-1].each do |lang|
|
||||
write_value(row, lang, f)
|
||||
write_value(definition, lang, f)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -208,8 +208,8 @@ module Twine
|
|||
|
||||
private
|
||||
|
||||
def write_value(row, language, file)
|
||||
value = row.translations[language]
|
||||
def write_value(definition, language, file)
|
||||
value = definition.translations[language]
|
||||
return nil unless value
|
||||
|
||||
if value[0] == ' ' || value[-1] == ' ' || (value[0] == '`' && value[-1] == '`')
|
|
@ -2,11 +2,11 @@ require 'twine_test_case'
|
|||
|
||||
class CommandTestCase < TwineTestCase
|
||||
def prepare_mock_formatter(formatter_class)
|
||||
strings = Twine::StringsFile.new
|
||||
strings.language_codes.concat KNOWN_LANGUAGES
|
||||
twine_file = Twine::TwineFile.new
|
||||
twine_file.language_codes.concat KNOWN_LANGUAGES
|
||||
|
||||
formatter = formatter_class.new
|
||||
formatter.strings = strings
|
||||
formatter.twine_file = twine_file
|
||||
Twine::Formatters.formatters.clear
|
||||
Twine::Formatters.formatters << formatter
|
||||
formatter
|
||||
|
|
|
@ -5,74 +5,74 @@ class TestAbstractFormatter < TwineTestCase
|
|||
def setup
|
||||
super
|
||||
|
||||
@strings = build_twine_file 'en', 'fr' do
|
||||
@twine_file = build_twine_file 'en', 'fr' do
|
||||
add_section 'Section' do
|
||||
add_row key1: 'value1-english'
|
||||
add_row key2: { en: 'value2-english', fr: 'value2-french' }
|
||||
add_definition key1: 'value1-english'
|
||||
add_definition key2: { en: 'value2-english', fr: 'value2-french' }
|
||||
end
|
||||
end
|
||||
|
||||
@formatter = Twine::Formatters::Abstract.new
|
||||
@formatter.strings = @strings
|
||||
@formatter.twine_file = @twine_file
|
||||
end
|
||||
|
||||
def test_set_translation_updates_existing_value
|
||||
@formatter.set_translation_for_key 'key1', 'en', 'value1-english-updated'
|
||||
|
||||
assert_equal 'value1-english-updated', @strings.strings_map['key1'].translations['en']
|
||||
assert_equal 'value1-english-updated', @twine_file.definitions_by_key['key1'].translations['en']
|
||||
end
|
||||
|
||||
def test_set_translation_does_not_alter_other_language
|
||||
@formatter.set_translation_for_key 'key2', 'en', 'value2-english-updated'
|
||||
|
||||
assert_equal 'value2-french', @strings.strings_map['key2'].translations['fr']
|
||||
assert_equal 'value2-french', @twine_file.definitions_by_key['key2'].translations['fr']
|
||||
end
|
||||
|
||||
def test_set_translation_escapes_newlines
|
||||
@formatter.set_translation_for_key 'key1', 'en', "new\nline"
|
||||
|
||||
assert_equal 'new\nline', @strings.strings_map['key1'].translations['en']
|
||||
assert_equal 'new\nline', @twine_file.definitions_by_key['key1'].translations['en']
|
||||
end
|
||||
|
||||
def test_set_translation_adds_translation_to_existing_key
|
||||
@formatter.set_translation_for_key 'key1', 'fr', 'value1-french'
|
||||
|
||||
assert_equal 'value1-french', @strings.strings_map['key1'].translations['fr']
|
||||
assert_equal 'value1-french', @twine_file.definitions_by_key['key1'].translations['fr']
|
||||
end
|
||||
|
||||
def test_set_translation_does_not_add_new_key
|
||||
@formatter.set_translation_for_key 'new-key', 'en', 'new-key-english'
|
||||
|
||||
assert_nil @strings.strings_map['new-key']
|
||||
assert_nil @twine_file.definitions_by_key['new-key']
|
||||
end
|
||||
|
||||
def test_set_translation_consume_all_adds_new_key
|
||||
formatter = Twine::Formatters::Abstract.new
|
||||
formatter.strings = @strings
|
||||
formatter.twine_file = @twine_file
|
||||
formatter.options = { consume_all: true }
|
||||
formatter.set_translation_for_key 'new-key', 'en', 'new-key-english'
|
||||
|
||||
assert_equal 'new-key-english', @strings.strings_map['new-key'].translations['en']
|
||||
assert_equal 'new-key-english', @twine_file.definitions_by_key['new-key'].translations['en']
|
||||
end
|
||||
|
||||
def test_set_translation_consume_all_adds_tags
|
||||
random_tag = SecureRandom.uuid
|
||||
formatter = Twine::Formatters::Abstract.new
|
||||
formatter.strings = @strings
|
||||
formatter.twine_file = @twine_file
|
||||
formatter.options = { consume_all: true, tags: [random_tag] }
|
||||
formatter.set_translation_for_key 'new-key', 'en', 'new-key-english'
|
||||
|
||||
assert_equal [random_tag], @strings.strings_map['new-key'].tags
|
||||
assert_equal [random_tag], @twine_file.definitions_by_key['new-key'].tags
|
||||
end
|
||||
|
||||
def test_set_translation_adds_new_keys_to_category_uncategoriezed
|
||||
formatter = Twine::Formatters::Abstract.new
|
||||
formatter.strings = @strings
|
||||
formatter.twine_file = @twine_file
|
||||
formatter.options = { consume_all: true }
|
||||
formatter.set_translation_for_key 'new-key', 'en', 'new-key-english'
|
||||
|
||||
assert_equal 'Uncategorized', @strings.sections[0].name
|
||||
assert_equal 'new-key', @strings.sections[0].rows[0].key
|
||||
assert_equal 'Uncategorized', @twine_file.sections[0].name
|
||||
assert_equal 'new-key', @twine_file.sections[0].definitions[0].key
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -80,27 +80,27 @@ class TestAbstractFormatter < TwineTestCase
|
|||
def setup
|
||||
super
|
||||
|
||||
@strings = build_twine_file 'en', 'fr' do
|
||||
@twine_file = build_twine_file 'en', 'fr' do
|
||||
add_section 'Section' do
|
||||
add_row refkey: 'ref-value'
|
||||
add_row key: :refkey
|
||||
add_definition refkey: 'ref-value'
|
||||
add_definition key: :refkey
|
||||
end
|
||||
end
|
||||
|
||||
@formatter = Twine::Formatters::Abstract.new
|
||||
@formatter.strings = @strings
|
||||
@formatter.twine_file = @twine_file
|
||||
end
|
||||
|
||||
def test_set_translation_does_not_add_unchanged_translation
|
||||
@formatter.set_translation_for_key 'key', 'en', 'ref-value'
|
||||
|
||||
assert_nil @strings.strings_map['key'].translations['en']
|
||||
assert_nil @twine_file.definitions_by_key['key'].translations['en']
|
||||
end
|
||||
|
||||
def test_set_translation_adds_changed_translation
|
||||
@formatter.set_translation_for_key 'key', 'en', 'changed value'
|
||||
|
||||
assert_equal 'changed value', @strings.strings_map['key'].translations['en']
|
||||
assert_equal 'changed value', @twine_file.definitions_by_key['key'].translations['en']
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -108,28 +108,28 @@ class TestAbstractFormatter < TwineTestCase
|
|||
def setup
|
||||
super
|
||||
|
||||
@strings = build_twine_file 'en' do
|
||||
@twine_file = build_twine_file 'en' do
|
||||
add_section 'Section' do
|
||||
add_row key: 'value'
|
||||
add_definition key: 'value'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_set_comment_for_key_does_not_update_comment
|
||||
formatter = Twine::Formatters::Abstract.new
|
||||
formatter.strings = @strings
|
||||
formatter.twine_file = @twine_file
|
||||
formatter.set_comment_for_key('key', 'comment')
|
||||
|
||||
assert_nil formatter.strings.strings_map['key'].comment
|
||||
assert_nil formatter.twine_file.definitions_by_key['key'].comment
|
||||
end
|
||||
|
||||
def test_set_comment_for_key_updates_comment_with_update_comments
|
||||
formatter = Twine::Formatters::Abstract.new
|
||||
formatter.strings = @strings
|
||||
formatter.twine_file = @twine_file
|
||||
formatter.options = { consume_comments: true }
|
||||
formatter.set_comment_for_key('key', 'comment')
|
||||
|
||||
assert_equal 'comment', formatter.strings.strings_map['key'].comment
|
||||
assert_equal 'comment', formatter.twine_file.definitions_by_key['key'].comment
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -137,28 +137,28 @@ class TestAbstractFormatter < TwineTestCase
|
|||
def setup
|
||||
super
|
||||
|
||||
@strings = build_twine_file 'en' do
|
||||
@twine_file = build_twine_file 'en' do
|
||||
add_section 'Section' do
|
||||
add_row refkey: 'ref-value', comment: 'reference comment'
|
||||
add_row key: 'value', ref: :refkey
|
||||
add_definition refkey: 'ref-value', comment: 'reference comment'
|
||||
add_definition key: 'value', ref: :refkey
|
||||
end
|
||||
end
|
||||
|
||||
@formatter = Twine::Formatters::Abstract.new
|
||||
@formatter.strings = @strings
|
||||
@formatter.twine_file = @twine_file
|
||||
@formatter.options = { consume_comments: true }
|
||||
end
|
||||
|
||||
def test_set_comment_does_not_add_unchanged_comment
|
||||
@formatter.set_comment_for_key 'key', 'reference comment'
|
||||
|
||||
assert_nil @strings.strings_map['key'].raw_comment
|
||||
assert_nil @twine_file.definitions_by_key['key'].raw_comment
|
||||
end
|
||||
|
||||
def test_set_comment_adds_changed_comment
|
||||
@formatter.set_comment_for_key 'key', 'changed comment'
|
||||
|
||||
assert_equal 'changed comment', @strings.strings_map['key'].raw_comment
|
||||
assert_equal 'changed comment', @twine_file.definitions_by_key['key'].raw_comment
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ class CLITestCase < TwineTestCase
|
|||
def setup
|
||||
super
|
||||
|
||||
@strings_file_path = File.join @output_dir, SecureRandom.uuid
|
||||
@twine_file_path = File.join @output_dir, SecureRandom.uuid
|
||||
@input_path = File.join @output_dir, SecureRandom.uuid
|
||||
@input_dir = @output_dir
|
||||
end
|
||||
|
@ -13,205 +13,205 @@ class CLITestCase < TwineTestCase
|
|||
@options = Twine::CLI::parse command.split
|
||||
end
|
||||
|
||||
class TestValidateStringsFile < CLITestCase
|
||||
class TestValidateTwineFile < CLITestCase
|
||||
def test_command
|
||||
parse "validate-strings-file #{@strings_file_path}"
|
||||
parse "validate-twine-file #{@twine_file_path}"
|
||||
|
||||
assert_equal 'validate-strings-file', @options[:command]
|
||||
assert_equal @strings_file_path, @options[:strings_file]
|
||||
assert_equal 'validate-twine-file', @options[:command]
|
||||
assert_equal @twine_file_path, @options[:twine_file]
|
||||
end
|
||||
|
||||
def test_pedantic
|
||||
parse "validate-strings-file #{@strings_file_path} --pedantic"
|
||||
parse "validate-twine-file #{@twine_file_path} --pedantic"
|
||||
assert @options[:pedantic]
|
||||
end
|
||||
|
||||
def test_missing_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse 'validate-strings-file'
|
||||
parse 'validate-twine-file'
|
||||
end
|
||||
end
|
||||
|
||||
def test_extra_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse 'validate-strings-file strings extra'
|
||||
parse 'validate-twine-file twine_file extra'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestGenerateStringFile < CLITestCase
|
||||
class TestGenerateLocalizationFile < CLITestCase
|
||||
def test_command
|
||||
parse "generate-string-file #{@strings_file_path} #{@output_path}"
|
||||
parse "generate-localization-file #{@twine_file_path} #{@output_path}"
|
||||
|
||||
assert_equal 'generate-string-file', @options[:command]
|
||||
assert_equal @strings_file_path, @options[:strings_file]
|
||||
assert_equal 'generate-localization-file', @options[:command]
|
||||
assert_equal @twine_file_path, @options[:twine_file]
|
||||
assert_equal @output_path, @options[:output_path]
|
||||
end
|
||||
|
||||
def test_missing_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse 'generate-string-file strings'
|
||||
parse 'generate-localization-file twine_file'
|
||||
end
|
||||
end
|
||||
|
||||
def test_validate
|
||||
parse "generate-string-file #{@strings_file_path} #{@output_path} --validate"
|
||||
parse "generate-localization-file #{@twine_file_path} #{@output_path} --validate"
|
||||
assert @options[:validate]
|
||||
end
|
||||
|
||||
def test_extra_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse 'generate-string-file strings output extra'
|
||||
parse 'generate-localization-file twine_file output extra'
|
||||
end
|
||||
end
|
||||
|
||||
def test_only_allows_one_language
|
||||
assert_raises Twine::Error do
|
||||
parse "generate-string-file strings output --lang en,fr"
|
||||
parse "generate-localization-file twine_file output --lang en,fr"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestGenerateAllStringFiles < CLITestCase
|
||||
class TestGenerateAllLocalizationFiles < CLITestCase
|
||||
def test_command
|
||||
parse "generate-all-string-files #{@strings_file_path} #{@output_dir}"
|
||||
parse "generate-all-localization-files #{@twine_file_path} #{@output_dir}"
|
||||
|
||||
assert_equal 'generate-all-string-files', @options[:command]
|
||||
assert_equal @strings_file_path, @options[:strings_file]
|
||||
assert_equal 'generate-all-localization-files', @options[:command]
|
||||
assert_equal @twine_file_path, @options[:twine_file]
|
||||
assert_equal @output_dir, @options[:output_path]
|
||||
end
|
||||
|
||||
def test_missing_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse "generate-all-string-files strings"
|
||||
parse "generate-all-localization-files twine_file"
|
||||
end
|
||||
end
|
||||
|
||||
def test_validate
|
||||
parse "generate-all-string-files #{@strings_file_path} #{@output_dir} --validate"
|
||||
parse "generate-all-localization-files #{@twine_file_path} #{@output_dir} --validate"
|
||||
assert @options[:validate]
|
||||
end
|
||||
|
||||
def test_extra_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse "generate-all-string-files strings output extra"
|
||||
parse "generate-all-localization-files twine_file output extra"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestConsumeStringFile < CLITestCase
|
||||
class TestConsumeLocalizationFile < CLITestCase
|
||||
def test_command
|
||||
parse "consume-string-file #{@strings_file_path} #{@input_path}"
|
||||
parse "consume-localization-file #{@twine_file_path} #{@input_path}"
|
||||
|
||||
assert_equal 'consume-string-file', @options[:command]
|
||||
assert_equal @strings_file_path, @options[:strings_file]
|
||||
assert_equal 'consume-localization-file', @options[:command]
|
||||
assert_equal @twine_file_path, @options[:twine_file]
|
||||
assert_equal @input_path, @options[:input_path]
|
||||
end
|
||||
|
||||
def test_missing_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse "consume-string-file strings"
|
||||
parse "consume-localization-file twine_file"
|
||||
end
|
||||
end
|
||||
|
||||
def test_extra_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse "consume-string-file strings output extra"
|
||||
parse "consume-localization-file twine_file output extra"
|
||||
end
|
||||
end
|
||||
|
||||
def test_only_allows_one_language
|
||||
assert_raises Twine::Error do
|
||||
parse "consume-string-file strings output --lang en,fr"
|
||||
parse "consume-localization-file twine_file output --lang en,fr"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestConsumeAllStringFiles < CLITestCase
|
||||
class TestConsumeAllLocalizationFiles < CLITestCase
|
||||
def test_command
|
||||
parse "consume-all-string-files #{@strings_file_path} #{@input_dir}"
|
||||
parse "consume-all-localization-files #{@twine_file_path} #{@input_dir}"
|
||||
|
||||
assert_equal 'consume-all-string-files', @options[:command]
|
||||
assert_equal @strings_file_path, @options[:strings_file]
|
||||
assert_equal 'consume-all-localization-files', @options[:command]
|
||||
assert_equal @twine_file_path, @options[:twine_file]
|
||||
assert_equal @input_dir, @options[:input_path]
|
||||
end
|
||||
|
||||
def test_missing_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse "consume-all-string-files strings"
|
||||
parse "consume-all-localization-files twine_file"
|
||||
end
|
||||
end
|
||||
|
||||
def test_extra_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse "consume-all-string-files strings output extra"
|
||||
parse "consume-all-localization-files twine_file output extra"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestGenerateLocDrop < CLITestCase
|
||||
def test_command
|
||||
parse "generate-loc-drop #{@strings_file_path} #{@output_path} --format apple"
|
||||
parse "generate-loc-drop #{@twine_file_path} #{@output_path} --format apple"
|
||||
|
||||
assert_equal 'generate-loc-drop', @options[:command]
|
||||
assert_equal @strings_file_path, @options[:strings_file]
|
||||
assert_equal @twine_file_path, @options[:twine_file]
|
||||
assert_equal @output_path, @options[:output_path]
|
||||
end
|
||||
|
||||
def test_missing_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse "generate-loc-drop strings --format apple"
|
||||
parse "generate-loc-drop twine_file --format apple"
|
||||
end
|
||||
end
|
||||
|
||||
def test_validate
|
||||
parse "generate-loc-drop #{@strings_file_path} #{@output_path} --format apple --validate"
|
||||
parse "generate-loc-drop #{@twine_file_path} #{@output_path} --format apple --validate"
|
||||
assert @options[:validate]
|
||||
end
|
||||
|
||||
def test_extra_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse "generate-loc-drop strings output extra --format apple"
|
||||
parse "generate-loc-drop twine_file output extra --format apple"
|
||||
end
|
||||
end
|
||||
|
||||
def test_format_needed
|
||||
assert_raises Twine::Error do
|
||||
parse "generate-loc-drop strings output"
|
||||
parse "generate-loc-drop twine_file output"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestConsumeLocDrop < CLITestCase
|
||||
def test_command
|
||||
parse "consume-loc-drop #{@strings_file_path} #{@input_path}"
|
||||
parse "consume-loc-drop #{@twine_file_path} #{@input_path}"
|
||||
|
||||
assert_equal 'consume-loc-drop', @options[:command]
|
||||
assert_equal @strings_file_path, @options[:strings_file]
|
||||
assert_equal @twine_file_path, @options[:twine_file]
|
||||
assert_equal @input_path, @options[:input_path]
|
||||
end
|
||||
|
||||
def test_missing_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse "consume-loc-drop strings"
|
||||
parse "consume-loc-drop twine_file"
|
||||
end
|
||||
end
|
||||
|
||||
def test_extra_parameter
|
||||
assert_raises Twine::Error do
|
||||
parse "consume-loc-drop strings input extra"
|
||||
parse "consume-loc-drop twine_file input extra"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestParameters < CLITestCase
|
||||
def parse_with(parameter)
|
||||
parse 'validate-strings-file input.txt ' + parameter
|
||||
parse 'validate-twine-file input.txt ' + parameter
|
||||
end
|
||||
|
||||
def test_default_options
|
||||
parse_with ''
|
||||
expected = {command: 'validate-strings-file', strings_file: 'input.txt', include: :all}
|
||||
expected = {command: 'validate-twine-file', twine_file: 'input.txt', include: :all}
|
||||
assert_equal expected, @options
|
||||
end
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ class TestConsumeLocDrop < CommandTestCase
|
|||
|
||||
@twine_file = build_twine_file 'en', 'es' do
|
||||
add_section 'Section' do
|
||||
add_row key1: 'value1'
|
||||
add_definition key1: 'value1'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -21,7 +21,7 @@ class TestConsumeLocDrop < CommandTestCase
|
|||
def test_consumes_zip_file
|
||||
@runner.consume_loc_drop
|
||||
|
||||
assert @twine_file.strings_map['key1'].translations['en'], 'value1-english'
|
||||
assert @twine_file.strings_map['key1'].translations['es'], 'value1-spanish'
|
||||
assert @twine_file.definitions_by_key['key1'].translations['en'], 'value1-english'
|
||||
assert @twine_file.definitions_by_key['key1'].translations['es'], 'value1-spanish'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
require 'command_test_case'
|
||||
|
||||
class TestConsumeStringFile < CommandTestCase
|
||||
class TestConsumeLocalizationFile < CommandTestCase
|
||||
def new_runner(language, file)
|
||||
options = {}
|
||||
options[:output_path] = File.join(@output_dir, file) if file
|
||||
|
@ -8,10 +8,10 @@ class TestConsumeStringFile < CommandTestCase
|
|||
FileUtils.touch options[:input_path]
|
||||
options[:languages] = language if language
|
||||
|
||||
@strings = Twine::StringsFile.new
|
||||
@strings.language_codes.concat KNOWN_LANGUAGES
|
||||
@twine_file = Twine::TwineFile.new
|
||||
@twine_file.language_codes.concat KNOWN_LANGUAGES
|
||||
|
||||
Twine::Runner.new(options, @strings)
|
||||
Twine::Runner.new(options, @twine_file)
|
||||
end
|
||||
|
||||
def prepare_mock_read_formatter(formatter_class)
|
||||
|
@ -22,25 +22,25 @@ class TestConsumeStringFile < CommandTestCase
|
|||
def test_deducts_android_format_from_output_path
|
||||
prepare_mock_read_formatter Twine::Formatters::Android
|
||||
|
||||
new_runner('fr', 'fr.xml').consume_string_file
|
||||
new_runner('fr', 'fr.xml').consume_localization_file
|
||||
end
|
||||
|
||||
def test_deducts_apple_format_from_output_path
|
||||
prepare_mock_read_formatter Twine::Formatters::Apple
|
||||
|
||||
new_runner('fr', 'fr.strings').consume_string_file
|
||||
new_runner('fr', 'fr.strings').consume_localization_file
|
||||
end
|
||||
|
||||
def test_deducts_jquery_format_from_output_path
|
||||
prepare_mock_read_formatter Twine::Formatters::JQuery
|
||||
|
||||
new_runner('fr', 'fr.json').consume_string_file
|
||||
new_runner('fr', 'fr.json').consume_localization_file
|
||||
end
|
||||
|
||||
def test_deducts_gettext_format_from_output_path
|
||||
prepare_mock_read_formatter Twine::Formatters::Gettext
|
||||
|
||||
new_runner('fr', 'fr.po').consume_string_file
|
||||
new_runner('fr', 'fr.po').consume_localization_file
|
||||
end
|
||||
|
||||
def test_deducts_language_from_input_path
|
||||
|
@ -48,7 +48,7 @@ class TestConsumeStringFile < CommandTestCase
|
|||
formatter = prepare_mock_formatter Twine::Formatters::Android
|
||||
formatter.expects(:read).with(anything, random_language)
|
||||
|
||||
new_runner(nil, "#{random_language}.xml").consume_string_file
|
||||
new_runner(nil, "#{random_language}.xml").consume_localization_file
|
||||
end
|
||||
|
||||
class TestEncodings < CommandTestCase
|
||||
|
@ -75,10 +75,10 @@ class TestConsumeStringFile < CommandTestCase
|
|||
options[:encoding] = encoding if encoding
|
||||
options[:languages] = 'en'
|
||||
|
||||
@strings = Twine::StringsFile.new
|
||||
@strings.language_codes.concat KNOWN_LANGUAGES
|
||||
@twine_file = Twine::TwineFile.new
|
||||
@twine_file.language_codes.concat KNOWN_LANGUAGES
|
||||
|
||||
Twine::Runner.new(options, @strings)
|
||||
Twine::Runner.new(options, @twine_file)
|
||||
end
|
||||
|
||||
def setup
|
||||
|
@ -88,31 +88,31 @@ class TestConsumeStringFile < CommandTestCase
|
|||
|
||||
def test_reads_utf8
|
||||
formatter = prepare_mock_formatter DummyFormatter
|
||||
new_runner(fixture_path('enc_utf8.dummy')).consume_string_file
|
||||
new_runner(fixture_path('enc_utf8.dummy')).consume_localization_file
|
||||
assert_equal @expected_content, formatter.content
|
||||
end
|
||||
|
||||
def test_reads_utf16le_bom
|
||||
formatter = prepare_mock_formatter DummyFormatter
|
||||
new_runner(fixture_path('enc_utf16le_bom.dummy')).consume_string_file
|
||||
new_runner(fixture_path('enc_utf16le_bom.dummy')).consume_localization_file
|
||||
assert_equal @expected_content, formatter.content
|
||||
end
|
||||
|
||||
def test_reads_utf16be_bom
|
||||
formatter = prepare_mock_formatter DummyFormatter
|
||||
new_runner(fixture_path('enc_utf16be_bom.dummy')).consume_string_file
|
||||
new_runner(fixture_path('enc_utf16be_bom.dummy')).consume_localization_file
|
||||
assert_equal @expected_content, formatter.content
|
||||
end
|
||||
|
||||
def test_reads_utf16le
|
||||
formatter = prepare_mock_formatter DummyFormatter
|
||||
new_runner(fixture_path('enc_utf16le.dummy'), 'UTF-16LE').consume_string_file
|
||||
new_runner(fixture_path('enc_utf16le.dummy'), 'UTF-16LE').consume_localization_file
|
||||
assert_equal @expected_content, formatter.content
|
||||
end
|
||||
|
||||
def test_reads_utf16be
|
||||
formatter = prepare_mock_formatter DummyFormatter
|
||||
new_runner(fixture_path('enc_utf16be.dummy'), 'UTF-16BE').consume_string_file
|
||||
new_runner(fixture_path('enc_utf16be.dummy'), 'UTF-16BE').consume_localization_file
|
||||
assert_equal @expected_content, formatter.content
|
||||
end
|
||||
end
|
|
@ -6,33 +6,33 @@ class FormatterTest < TwineTestCase
|
|||
|
||||
@twine_file = build_twine_file 'en' do
|
||||
add_section 'Section 1' do
|
||||
add_row key1: 'value1-english', comment: 'comment key1'
|
||||
add_row key2: 'value2-english'
|
||||
add_definition key1: 'value1-english', comment: 'comment key1'
|
||||
add_definition key2: 'value2-english'
|
||||
end
|
||||
|
||||
add_section 'Section 2' do
|
||||
add_row key3: 'value3-english'
|
||||
add_row key4: 'value4-english', comment: 'comment key4'
|
||||
add_definition key3: 'value3-english'
|
||||
add_definition key4: 'value4-english', comment: 'comment key4'
|
||||
end
|
||||
end
|
||||
|
||||
@strings = Twine::StringsFile.new
|
||||
@empty_twine_file = Twine::TwineFile.new
|
||||
@formatter = formatter_class.new
|
||||
@formatter.strings = @strings
|
||||
@formatter.twine_file = @empty_twine_file
|
||||
@formatter.options = { consume_all: true, consume_comments: true }
|
||||
end
|
||||
|
||||
def assert_translations_read_correctly
|
||||
1.upto(4) do |i|
|
||||
assert_equal "value#{i}-english", @strings.strings_map["key#{i}"].translations['en']
|
||||
assert_equal "value#{i}-english", @empty_twine_file.definitions_by_key["key#{i}"].translations['en']
|
||||
end
|
||||
end
|
||||
|
||||
def assert_file_contents_read_correctly
|
||||
assert_translations_read_correctly
|
||||
|
||||
assert_equal "comment key1", @strings.strings_map["key1"].comment
|
||||
assert_equal "comment key4", @strings.strings_map["key4"].comment
|
||||
assert_equal "comment key1", @empty_twine_file.definitions_by_key["key1"].comment
|
||||
assert_equal "comment key4", @empty_twine_file.definitions_by_key["key4"].comment
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -49,27 +49,27 @@ class TestAndroidFormatter < FormatterTest
|
|||
|
||||
def test_set_translation_converts_leading_spaces
|
||||
@formatter.set_translation_for_key 'key1', 'en', "\u0020value"
|
||||
assert_equal ' value', @strings.strings_map['key1'].translations['en']
|
||||
assert_equal ' value', @empty_twine_file.definitions_by_key['key1'].translations['en']
|
||||
end
|
||||
|
||||
def test_set_translation_coverts_trailing_spaces
|
||||
@formatter.set_translation_for_key 'key1', 'en', "value\u0020\u0020"
|
||||
assert_equal 'value ', @strings.strings_map['key1'].translations['en']
|
||||
assert_equal 'value ', @empty_twine_file.definitions_by_key['key1'].translations['en']
|
||||
end
|
||||
|
||||
def test_set_translation_converts_string_placeholders
|
||||
@formatter.set_translation_for_key 'key1', 'en', "value %s"
|
||||
assert_equal 'value %@', @strings.strings_map['key1'].translations['en']
|
||||
assert_equal 'value %@', @empty_twine_file.definitions_by_key['key1'].translations['en']
|
||||
end
|
||||
|
||||
def test_set_translation_unescapes_at_signs
|
||||
@formatter.set_translation_for_key 'key1', 'en', '\@value'
|
||||
assert_equal '@value', @strings.strings_map['key1'].translations['en']
|
||||
assert_equal '@value', @empty_twine_file.definitions_by_key['key1'].translations['en']
|
||||
end
|
||||
|
||||
def test_format_file
|
||||
formatter = Twine::Formatters::Android.new
|
||||
formatter.strings = @twine_file
|
||||
formatter.twine_file = @twine_file
|
||||
assert_equal content('formatter_android.xml'), formatter.format_file('en')
|
||||
end
|
||||
|
||||
|
@ -139,47 +139,47 @@ class TestAppleFormatter < FormatterTest
|
|||
|
||||
def test_reads_quoted_keys
|
||||
@formatter.read StringIO.new('"key" = "value"'), 'en'
|
||||
assert_equal 'value', @strings.strings_map['key'].translations['en']
|
||||
assert_equal 'value', @empty_twine_file.definitions_by_key['key'].translations['en']
|
||||
end
|
||||
|
||||
def test_reads_unquoted_keys
|
||||
@formatter.read StringIO.new('key = "value"'), 'en'
|
||||
assert_equal 'value', @strings.strings_map['key'].translations['en']
|
||||
assert_equal 'value', @empty_twine_file.definitions_by_key['key'].translations['en']
|
||||
end
|
||||
|
||||
def test_ignores_leading_whitespace_before_quoted_keys
|
||||
@formatter.read StringIO.new("\t \"key\" = \"value\""), 'en'
|
||||
assert_equal 'value', @strings.strings_map['key'].translations['en']
|
||||
assert_equal 'value', @empty_twine_file.definitions_by_key['key'].translations['en']
|
||||
end
|
||||
|
||||
def test_ignores_leading_whitespace_before_unquoted_keys
|
||||
@formatter.read StringIO.new("\t key = \"value\""), 'en'
|
||||
assert_equal 'value', @strings.strings_map['key'].translations['en']
|
||||
assert_equal 'value', @empty_twine_file.definitions_by_key['key'].translations['en']
|
||||
end
|
||||
|
||||
def test_allows_quotes_in_quoted_keys
|
||||
@formatter.read StringIO.new('"ke\"y" = "value"'), 'en'
|
||||
assert_equal 'value', @strings.strings_map['ke"y'].translations['en']
|
||||
assert_equal 'value', @empty_twine_file.definitions_by_key['ke"y'].translations['en']
|
||||
end
|
||||
|
||||
def test_does_not_allow_quotes_in_quoted_keys
|
||||
@formatter.read StringIO.new('ke"y = "value"'), 'en'
|
||||
assert_nil @strings.strings_map['key']
|
||||
assert_nil @empty_twine_file.definitions_by_key['key']
|
||||
end
|
||||
|
||||
def test_allows_equal_signs_in_quoted_keys
|
||||
@formatter.read StringIO.new('"k=ey" = "value"'), 'en'
|
||||
assert_equal 'value', @strings.strings_map['k=ey'].translations['en']
|
||||
assert_equal 'value', @empty_twine_file.definitions_by_key['k=ey'].translations['en']
|
||||
end
|
||||
|
||||
def test_does_not_allow_equal_signs_in_unquoted_keys
|
||||
@formatter.read StringIO.new('k=ey = "value"'), 'en'
|
||||
assert_nil @strings.strings_map['key']
|
||||
assert_nil @empty_twine_file.definitions_by_key['key']
|
||||
end
|
||||
|
||||
def test_format_file
|
||||
formatter = Twine::Formatters::Apple.new
|
||||
formatter.strings = @twine_file
|
||||
formatter.twine_file = @twine_file
|
||||
assert_equal content('formatter_apple.strings'), formatter.format_file('en')
|
||||
end
|
||||
|
||||
|
@ -210,7 +210,7 @@ class TestJQueryFormatter < FormatterTest
|
|||
|
||||
def test_format_file
|
||||
formatter = Twine::Formatters::JQuery.new
|
||||
formatter.strings = @twine_file
|
||||
formatter.twine_file = @twine_file
|
||||
assert_equal content('formatter_jquery.json'), formatter.format_file('en')
|
||||
end
|
||||
|
||||
|
@ -234,12 +234,12 @@ class TestGettextFormatter < FormatterTest
|
|||
def test_read_with_multiple_line_value
|
||||
@formatter.read content_io('gettext_multiline.po'), 'en'
|
||||
|
||||
assert_equal 'multiline\nstring', @strings.strings_map['key1'].translations['en']
|
||||
assert_equal 'multiline\nstring', @empty_twine_file.definitions_by_key['key1'].translations['en']
|
||||
end
|
||||
|
||||
def test_format_file
|
||||
formatter = Twine::Formatters::Gettext.new
|
||||
formatter.strings = @twine_file
|
||||
formatter.twine_file = @twine_file
|
||||
assert_equal content('formatter_gettext.po'), formatter.format_file('en')
|
||||
end
|
||||
|
||||
|
@ -260,7 +260,7 @@ class TestTizenFormatter < FormatterTest
|
|||
|
||||
def test_format_file
|
||||
formatter = Twine::Formatters::Tizen.new
|
||||
formatter.strings = @twine_file
|
||||
formatter.twine_file = @twine_file
|
||||
assert_equal content('formatter_tizen.xml'), formatter.format_file('en')
|
||||
end
|
||||
|
||||
|
@ -279,7 +279,7 @@ class TestDjangoFormatter < FormatterTest
|
|||
|
||||
def test_format_file
|
||||
formatter = Twine::Formatters::Django.new
|
||||
formatter.strings = @twine_file
|
||||
formatter.twine_file = @twine_file
|
||||
assert_equal content('formatter_django.po'), formatter.format_file('en')
|
||||
end
|
||||
end
|
||||
|
@ -297,7 +297,7 @@ class TestFlashFormatter < FormatterTest
|
|||
|
||||
def test_format_file
|
||||
formatter = Twine::Formatters::Flash.new
|
||||
formatter.strings = @twine_file
|
||||
formatter.twine_file = @twine_file
|
||||
assert_equal content('formatter_flash.properties'), formatter.format_file('en')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
require 'command_test_case'
|
||||
|
||||
class TestGenerateAllStringFiles < CommandTestCase
|
||||
class TestGenerateAllLocalizationFiles < CommandTestCase
|
||||
def new_runner(create_folders, twine_file = nil)
|
||||
options = {}
|
||||
options[:output_path] = @output_dir
|
||||
|
@ -10,7 +10,7 @@ class TestGenerateAllStringFiles < CommandTestCase
|
|||
unless twine_file
|
||||
twine_file = build_twine_file 'en', 'es' do
|
||||
add_section 'Section' do
|
||||
add_row key: 'value'
|
||||
add_definition key: 'value'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -18,51 +18,51 @@ class TestGenerateAllStringFiles < CommandTestCase
|
|||
Twine::Runner.new(options, twine_file)
|
||||
end
|
||||
|
||||
class TestDoNotCreateFolders < TestGenerateAllStringFiles
|
||||
class TestDoNotCreateFolders < TestGenerateAllLocalizationFiles
|
||||
def new_runner(twine_file = nil)
|
||||
super(false, twine_file)
|
||||
end
|
||||
|
||||
def test_fails_if_output_folder_does_not_exist
|
||||
assert_raises Twine::Error do
|
||||
new_runner.generate_all_string_files
|
||||
new_runner.generate_all_localization_files
|
||||
end
|
||||
end
|
||||
|
||||
def test_does_not_create_language_folders
|
||||
Dir.mkdir File.join @output_dir, 'en.lproj'
|
||||
new_runner.generate_all_string_files
|
||||
new_runner.generate_all_localization_files
|
||||
refute File.exists?(File.join(@output_dir, 'es.lproj')), "language folder should not be created"
|
||||
end
|
||||
|
||||
def test_prints_empty_file_warnings
|
||||
Dir.mkdir File.join @output_dir, 'en.lproj'
|
||||
empty_twine_file = build_twine_file('en') {}
|
||||
new_runner(empty_twine_file).generate_all_string_files
|
||||
new_runner(empty_twine_file).generate_all_localization_files
|
||||
assert_match "Skipping file at path", Twine::stderr.string
|
||||
end
|
||||
end
|
||||
|
||||
class TestCreateFolders < TestGenerateAllStringFiles
|
||||
class TestCreateFolders < TestGenerateAllLocalizationFiles
|
||||
def new_runner(twine_file = nil)
|
||||
super(true, twine_file)
|
||||
end
|
||||
|
||||
def test_creates_output_folder
|
||||
FileUtils.remove_entry_secure @output_dir
|
||||
new_runner.generate_all_string_files
|
||||
new_runner.generate_all_localization_files
|
||||
assert File.exists? @output_dir
|
||||
end
|
||||
|
||||
def test_creates_language_folders
|
||||
new_runner.generate_all_string_files
|
||||
new_runner.generate_all_localization_files
|
||||
assert File.exists?(File.join(@output_dir, 'en.lproj')), "language folder 'en.lproj' should be created"
|
||||
assert File.exists?(File.join(@output_dir, 'es.lproj')), "language folder 'es.lproj' should be created"
|
||||
end
|
||||
|
||||
def test_prints_empty_file_warnings
|
||||
empty_twine_file = build_twine_file('en') {}
|
||||
new_runner(empty_twine_file).generate_all_string_files
|
||||
new_runner(empty_twine_file).generate_all_localization_files
|
||||
|
||||
assert_match "Skipping file at path", Twine::stderr.string
|
||||
end
|
||||
|
@ -79,23 +79,23 @@ class TestGenerateAllStringFiles < CommandTestCase
|
|||
|
||||
twine_file = build_twine_file 'en' do
|
||||
add_section 'Section' do
|
||||
add_row key: 'value'
|
||||
add_row key: 'value'
|
||||
add_definition key: 'value'
|
||||
add_definition key: 'value'
|
||||
end
|
||||
end
|
||||
|
||||
Twine::Runner.new(options, twine_file)
|
||||
end
|
||||
|
||||
def test_does_not_validate_strings_file
|
||||
def test_does_not_validate_twine_file
|
||||
prepare_mock_formatter Twine::Formatters::Android
|
||||
|
||||
new_runner(false).generate_all_string_files
|
||||
new_runner(false).generate_all_localization_files
|
||||
end
|
||||
|
||||
def test_validates_strings_file_if_validate
|
||||
def test_validates_twine_file_if_validate
|
||||
assert_raises Twine::Error do
|
||||
new_runner(true).generate_all_string_files
|
||||
new_runner(true).generate_all_localization_files
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,7 +9,7 @@ class TestGenerateLocDrop < CommandTestCase
|
|||
unless twine_file
|
||||
twine_file = build_twine_file 'en', 'fr' do
|
||||
add_section 'Section' do
|
||||
add_row key: 'value'
|
||||
add_definition key: 'value'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -57,21 +57,21 @@ class TestGenerateLocDrop < CommandTestCase
|
|||
|
||||
twine_file = build_twine_file 'en' do
|
||||
add_section 'Section' do
|
||||
add_row key: 'value'
|
||||
add_row key: 'value'
|
||||
add_definition key: 'value'
|
||||
add_definition key: 'value'
|
||||
end
|
||||
end
|
||||
|
||||
Twine::Runner.new(options, twine_file)
|
||||
end
|
||||
|
||||
def test_does_not_validate_strings_file
|
||||
def test_does_not_validate_twine_file
|
||||
prepare_mock_formatter Twine::Formatters::Android
|
||||
|
||||
new_runner(false).generate_loc_drop
|
||||
end
|
||||
|
||||
def test_validates_strings_file_if_validate
|
||||
def test_validates_twine_file_if_validate
|
||||
assert_raises Twine::Error do
|
||||
new_runner(true).generate_loc_drop
|
||||
end
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
require 'command_test_case'
|
||||
|
||||
class TestGenerateStringFile < CommandTestCase
|
||||
class TestGenerateLocalizationFile < CommandTestCase
|
||||
def new_runner(language, file)
|
||||
options = {}
|
||||
options[:output_path] = File.join(@output_dir, file) if file
|
||||
options[:languages] = language if language
|
||||
|
||||
strings = Twine::StringsFile.new
|
||||
strings.language_codes.concat KNOWN_LANGUAGES
|
||||
twine_file = Twine::TwineFile.new
|
||||
twine_file.language_codes.concat KNOWN_LANGUAGES
|
||||
|
||||
Twine::Runner.new(options, strings)
|
||||
Twine::Runner.new(options, twine_file)
|
||||
end
|
||||
|
||||
def prepare_mock_format_file_formatter(formatter_class)
|
||||
|
@ -20,25 +20,25 @@ class TestGenerateStringFile < CommandTestCase
|
|||
def test_deducts_android_format_from_output_path
|
||||
prepare_mock_format_file_formatter Twine::Formatters::Android
|
||||
|
||||
new_runner('fr', 'fr.xml').generate_string_file
|
||||
new_runner('fr', 'fr.xml').generate_localization_file
|
||||
end
|
||||
|
||||
def test_deducts_apple_format_from_output_path
|
||||
prepare_mock_format_file_formatter Twine::Formatters::Apple
|
||||
|
||||
new_runner('fr', 'fr.strings').generate_string_file
|
||||
new_runner('fr', 'fr.strings').generate_localization_file
|
||||
end
|
||||
|
||||
def test_deducts_jquery_format_from_output_path
|
||||
prepare_mock_format_file_formatter Twine::Formatters::JQuery
|
||||
|
||||
new_runner('fr', 'fr.json').generate_string_file
|
||||
new_runner('fr', 'fr.json').generate_localization_file
|
||||
end
|
||||
|
||||
def test_deducts_gettext_format_from_output_path
|
||||
prepare_mock_format_file_formatter Twine::Formatters::Gettext
|
||||
|
||||
new_runner('fr', 'fr.po').generate_string_file
|
||||
new_runner('fr', 'fr.po').generate_localization_file
|
||||
end
|
||||
|
||||
def test_deducts_language_from_output_path
|
||||
|
@ -46,7 +46,7 @@ class TestGenerateStringFile < CommandTestCase
|
|||
formatter = prepare_mock_formatter Twine::Formatters::Android
|
||||
formatter.expects(:format_file).with(random_language).returns(true)
|
||||
|
||||
new_runner(nil, "#{random_language}.xml").generate_string_file
|
||||
new_runner(nil, "#{random_language}.xml").generate_localization_file
|
||||
end
|
||||
|
||||
def test_returns_error_if_nothing_written
|
||||
|
@ -54,7 +54,7 @@ class TestGenerateStringFile < CommandTestCase
|
|||
formatter.expects(:format_file).returns(false)
|
||||
|
||||
assert_raises Twine::Error do
|
||||
new_runner('fr', 'fr.xml').generate_string_file
|
||||
new_runner('fr', 'fr.xml').generate_localization_file
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -68,23 +68,23 @@ class TestGenerateStringFile < CommandTestCase
|
|||
|
||||
twine_file = build_twine_file 'en' do
|
||||
add_section 'Section' do
|
||||
add_row key: 'value'
|
||||
add_row key: 'value'
|
||||
add_definition key: 'value'
|
||||
add_definition key: 'value'
|
||||
end
|
||||
end
|
||||
|
||||
Twine::Runner.new(options, twine_file)
|
||||
end
|
||||
|
||||
def test_does_not_validate_strings_file
|
||||
def test_does_not_validate_twine_file
|
||||
prepare_mock_formatter Twine::Formatters::Android
|
||||
|
||||
new_runner(false).generate_string_file
|
||||
new_runner(false).generate_localization_file
|
||||
end
|
||||
|
||||
def test_validates_strings_file_if_validate
|
||||
def test_validates_twine_file_if_validate
|
||||
assert_raises Twine::Error do
|
||||
new_runner(true).generate_string_file
|
||||
new_runner(true).generate_localization_file
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,81 +4,81 @@ class TestOutputProcessor < TwineTestCase
|
|||
def setup
|
||||
super
|
||||
|
||||
@strings = build_twine_file 'en', 'fr' do
|
||||
@twine_file = build_twine_file 'en', 'fr' do
|
||||
add_section 'Section' do
|
||||
add_row key1: 'value1', tags: ['tag1']
|
||||
add_row key2: 'value2', tags: ['tag1', 'tag2']
|
||||
add_row key3: 'value3', tags: ['tag2']
|
||||
add_row key4: { en: 'value4-en', fr: 'value4-fr' }
|
||||
add_definition key1: 'value1', tags: ['tag1']
|
||||
add_definition key2: 'value2', tags: ['tag1', 'tag2']
|
||||
add_definition key3: 'value3', tags: ['tag2']
|
||||
add_definition key4: { en: 'value4-en', fr: 'value4-fr' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_includes_all_keys_by_default
|
||||
processor = Twine::Processors::OutputProcessor.new(@strings, {})
|
||||
processor = Twine::Processors::OutputProcessor.new(@twine_file, {})
|
||||
result = processor.process('en')
|
||||
|
||||
assert_equal %w(key1 key2 key3 key4), result.strings_map.keys.sort
|
||||
assert_equal %w(key1 key2 key3 key4), result.definitions_by_key.keys.sort
|
||||
end
|
||||
|
||||
def test_filter_by_tag
|
||||
processor = Twine::Processors::OutputProcessor.new(@strings, { tags: ['tag1'] })
|
||||
processor = Twine::Processors::OutputProcessor.new(@twine_file, { tags: ['tag1'] })
|
||||
result = processor.process('en')
|
||||
|
||||
assert_equal %w(key1 key2), result.strings_map.keys.sort
|
||||
assert_equal %w(key1 key2), result.definitions_by_key.keys.sort
|
||||
end
|
||||
|
||||
def test_filter_by_multiple_tags
|
||||
processor = Twine::Processors::OutputProcessor.new(@strings, { tags: ['tag1', 'tag2'] })
|
||||
processor = Twine::Processors::OutputProcessor.new(@twine_file, { tags: ['tag1', 'tag2'] })
|
||||
result = processor.process('en')
|
||||
|
||||
assert_equal %w(key1 key2 key3), result.strings_map.keys.sort
|
||||
assert_equal %w(key1 key2 key3), result.definitions_by_key.keys.sort
|
||||
end
|
||||
|
||||
def test_filter_untagged
|
||||
processor = Twine::Processors::OutputProcessor.new(@strings, { tags: ['tag1'], untagged: true })
|
||||
processor = Twine::Processors::OutputProcessor.new(@twine_file, { tags: ['tag1'], untagged: true })
|
||||
result = processor.process('en')
|
||||
|
||||
assert_equal %w(key1 key2 key4), result.strings_map.keys.sort
|
||||
assert_equal %w(key1 key2 key4), result.definitions_by_key.keys.sort
|
||||
end
|
||||
|
||||
def test_include_translated
|
||||
processor = Twine::Processors::OutputProcessor.new(@strings, { include: :translated })
|
||||
processor = Twine::Processors::OutputProcessor.new(@twine_file, { include: :translated })
|
||||
result = processor.process('fr')
|
||||
|
||||
assert_equal %w(key4), result.strings_map.keys.sort
|
||||
assert_equal %w(key4), result.definitions_by_key.keys.sort
|
||||
end
|
||||
|
||||
def test_include_untranslated
|
||||
processor = Twine::Processors::OutputProcessor.new(@strings, { include: :untranslated })
|
||||
processor = Twine::Processors::OutputProcessor.new(@twine_file, { include: :untranslated })
|
||||
result = processor.process('fr')
|
||||
|
||||
assert_equal %w(key1 key2 key3), result.strings_map.keys.sort
|
||||
assert_equal %w(key1 key2 key3), result.definitions_by_key.keys.sort
|
||||
end
|
||||
|
||||
class TranslationFallback < TwineTestCase
|
||||
def setup
|
||||
super
|
||||
|
||||
@strings = build_twine_file 'en', 'fr', 'de' do
|
||||
@twine_file = build_twine_file 'en', 'fr', 'de' do
|
||||
add_section 'Section' do
|
||||
add_row key1: { en: 'value1-en', fr: 'value1-fr' }
|
||||
add_definition key1: { en: 'value1-en', fr: 'value1-fr' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_fallback_to_default_language
|
||||
processor = Twine::Processors::OutputProcessor.new(@strings, {})
|
||||
processor = Twine::Processors::OutputProcessor.new(@twine_file, {})
|
||||
result = processor.process('de')
|
||||
|
||||
assert_equal 'value1-en', result.strings_map['key1'].translations['de']
|
||||
assert_equal 'value1-en', result.definitions_by_key['key1'].translations['de']
|
||||
end
|
||||
|
||||
def test_fallback_to_developer_language
|
||||
processor = Twine::Processors::OutputProcessor.new(@strings, {developer_language: 'fr'})
|
||||
processor = Twine::Processors::OutputProcessor.new(@twine_file, {developer_language: 'fr'})
|
||||
result = processor.process('de')
|
||||
|
||||
assert_equal 'value1-fr', result.strings_map['key1'].translations['de']
|
||||
assert_equal 'value1-fr', result.definitions_by_key['key1'].translations['de']
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
require 'twine_test_case'
|
||||
|
||||
class TestStringsFile < TwineTestCase
|
||||
class Reading < TwineTestCase
|
||||
def setup
|
||||
super
|
||||
|
||||
@strings = Twine::StringsFile.new
|
||||
@strings.read fixture_path('twine_accent_values.txt')
|
||||
end
|
||||
|
||||
def test_reading_keeps_leading_accent
|
||||
assert_equal '`value', @strings.strings_map['value_with_leading_accent'].translations['en']
|
||||
end
|
||||
|
||||
def test_reading_keeps_trailing_accent
|
||||
assert_equal 'value`', @strings.strings_map['value_with_trailing_accent'].translations['en']
|
||||
end
|
||||
|
||||
def test_reading_keeps_leading_space
|
||||
assert_equal ' value', @strings.strings_map['value_with_leading_space'].translations['en']
|
||||
end
|
||||
|
||||
def test_reading_keeps_trailing_space
|
||||
assert_equal 'value ', @strings.strings_map['value_with_trailing_space'].translations['en']
|
||||
end
|
||||
|
||||
def test_reading_keeps_wrapping_spaces
|
||||
assert_equal ' value ', @strings.strings_map['value_wrapped_by_spaces'].translations['en']
|
||||
end
|
||||
|
||||
def test_reading_keeps_wrapping_accents
|
||||
assert_equal '`value`', @strings.strings_map['value_wrapped_by_accents'].translations['en']
|
||||
end
|
||||
end
|
||||
|
||||
class Writing < TwineTestCase
|
||||
|
||||
def test_accent_wrapping
|
||||
@strings = build_twine_file 'en' do
|
||||
add_section 'Section' do
|
||||
add_row value_with_leading_accent: '`value'
|
||||
add_row value_with_trailing_accent: 'value`'
|
||||
add_row value_with_leading_space: ' value'
|
||||
add_row value_with_trailing_space: 'value '
|
||||
add_row value_wrapped_by_spaces: ' value '
|
||||
add_row value_wrapped_by_accents: '`value`'
|
||||
end
|
||||
end
|
||||
|
||||
@strings.write @output_path
|
||||
|
||||
assert_equal content('twine_accent_values.txt'), File.read(@output_path)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -1,47 +0,0 @@
|
|||
require 'twine_test_case'
|
||||
|
||||
class TestStringsRow < TwineTestCase
|
||||
def setup
|
||||
super
|
||||
|
||||
@reference = Twine::StringsRow.new 'reference-key'
|
||||
@reference.comment = 'reference comment'
|
||||
@reference.tags = ['ref1']
|
||||
@reference.translations['en'] = 'ref-value'
|
||||
|
||||
@row = Twine::StringsRow.new 'key'
|
||||
@row.reference_key = @reference.key
|
||||
@row.reference = @reference
|
||||
end
|
||||
|
||||
def test_reference_comment_used
|
||||
assert_equal 'reference comment', @row.comment
|
||||
end
|
||||
|
||||
def test_reference_comment_override
|
||||
@row.comment = 'row comment'
|
||||
|
||||
assert_equal 'row comment', @row.comment
|
||||
end
|
||||
|
||||
def test_reference_tags_used
|
||||
assert @row.matches_tags?(['ref1'], false)
|
||||
end
|
||||
|
||||
def test_reference_tags_override
|
||||
@row.tags = ['tag1']
|
||||
|
||||
refute @row.matches_tags?(['ref1'], false)
|
||||
assert @row.matches_tags?(['tag1'], false)
|
||||
end
|
||||
|
||||
def test_reference_translation_used
|
||||
assert_equal 'ref-value', @row.translated_string_for_lang('en')
|
||||
end
|
||||
|
||||
def test_reference_translation_override
|
||||
@row.translations['en'] = 'value'
|
||||
|
||||
assert_equal 'value', @row.translated_string_for_lang('en')
|
||||
end
|
||||
end
|
47
test/test_twine_definition.rb
Normal file
47
test/test_twine_definition.rb
Normal file
|
@ -0,0 +1,47 @@
|
|||
require 'twine_test_case'
|
||||
|
||||
class TestTwineDefinition < TwineTestCase
|
||||
def setup
|
||||
super
|
||||
|
||||
@reference = Twine::TwineDefinition.new 'reference-key'
|
||||
@reference.comment = 'reference comment'
|
||||
@reference.tags = ['ref1']
|
||||
@reference.translations['en'] = 'ref-value'
|
||||
|
||||
@definition = Twine::TwineDefinition.new 'key'
|
||||
@definition.reference_key = @reference.key
|
||||
@definition.reference = @reference
|
||||
end
|
||||
|
||||
def test_reference_comment_used
|
||||
assert_equal 'reference comment', @definition.comment
|
||||
end
|
||||
|
||||
def test_reference_comment_override
|
||||
@definition.comment = 'definition comment'
|
||||
|
||||
assert_equal 'definition comment', @definition.comment
|
||||
end
|
||||
|
||||
def test_reference_tags_used
|
||||
assert @definition.matches_tags?(['ref1'], false)
|
||||
end
|
||||
|
||||
def test_reference_tags_override
|
||||
@definition.tags = ['tag1']
|
||||
|
||||
refute @definition.matches_tags?(['ref1'], false)
|
||||
assert @definition.matches_tags?(['tag1'], false)
|
||||
end
|
||||
|
||||
def test_reference_translation_used
|
||||
assert_equal 'ref-value', @definition.translation_for_lang('en')
|
||||
end
|
||||
|
||||
def test_reference_translation_override
|
||||
@definition.translations['en'] = 'value'
|
||||
|
||||
assert_equal 'value', @definition.translation_for_lang('en')
|
||||
end
|
||||
end
|
58
test/test_twine_file.rb
Normal file
58
test/test_twine_file.rb
Normal file
|
@ -0,0 +1,58 @@
|
|||
require 'twine_test_case'
|
||||
|
||||
class TestTwineFile < TwineTestCase
|
||||
class Reading < TwineTestCase
|
||||
def setup
|
||||
super
|
||||
|
||||
@twine_file = Twine::TwineFile.new
|
||||
@twine_file.read fixture_path('twine_accent_values.txt')
|
||||
end
|
||||
|
||||
def test_reading_keeps_leading_accent
|
||||
assert_equal '`value', @twine_file.definitions_by_key['value_with_leading_accent'].translations['en']
|
||||
end
|
||||
|
||||
def test_reading_keeps_trailing_accent
|
||||
assert_equal 'value`', @twine_file.definitions_by_key['value_with_trailing_accent'].translations['en']
|
||||
end
|
||||
|
||||
def test_reading_keeps_leading_space
|
||||
assert_equal ' value', @twine_file.definitions_by_key['value_with_leading_space'].translations['en']
|
||||
end
|
||||
|
||||
def test_reading_keeps_trailing_space
|
||||
assert_equal 'value ', @twine_file.definitions_by_key['value_with_trailing_space'].translations['en']
|
||||
end
|
||||
|
||||
def test_reading_keeps_wrapping_spaces
|
||||
assert_equal ' value ', @twine_file.definitions_by_key['value_wrapped_by_spaces'].translations['en']
|
||||
end
|
||||
|
||||
def test_reading_keeps_wrapping_accents
|
||||
assert_equal '`value`', @twine_file.definitions_by_key['value_wrapped_by_accents'].translations['en']
|
||||
end
|
||||
end
|
||||
|
||||
class Writing < TwineTestCase
|
||||
|
||||
def test_accent_wrapping
|
||||
@twine_file = build_twine_file 'en' do
|
||||
add_section 'Section' do
|
||||
add_definition value_with_leading_accent: '`value'
|
||||
add_definition value_with_trailing_accent: 'value`'
|
||||
add_definition value_with_leading_space: ' value'
|
||||
add_definition value_with_trailing_space: 'value '
|
||||
add_definition value_wrapped_by_spaces: ' value '
|
||||
add_definition value_wrapped_by_accents: '`value`'
|
||||
end
|
||||
end
|
||||
|
||||
@twine_file.write @output_path
|
||||
|
||||
assert_equal content('twine_accent_values.txt'), File.read(@output_path)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -1,61 +0,0 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'command_test_case'
|
||||
|
||||
class TestValidateStringsFile < CommandTestCase
|
||||
def setup
|
||||
super
|
||||
@options = { strings_file: 'input.txt' }
|
||||
|
||||
@twine_file = build_twine_file 'en' do
|
||||
add_section 'Section 1' do
|
||||
add_row key1: 'value1', tags: ['tag1']
|
||||
add_row key2: 'value2', tags: ['tag1']
|
||||
end
|
||||
|
||||
add_section 'Section 2' do
|
||||
add_row key3: 'value3', tags: ['tag1', 'tag2']
|
||||
add_row key4: 'value4', tags: ['tag2']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def random_row
|
||||
@twine_file.strings_map[@twine_file.strings_map.keys.sample]
|
||||
end
|
||||
|
||||
def test_recognizes_valid_file
|
||||
Twine::Runner.new(@options, @twine_file).validate_strings_file
|
||||
assert_equal "input.txt is valid.\n", Twine::stdout.string
|
||||
end
|
||||
|
||||
def test_reports_duplicate_keys
|
||||
@twine_file.sections[0].rows << random_row
|
||||
|
||||
assert_raises Twine::Error do
|
||||
Twine::Runner.new(@options, @twine_file).validate_strings_file
|
||||
end
|
||||
end
|
||||
|
||||
def test_reports_invalid_characters_in_keys
|
||||
random_row.key[0] = "!?;:,^`´'\"\\|/(){}[]~-+*=#$%".chars.to_a.sample
|
||||
|
||||
assert_raises Twine::Error do
|
||||
Twine::Runner.new(@options, @twine_file).validate_strings_file
|
||||
end
|
||||
end
|
||||
|
||||
def test_does_not_reports_missing_tags_by_default
|
||||
random_row.tags.clear
|
||||
|
||||
Twine::Runner.new(@options, @twine_file).validate_strings_file
|
||||
end
|
||||
|
||||
def test_reports_missing_tags
|
||||
random_row.tags.clear
|
||||
|
||||
assert_raises Twine::Error do
|
||||
Twine::Runner.new(@options.merge(pedantic: true), @twine_file).validate_strings_file
|
||||
end
|
||||
end
|
||||
end
|
61
test/test_validate_twine_file.rb
Normal file
61
test/test_validate_twine_file.rb
Normal file
|
@ -0,0 +1,61 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'command_test_case'
|
||||
|
||||
class TestValidateTwineFile < CommandTestCase
|
||||
def setup
|
||||
super
|
||||
@options = { twine_file: 'input.txt' }
|
||||
|
||||
@twine_file = build_twine_file 'en' do
|
||||
add_section 'Section 1' do
|
||||
add_definition key1: 'value1', tags: ['tag1']
|
||||
add_definition key2: 'value2', tags: ['tag1']
|
||||
end
|
||||
|
||||
add_section 'Section 2' do
|
||||
add_definition key3: 'value3', tags: ['tag1', 'tag2']
|
||||
add_definition key4: 'value4', tags: ['tag2']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def random_definition
|
||||
@twine_file.definitions_by_key[@twine_file.definitions_by_key.keys.sample]
|
||||
end
|
||||
|
||||
def test_recognizes_valid_file
|
||||
Twine::Runner.new(@options, @twine_file).validate_twine_file
|
||||
assert_equal "input.txt is valid.\n", Twine::stdout.string
|
||||
end
|
||||
|
||||
def test_reports_duplicate_keys
|
||||
@twine_file.sections[0].definitions << random_definition
|
||||
|
||||
assert_raises Twine::Error do
|
||||
Twine::Runner.new(@options, @twine_file).validate_twine_file
|
||||
end
|
||||
end
|
||||
|
||||
def test_reports_invalid_characters_in_keys
|
||||
random_definition.key[0] = "!?;:,^`´'\"\\|/(){}[]~-+*=#$%".chars.to_a.sample
|
||||
|
||||
assert_raises Twine::Error do
|
||||
Twine::Runner.new(@options, @twine_file).validate_twine_file
|
||||
end
|
||||
end
|
||||
|
||||
def test_does_not_reports_missing_tags_by_default
|
||||
random_definition.tags.clear
|
||||
|
||||
Twine::Runner.new(@options, @twine_file).validate_twine_file
|
||||
end
|
||||
|
||||
def test_reports_missing_tags
|
||||
random_definition.tags.clear
|
||||
|
||||
assert_raises Twine::Error do
|
||||
Twine::Runner.new(@options.merge(pedantic: true), @twine_file).validate_twine_file
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +1,6 @@
|
|||
module TwineFileDSL
|
||||
def build_twine_file(*languages)
|
||||
@currently_built_twine_file = Twine::StringsFile.new
|
||||
@currently_built_twine_file = Twine::TwineFile.new
|
||||
@currently_built_twine_file.language_codes.concat languages
|
||||
yield
|
||||
result = @currently_built_twine_file
|
||||
|
@ -10,37 +10,37 @@ module TwineFileDSL
|
|||
|
||||
def add_section(name)
|
||||
return unless @currently_built_twine_file
|
||||
@currently_built_twine_file_section = Twine::StringsSection.new name
|
||||
@currently_built_twine_file_section = Twine::TwineSection.new name
|
||||
@currently_built_twine_file.sections << @currently_built_twine_file_section
|
||||
yield
|
||||
@currently_built_twine_file_section = nil
|
||||
end
|
||||
|
||||
def add_row(parameters)
|
||||
def add_definition(parameters)
|
||||
return unless @currently_built_twine_file
|
||||
return unless @currently_built_twine_file_section
|
||||
|
||||
# this relies on Ruby preserving the order of hash elements
|
||||
key, value = parameters.first
|
||||
row = Twine::StringsRow.new(key.to_s)
|
||||
definition = Twine::TwineDefinition.new(key.to_s)
|
||||
if value.is_a? Hash
|
||||
value.each do |language, translation|
|
||||
row.translations[language.to_s] = translation
|
||||
definition.translations[language.to_s] = translation
|
||||
end
|
||||
elsif !value.is_a? Symbol
|
||||
language = @currently_built_twine_file.language_codes.first
|
||||
row.translations[language] = value
|
||||
definition.translations[language] = value
|
||||
end
|
||||
|
||||
row.comment = parameters[:comment] if parameters[:comment]
|
||||
row.tags = parameters[:tags] if parameters[:tags]
|
||||
definition.comment = parameters[:comment] if parameters[:comment]
|
||||
definition.tags = parameters[:tags] if parameters[:tags]
|
||||
if parameters[:ref] || value.is_a?(Symbol)
|
||||
reference_key = (parameters[:ref] || value).to_s
|
||||
row.reference_key = reference_key
|
||||
row.reference = @currently_built_twine_file.strings_map[reference_key]
|
||||
definition.reference_key = reference_key
|
||||
definition.reference = @currently_built_twine_file.definitions_by_key[reference_key]
|
||||
end
|
||||
|
||||
@currently_built_twine_file_section.rows << row
|
||||
@currently_built_twine_file.strings_map[row.key] = row
|
||||
@currently_built_twine_file_section.definitions << definition
|
||||
@currently_built_twine_file.definitions_by_key[definition.key] = definition
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ Gem::Specification.new do |s|
|
|||
s.name = "twine"
|
||||
s.version = Twine::VERSION
|
||||
s.date = Time.now.strftime('%Y-%m-%d')
|
||||
s.summary = "Manage strings and their translations for your iOS and Android projects."
|
||||
s.summary = "Manage strings and their translations for your iOS, Android and other projects."
|
||||
s.homepage = "https://github.com/mobiata/twine"
|
||||
s.email = "twine@mobiata.com"
|
||||
s.authors = [ "Sebastian Celis" ]
|
||||
|
@ -31,4 +31,6 @@ Gem::Specification.new do |s|
|
|||
|
||||
It is geared toward Mac OS X, iOS, and Android developers.
|
||||
desc
|
||||
|
||||
s.post_install_message = "Twine command names have changed! Please check the documentation for their new names."
|
||||
end
|
||||
|
|
Reference in a new issue