gettext-setup-0.34/0000755000004100000410000000000013616243263014320 5ustar www-datawww-datagettext-setup-0.34/README.md0000644000004100000410000001604413616243263015604 0ustar www-datawww-data# gettext-setup gem This is a simple gem to set up i18n for Ruby projects (including [Sinatra](http://www.sinatrarb.com/) web apps) using gettext and fast gettext. This project sets the default locale to English. If the user has set a different locale in their browser preferences, and we support the user's preferred locale, strings and data formatting will be customized for that locale. ## Terminology **Translatable strings** - User-facing strings that are in scope for i18n (see the in scope/out of scope sections [here](https://confluence.puppetlabs.com/display/ENG/i18n#i18n-TasksandMilestones)). **POT file** - Portable Objects Template. This is the file that stores externalized English strings. It includes the source file and line number of the string. **PO file** - A bilingual file containing the source English strings (msgid) and target language strings (msgstr). This file is generated from the POT file and is where translated strings are added. A PO file is generated for each language. **Transifex** - A translation management system. When POT files are updated, the updates are pushed to Transifex. Transifex generates a PO file for each language that has been set up in the Transifex project. Translators enter the string translations in the PO for their language. ## Setup for your project These are the poignant bits of this example that you need to replicate in your project: 1. Add `gem 'gettext-setup'` to your `Gemfile`. 2. Copy `locales/config-sample.yaml` to your project and put it into a `locales` directory as `config.yaml`. 3. Edit `locales/config.yaml` and make the necessary changes for your project 4. Add these three lines to your `Rakefile`, ensuring the `locales` directory is found by the last line: ```ruby spec = Gem::Specification.find_by_name 'gettext-setup' load "#{spec.gem_dir}/lib/tasks/gettext.rake" GettextSetup.initialize(File.absolute_path('locales', File.dirname(__FILE__))) ``` 5. Add these lines at the start of your app (`app.rb` for server-side, the executable binary for CLI applications): ```ruby require 'gettext-setup' GettextSetup.initialize(File.absolute_path('locales', File.dirname(__FILE__))) ``` Note that the second line may require modification to find the `locales` directory. 6. For client-side applications, add this line: ```ruby GettextSetup.negotiate_locale!(GettextSetup.candidate_locales) ``` 7. For server-side applications, add these lines: ```ruby before do GettextSetup.negotiate_locale!(env["HTTP_ACCEPT_LANGUAGE"]) end ``` ## Writing translatable code ### Use full sentences Write user-facing strings as full sentences rather than joining multiple strings to form a full sentence because the word order in other languages can be different to English. See [Tips on writing translation-friendly strings](https://confluence.puppetlabs.com/display/ENG/Tips+for+writing+translation-friendly+strings). ### Use the translation function _() Wrap user-facing strings in the `_()` function so they can be externalized to a POT file. E.g. `_("Hello, world!")` ### Interpolation To add dynamic data to a string, use the following string formatting and translation function: `_("We negotiated a locale of %{locale}") % {locale: FastGettext.locale}` ### Pluralize with the n_() function Wrap strings that include pluralization with the `n_()` function. E.g. `n_("There is %{count} bicycle in %{city}", "There are %{count} bicycles in %{city}", num_bikes) % {count: num_bikes, city: "Beijing"},` Pluralization rules vary across languages. The pluralization rules are specified in the PO file and look something like this `Plural-Forms: nplurals=2; plural=(n > 1);`. This is the pluralization rule for German. It means that German has two pluralization rules. The first rule is `plural=n > 1)` and the second rule is all other counts. Plurals are selected from the PO file by index. Here's an example of how a pluralized string is handled in a PO file: ``` msgid "%{count} file" msgid_plural "%{count} files" msgstr[0] "%{count} Dateien" msgstr[1] "%{count} Datei" ``` The `msgid` is the singular version of the English source string that's pulled in to the POT file and PO from the code file. The `msgid_plural` is the plural version of the English source string. The two `msgstr` lines show that German has two rules for pluralization. The indices map to the `Plural-Forms: nplurals=2; plural=(n > 1);` rule that we specified in the PO file. The `[0]` index represents `plural=(n > 1)` and the `[1]` index represents all other pluralization cases (in other words, when the count equals 0 or 1). When Transifex generates a PO file for a specific language, it automatically adds the appropriate pluralization rules in the PO file. ### Comments To provide translators with some contextual information or instructions about a string, precede the string with a comment using `#. `. The comment gets pulled in to the POT file and will show up as a comment in Transifex. E.g. `#. The placeholder in this string represents the name of a parameter.` ## Translation workflow 1. Wrap the translation function around translatable strings in code files 2. A CI job checks code commits to see if any changes have been made to user-facing strings. If changes have been made, the CI job parses the source code and extracts translatable strings into a POT file. If a POT file already exists, the CI job will update the existing POT file. (If the CI job hasn't already been added to your pipeline, you will need to add it.) 3. When a POT file is updated, the Transifex webhook pushes the new POT file to Transifex ready for translation. (If your POT file hasn't been added to the Transifex integration yet, you will need to get it added.) 4. When a PO file reaches 100% translated and reviewed, a webhook pushes it back to the source repo ready to be consumed by your app. 5. Your app checks the user's locale settings (the browser settings for web apps, or the system settings for the CLI). If we support the user's preferred locale, the app will display strings in the user's language. Otherwise, it defaults to English. ## Merge Pot files rake task The rake task that merges .pot files is present for the internationalisation of a module. This task uses 'msgcat', which is only natively present on OSes that are GNU based. For running this task locally on another OS, you will need to download the gettext pkg and install it locally: https://pkgs.org/download/gettext This task will run within the gettext setup locales_path provided by GettextSetup. The result will be a merged pot file created from all pot files kept in this location. By default, the merged pot file is locales_path/project_name.pot. This can be overridden when calling the method by providing a chosen path. Please note: Since the default merged file name is project_name.pot, it will override anything of that name within the locales directory. ## Making releases Add the tag you wish to release (via the bot in the release channel is preferred) and then `gem build gettext-setup.gemspec` and then `gem push gettext-setup-.gem`. (you will need to be an owner to push).gettext-setup-0.34/spec/0000755000004100000410000000000013616243263015252 5ustar www-datawww-datagettext-setup-0.34/spec/fixtures/0000755000004100000410000000000013616243263017123 5ustar www-datawww-datagettext-setup-0.34/spec/fixtures/merge_locales/0000755000004100000410000000000013616243263021724 5ustar www-datawww-datagettext-setup-0.34/spec/fixtures/merge_locales/config.yaml0000644000004100000410000000153713616243263024063 0ustar www-datawww-data--- # This is the project-specific configuration file for setting up # fast_gettext for your project. gettext: # This is used for the name of the .pot and .po files; they will be # called .pot? project_name: 'merge_locales' # This is used in comments in the .pot and .po files to indicate what # project the files belong to and should bea little more desctiptive than # package_name: Merge locales # The locale that the default messages in the .pot file are in default_locale: en # The email used for sending bug reports. bugs_address: docs@puppetlabs.com # The holder of the copyright. copyright_holder: Puppet, LLC. # Patterns for +Dir.glob+ used to find all files that might contain # translatable content, relative to the project root directory source_files: - 'spec/fixtures/merge_locales/*.rb' gettext-setup-0.34/spec/fixtures/spec_locales/0000755000004100000410000000000013616243263021557 5ustar www-datawww-datagettext-setup-0.34/spec/fixtures/spec_locales/config.yaml0000644000004100000410000000152513616243263023713 0ustar www-datawww-data--- # This is the project-specific configuration file for setting up # fast_gettext for your project. gettext: # This is used for the name of the .pot and .po files; they will be # called .pot? project_name: 'sinatra-i18n' # This is used in comments in the .pot and .po files to indicate what # project the files belong to and should bea little more desctiptive than # package_name: Sinatra i18n demo # The locale that the default messages in the .pot file are in default_locale: en # The email used for sending bug reports. bugs_address: docs@puppetlabs.com # The holder of the copyright. copyright_holder: Puppet Labs, LLC. # Patterns for +Dir.glob+ used to find all files that might contain # translatable content, relative to the project root directory source_files: - 'spec/**/*.rb' gettext-setup-0.34/spec/fixtures/spec_locales/sinatra-i18n.pot0000644000004100000410000000212513616243263024521 0ustar www-datawww-data# SOME DESCRIPTIVE TITLE. # Copyright (C) 2017 Puppet Labs, LLC. # This file is distributed under the same license as the Sinatra i18n demo package. # FIRST AUTHOR , 2017. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Sinatra i18n demo 0.21-6-gc301398\n" "\n" "Report-Msgid-Bugs-To: docs@puppetlabs.com\n" "POT-Creation-Date: 2017-04-12 00:27-0500\n" "PO-Revision-Date: 2017-04-12 00:27-0500\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: ../../lib/gettext_setup_spec.rb:26 ../../lib/gettext_setup_spec.rb:89 ../../lib/gettext_setup_spec.rb:91 ../fixture_locales/test_strings.rb:1 msgid "Hello, world!" msgstr "" #: ../../lib/pot_spec.rb:91 msgid "merged-po-file" msgstr "" #: ../../lib/pot_spec.rb:117 msgid "no-pot-file" msgstr "" #: ../../lib/pot_spec.rb:127 msgid "some-spec-only-string" msgstr "" #: ../../lib/pot_spec.rb:144 msgid "unchanged-string" msgstr "" gettext-setup-0.34/spec/fixtures/tmp_locales/0000755000004100000410000000000013616243263021425 5ustar www-datawww-datagettext-setup-0.34/spec/fixtures/tmp_locales/config.yaml0000644000004100000410000000155113616243263023560 0ustar www-datawww-data--- # This is the project-specific configuration file for setting up # fast_gettext for your project. gettext: # This is used for the name of the .pot and .po files; they will be # called .pot? project_name: 'sinatra-i18n' # This is used in comments in the .pot and .po files to indicate what # project the files belong to and should bea little more desctiptive than # package_name: Sinatra i18n demo # The locale that the default messages in the .pot file are in default_locale: en # The email used for sending bug reports. bugs_address: docs@puppetlabs.com # The holder of the copyright. copyright_holder: Puppet Labs, LLC. # Patterns for +Dir.glob+ used to find all files that might contain # translatable content, relative to the project root directory source_files: - 'lib/**/*.rb' - 'spec/**/*.rb' gettext-setup-0.34/spec/fixtures/tmp_locales/de/0000755000004100000410000000000013616243263022015 5ustar www-datawww-datagettext-setup-0.34/spec/fixtures/tmp_locales/de/sinatra-i18n.po0000644000004100000410000000130113616243263024566 0ustar www-datawww-data# German translations for Sinatra i18n demo package. # Copyright (C) 2016 Puppet Labs, LLC. # This file is distributed under the same license as the Sinatra i18n demo package. # Automatically generated, 2016. # msgid "" msgstr "" "Project-Id-Version: Sinatra i18n demo \n" "Report-Msgid-Bugs-To: docs@puppetlabs.com\n" "POT-Creation-Date: 2016-04-05 10:39-0700\n" "PO-Revision-Date: 2016-02-26 18:21-0800\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../app.rb:14 msgid "Hello, world!" msgstr "Hallo, Welt!" gettext-setup-0.34/spec/fixtures/tmp_locales/sinatra-i18n.pot0000644000004100000410000000205613616243263024372 0ustar www-datawww-data# SOME DESCRIPTIVE TITLE. # Copyright (C) 2017 Puppet Labs, LLC. # This file is distributed under the same license as the Sinatra i18n demo package. # FIRST AUTHOR , 2017. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Sinatra i18n demo 0.21-4-geccdac4\n" "\n" "Report-Msgid-Bugs-To: docs@puppetlabs.com\n" "POT-Creation-Date: 2017-04-11 20:08-0500\n" "PO-Revision-Date: 2017-04-11 20:08-0500\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: ../../lib/gettext_setup_spec.rb:26 ../../lib/gettext_setup_spec.rb:89 ../../lib/gettext_setup_spec.rb:91 msgid "Hello, world!" msgstr "" #: ../../lib/pot_spec.rb:52 msgid "merged-po-file" msgstr "" #: ../../lib/pot_spec.rb:74 msgid "no-pot-file" msgstr "" #: ../../lib/pot_spec.rb:85 msgid "some-spec-only-string" msgstr "" #: ../../lib/pot_spec.rb:104 msgid "unchanged-string" msgstr "" gettext-setup-0.34/spec/fixtures/locales/0000755000004100000410000000000013616243263020545 5ustar www-datawww-datagettext-setup-0.34/spec/fixtures/locales/config.yaml0000644000004100000410000000155113616243263022700 0ustar www-datawww-data--- # This is the project-specific configuration file for setting up # fast_gettext for your project. gettext: # This is used for the name of the .pot and .po files; they will be # called .pot? project_name: 'sinatra-i18n' # This is used in comments in the .pot and .po files to indicate what # project the files belong to and should bea little more desctiptive than # package_name: Sinatra i18n demo # The locale that the default messages in the .pot file are in default_locale: en # The email used for sending bug reports. bugs_address: docs@puppetlabs.com # The holder of the copyright. copyright_holder: Puppet Labs, LLC. # Patterns for +Dir.glob+ used to find all files that might contain # translatable content, relative to the project root directory source_files: - 'lib/**/*.rb' - 'spec/**/*.rb' gettext-setup-0.34/spec/fixtures/locales/de/0000755000004100000410000000000013616243263021135 5ustar www-datawww-datagettext-setup-0.34/spec/fixtures/locales/de/sinatra-i18n.po0000644000004100000410000000130113616243263023706 0ustar www-datawww-data# German translations for Sinatra i18n demo package. # Copyright (C) 2016 Puppet Labs, LLC. # This file is distributed under the same license as the Sinatra i18n demo package. # Automatically generated, 2016. # msgid "" msgstr "" "Project-Id-Version: Sinatra i18n demo \n" "Report-Msgid-Bugs-To: docs@puppetlabs.com\n" "POT-Creation-Date: 2016-04-05 10:39-0700\n" "PO-Revision-Date: 2016-02-26 18:21-0800\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../app.rb:14 msgid "Hello, world!" msgstr "Hallo, Welt!" gettext-setup-0.34/spec/fixtures/locales/sinatra-i18n.pot0000644000004100000410000000205613616243263023512 0ustar www-datawww-data# SOME DESCRIPTIVE TITLE. # Copyright (C) 2017 Puppet Labs, LLC. # This file is distributed under the same license as the Sinatra i18n demo package. # FIRST AUTHOR , 2017. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Sinatra i18n demo 0.21-4-geccdac4\n" "\n" "Report-Msgid-Bugs-To: docs@puppetlabs.com\n" "POT-Creation-Date: 2017-04-11 20:08-0500\n" "PO-Revision-Date: 2017-04-11 20:08-0500\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: ../../lib/gettext_setup_spec.rb:26 ../../lib/gettext_setup_spec.rb:89 ../../lib/gettext_setup_spec.rb:91 msgid "Hello, world!" msgstr "" #: ../../lib/pot_spec.rb:52 msgid "merged-po-file" msgstr "" #: ../../lib/pot_spec.rb:74 msgid "no-pot-file" msgstr "" #: ../../lib/pot_spec.rb:85 msgid "some-spec-only-string" msgstr "" #: ../../lib/pot_spec.rb:104 msgid "unchanged-string" msgstr "" gettext-setup-0.34/spec/fixtures/fixture_locales/0000755000004100000410000000000013616243263022313 5ustar www-datawww-datagettext-setup-0.34/spec/fixtures/fixture_locales/config.yaml0000644000004100000410000000154513616243263024451 0ustar www-datawww-data--- # This is the project-specific configuration file for setting up # fast_gettext for your project. gettext: # This is used for the name of the .pot and .po files; they will be # called .pot? project_name: 'fixture_locales' # This is used in comments in the .pot and .po files to indicate what # project the files belong to and should bea little more desctiptive than # package_name: Fixture locales # The locale that the default messages in the .pot file are in default_locale: en # The email used for sending bug reports. bugs_address: docs@puppetlabs.com # The holder of the copyright. copyright_holder: Puppet, LLC. # Patterns for +Dir.glob+ used to find all files that might contain # translatable content, relative to the project root directory source_files: - 'spec/fixtures/fixture_locales/*.rb' gettext-setup-0.34/spec/fixtures/fixture_locales/test_strings.rb0000644000004100000410000000006213616243263025366 0ustar www-datawww-data# frozen_string_literal: true _('Hello, world!') gettext-setup-0.34/spec/fixtures/fixture_locales/jp/0000755000004100000410000000000013616243263022724 5ustar www-datawww-datagettext-setup-0.34/spec/fixtures/fixture_locales/jp/fixture_locales.po0000644000004100000410000000131213616243263026451 0ustar www-datawww-data# German translations for Sinatra i18n demo package. # Copyright (C) 2016 Puppet Labs, LLC. # This file is distributed under the same license as the Sinatra i18n demo package. # Automatically generated, 2016. # msgid "" msgstr "" "Project-Id-Version: Sinatra i18n demo \n" "Report-Msgid-Bugs-To: docs@puppetlabs.com\n" "POT-Creation-Date: 2016-04-05 10:39-0700\n" "PO-Revision-Date: 2016-02-26 18:21-0800\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: jp\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" #: ../app.rb:14 msgid "Hello, world!" msgstr "こんにちは世界" gettext-setup-0.34/spec/fixtures/fixture_locales/fixture_locales.pot0000644000004100000410000000131713616243263026231 0ustar www-datawww-data# SOME DESCRIPTIVE TITLE. # Copyright (C) 2017 Puppet, LLC. # This file is distributed under the same license as the Fixture locales package. # FIRST AUTHOR , 2017. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Fixture locales 0.21-6-gc301398\n" "\n" "Report-Msgid-Bugs-To: docs@puppetlabs.com\n" "POT-Creation-Date: 2017-04-12 00:27-0500\n" "PO-Revision-Date: 2017-04-12 00:27-0500\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: test_strings.rb:1 msgid "Hello, world!" msgstr "" gettext-setup-0.34/spec/fixtures/string_changes/0000755000004100000410000000000013616243263022121 5ustar www-datawww-datagettext-setup-0.34/spec/fixtures/string_changes/change.pot0000644000004100000410000000161513616243263024075 0ustar www-datawww-data# SOME DESCRIPTIVE TITLE. # Copyright (C) 2016 Puppet Labs, LLC. # This file is distributed under the same license as the Sinatra i18n demo package. # FIRST AUTHOR , 2016. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Sinatra i18n demo init-11-ga552a06\n" "\n" "Report-Msgid-Bugs-To: docs@puppetlabs.com\n" "POT-Creation-Date: 2016-06-07 17:38-0500\n" "PO-Revision-Date: 2016-06-07 17:38-0500\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #. GettextSetup.initialize(File::join(File::dirname(File::dirname(__FILE__)), 'fixtures')) #: ../../lib/gettext_setup_spec.rb:25 msgid "Hello, world!" msgstr "" #: ../../lib/gettext_setup_spec.rb:25 msgid "changed string" msgstr "" gettext-setup-0.34/spec/fixtures/string_changes/old.pot0000644000004100000410000000161613616243263023427 0ustar www-datawww-data# SOME DESCRIPTIVE TITLE. # Copyright (C) 2016 Puppet Labs, LLC. # This file is distributed under the same license as the Sinatra i18n demo package. # FIRST AUTHOR , 2016. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Sinatra i18n demo init-11-ga552a06\n" "\n" "Report-Msgid-Bugs-To: docs@puppetlabs.com\n" "POT-Creation-Date: 2016-06-07 17:38-0500\n" "PO-Revision-Date: 2016-06-07 17:38-0500\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #. GettextSetup.initialize(File::join(File::dirname(File::dirname(__FILE__)), 'fixtures')) #: ../../lib/gettext_setup_spec.rb:25 msgid "Hello, world!" msgstr "" #: ../../lib/gettext_setup_spec.rb:25 msgid "Goodbye, world!" msgstr "" gettext-setup-0.34/spec/fixtures/string_changes/remove.pot0000644000004100000410000000150513616243263024143 0ustar www-datawww-data# SOME DESCRIPTIVE TITLE. # Copyright (C) 2016 Puppet Labs, LLC. # This file is distributed under the same license as the Sinatra i18n demo package. # FIRST AUTHOR , 2016. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Sinatra i18n demo init-11-ga552a06\n" "\n" "Report-Msgid-Bugs-To: docs@puppetlabs.com\n" "POT-Creation-Date: 2016-06-07 17:38-0500\n" "PO-Revision-Date: 2016-06-07 17:38-0500\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #. GettextSetup.initialize(File::join(File::dirname(File::dirname(__FILE__)), 'fixtures')) #: ../../lib/gettext_setup_spec.rb:25 msgid "Hello, world!" msgstr "" gettext-setup-0.34/spec/fixtures/string_changes/add.pot0000644000004100000410000000172213616243263023377 0ustar www-datawww-data# SOME DESCRIPTIVE TITLE. # Copyright (C) 2016 Puppet Labs, LLC. # This file is distributed under the same license as the Sinatra i18n demo package. # FIRST AUTHOR , 2016. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Sinatra i18n demo init-11-ga552a06\n" "\n" "Report-Msgid-Bugs-To: docs@puppetlabs.com\n" "POT-Creation-Date: 2016-06-07 17:38-0500\n" "PO-Revision-Date: 2016-06-07 17:38-0500\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #. GettextSetup.initialize(File::join(File::dirname(File::dirname(__FILE__)), 'fixtures')) #: ../../lib/gettext_setup_spec.rb:25 msgid "Hello, world!" msgstr "" #: ../../lib/gettext_setup_spec.rb:25 msgid "Goodbye, world!" msgstr "" #: ../../lib/gettext_setup_spec.rb:25 msgid "new string" msgstr "" gettext-setup-0.34/spec/fixtures/string_changes/non_string_changes.pot0000644000004100000410000000161213616243263026515 0ustar www-datawww-data# SOME DESCRIPTIVE TITLE. # Copyright (C) 2016 Puppet Labs, LLC. # This file is distributed under the same license as the Sinatra i18n demo package. # FIRST AUTHOR , 2016. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Sinatra i18n demo init-11-ga552a06\n" "\n" "Report-Msgid-Bugs-To: docs@puppetlabs.com\n" "POT-Creation-Date: 2017-04-17 17:38-0500\n" "PO-Revision-Date: 2017-04-17 17:38-0500\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #. GettextSetup.initialize(File::join(File::dirname(File::dirname(__FILE__)), 'fixtures')) #: ../../lib/gettext_setup_spec.rb:25 msgid "Hello, world!" msgstr "" #: ../../lib/different_file.rb:25 msgid "Goodbye, world!" msgstr "" gettext-setup-0.34/spec/lib/0000755000004100000410000000000013616243263016020 5ustar www-datawww-datagettext-setup-0.34/spec/lib/tasks/0000755000004100000410000000000013616243263017145 5ustar www-datawww-datagettext-setup-0.34/spec/lib/tasks/gettext_rake_spec.rb0000644000004100000410000001110713616243263023172 0ustar www-datawww-data# frozen_string_literal: true require 'rspec/expectations' require 'rake' require_relative '../../spec_helper.rb' load File.expand_path('../../../../lib/tasks/gettext.rake', __FILE__) describe 'gettext.rake' do locales = File.expand_path('../../fixtures/locales', File.dirname(__FILE__)) tmp_locales = File.expand_path('../../fixtures/tmp_locales', File.dirname(__FILE__)) fixture_locales = File.expand_path('../../fixtures/fixture_locales', File.dirname(__FILE__)) tmp_pot_path = File.expand_path('sinatra-i18n.pot', tmp_locales) merge_locales = File.expand_path('../../fixtures/merge_locales', File.dirname(__FILE__)) before :each do FileUtils.rm_r(tmp_locales, force: true) FileUtils.cp_r(locales, tmp_locales) end after :each do GettextSetup.clear Rake::Task.tasks.each(&:reenable) end around :each do |test| # Since we have `exit 1` in these rake tasks, we need to explicitly tell # rspec that any unexpected errors aren't expected. Otherwise, if a # SystemExit error is thrown, it just doesn't finish running the rest of # the tests and considers the suite passing... expect { test.run }.not_to raise_error end context Rake::Task['gettext:pot'] do it 'outputs correctly' do expect do GettextSetup.initialize(tmp_locales) subject.invoke end.to output(/POT file .+\/spec\/fixtures\/tmp_locales\/sinatra-i18n.pot has been generated/).to_stdout end it 'exits 1 on error' do allow(GettextSetup::Pot).to receive(:generate_new_pot).and_return(false) expect do GettextSetup.initialize(tmp_locales) subject.invoke end.to raise_error(SystemExit) end end context Rake::Task['gettext:pot'] do it 'outputs correctly, when passing a filename' do expect do GettextSetup.initialize(tmp_locales) subject.invoke(File.expand_path('bill.pot', tmp_locales)) end.to output(/POT file .+\/spec\/fixtures\/tmp_locales\/bill.pot has been generated/).to_stdout end end context Rake::Task['gettext:metadata_pot'] do it 'outputs correctly' do expect do GettextSetup.initialize(tmp_locales) subject.invoke end.to output(/POT metadata file .+sinatra-i18n_metadata.pot has been generated/).to_stdout end it 'exits 1 on error' do allow(GettextSetup::MetadataPot).to receive(:generate_metadata_pot).and_return(false) expect do GettextSetup.initialize(tmp_locales) subject.invoke end.to raise_error(SystemExit) end end context Rake::Task['gettext:po'] do it 'outputs correctly' do expect do GettextSetup.initialize(tmp_locales) subject.invoke('de') end.to output(/PO file .+de\/sinatra-i18n.po merged/).to_stdout end it 'exits 1 on error' do allow(GettextSetup::Pot).to receive(:generate_new_po).with('de').and_return(false) expect do GettextSetup.initialize(tmp_locales) subject.invoke('de') end.to raise_error(SystemExit) end end context Rake::Task['gettext:update_pot'] do it 'does not update the POT when no changes are detected' do expect do GettextSetup.initialize(tmp_locales) subject.invoke end.to output(/No string changes detected, keeping old POT file/).to_stdout end it 'can create a new POT' do FileUtils.rm(tmp_pot_path) expect do GettextSetup.initialize(tmp_locales) subject.invoke end.to output(/No existing POT file, generating new\nPOT file .+sinatra-i18n.pot has been generated/).to_stdout end it 'can update the POT' do fixture_locales_pot = File.expand_path('fixture_locales.pot', fixture_locales) FileUtils.cp(fixture_locales_pot, tmp_pot_path) expect do GettextSetup.initialize(tmp_locales) subject.invoke end.to output(/String changes detected, replacing with updated POT file/).to_stdout end it 'exits 1 upon error' do allow(GettextSetup::Pot).to receive(:update_pot).and_return(false) expect do GettextSetup.initialize(tmp_locales) subject.invoke end.to raise_error(SystemExit) end end context Rake::Task['gettext:merge'] do it 'outputs correctly' do expect do GettextSetup.initialize(merge_locales) subject.invoke end.to output(/PO files have been successfully merged/).to_stdout end it 'exits 1 on error' do allow(GettextSetup::Pot).to receive(:merge).and_return(false) expect do GettextSetup.initialize(merge_locales) subject.invoke end.to raise_error(SystemExit) end end end gettext-setup-0.34/spec/lib/gettext-setup/0000755000004100000410000000000013616243263020642 5ustar www-datawww-datagettext-setup-0.34/spec/lib/gettext-setup/metadata_pot_spec.rb0000644000004100000410000000462713616243263024654 0ustar www-datawww-data# frozen_string_literal: true require 'rspec/expectations' require 'tmpdir' require_relative '../../spec_helper' require_relative '../../../lib/gettext-setup' describe GettextSetup::MetadataPot do before(:each) do GettextSetup.initialize(File.absolute_path(File.join(File.dirname(__FILE__), '../../fixtures/locales'))) end context '#metadata_path' do it 'finds the right metadata path' do expect(GettextSetup::MetadataPot.metadata_path).to match(/sinatra-i18n_metadata\.pot/) end end context '#pot_string' do it 'generates a reasonable POT string' do expect(GettextSetup::MetadataPot.pot_string({})).to match(/Last-Translator: FULL NAME /) end it 'includes summary when provided' do metadata = { 'summary' => 'abc' } expect(GettextSetup::MetadataPot.pot_string(metadata)).to match(/msgid "abc"/) end it 'includes summary when provided' do metadata = { 'description' => 'def' } expect(GettextSetup::MetadataPot.pot_string(metadata)).to match(/msgid "def"/) end it 'includes both summary and description when provided' do metadata = { 'summary' => 'abc', 'description' => 'def' } result = expect(GettextSetup::MetadataPot.pot_string(metadata)) result.to match(/msgid "def"/) result.to match(/msgid "abc"/) end end context '#load_metadata' do it 'loads metadata correctly' do Dir.mktmpdir do |dir| file = File.join(dir, 'metadata.json') File.open(file, 'w') { |f| f.write('{"description":"abcdef", "summary":"ghi"}') } metadata = GettextSetup::MetadataPot.metadata(File.join(dir, 'metadata.json').to_s) expect(metadata).to eq('description' => 'abcdef', 'summary' => 'ghi') end end it 'uses an empty hash if no metadata.json is found' do metadata = GettextSetup::MetadataPot.metadata(File.join(Dir.mktmpdir, 'metadata.json').to_s) expect(metadata).to eq({}) end end context '#generate_metadata_pot' do it 'works with everything supplied' do dir = Dir.mktmpdir file = File.join(dir, 'metadata.pot') metadata = { 'description' => 'abc', 'summary' => 'def' } GettextSetup::MetadataPot.generate_metadata_pot(metadata, file) contents = File.read(file) expect(contents).to match(/msgid "abc"/) expect(contents).to match(/msgid "def"/) end end end gettext-setup-0.34/spec/lib/gettext-setup/pot_spec.rb0000644000004100000410000002233013616243263023003 0ustar www-datawww-data# frozen_string_literal: true require 'rspec/expectations' require 'tmpdir' require_relative '../../spec_helper.rb' require_relative '../../../lib/gettext-setup' describe GettextSetup::Pot do NoConfigFoundError = GettextSetup::NoConfigFoundError def fixture_locales_path File.join(File.dirname(__FILE__), '../../fixtures/fixture_locales') end def spec_locales_path File.join(File.dirname(__FILE__), '../../fixtures/spec_locales') end def locales_path File.join(File.dirname(__FILE__), '../../fixtures/locales') end def merge_locales_path File.join(File.dirname(__FILE__), '../../fixtures/merge_locales') end describe 'string_changes?', if: msgcmp_present? do old_pot = File.absolute_path('../../fixtures/string_changes/old.pot', File.dirname(__FILE__)) it 'should detect string addition' do new_pot = File.absolute_path('../../fixtures/string_changes/add.pot', File.dirname(__FILE__)) expect(GettextSetup::Pot.string_changes?(old_pot, new_pot)).to eq(true) end it 'should detect string removal' do new_pot = File.absolute_path('../../fixtures/string_changes/remove.pot', File.dirname(__FILE__)) expect(GettextSetup::Pot.string_changes?(old_pot, new_pot)).to eq(true) end it 'should detect string changes' do new_pot = File.absolute_path('../../fixtures/string_changes/change.pot', File.dirname(__FILE__)) expect(GettextSetup::Pot.string_changes?(old_pot, new_pot)).to eq(true) end it 'should not detect non-string changes' do new_pot = File.absolute_path('../../fixtures/string_changes/non_string_changes.pot', File.dirname(__FILE__)) expect(GettextSetup::Pot.string_changes?(old_pot, new_pot)).to eq(false) end end context 'generate_new_pot' do it "fails when GettextSetup can't find a config.yaml" do path = File.join(Dir.mktmpdir, 'empty.pot') expect { GettextSetup::Pot.generate_new_pot(locales_path: Dir.mktmpdir, target_path: path) }.to raise_error(NoConfigFoundError) end it 'builds a POT file' do path = File.join(Dir.mktmpdir, 'new.pot') expect do GettextSetup::Pot.generate_new_pot(locales_path: fixture_locales_path, target_path: path) end.to output('').to_stdout # STDOUT is determined in `update_pot`. contents = File.read(path) expect(contents).to match(/Fixture locales/) expect(contents).to match(/docs@puppetlabs.com/) expect(contents).to match(/Puppet, LLC/) expect(contents).to match(/test_strings.rb:3/) end it 'builds a POT file with :header_only' do path = File.join(Dir.mktmpdir, 'new.pot') expect do GettextSetup::Pot.generate_new_pot(locales_path: fixture_locales_path, target_path: path, header_only: true) end.to output('').to_stdout # STDOUT is determined in `update_pot` contents = File.read(path) expect(contents).to_not match(/Hello, world/) expect(contents).to match(/Fixture locales/) expect(contents).to match(/docs@puppetlabs.com/) expect(contents).to match(/Puppet, LLC/) end end context 'generate_new_po' do it "fails when GettextSetup can't find a config.yaml" do path = File.join(Dir.mktmpdir, 'fails.pot') po_path = File.join(Dir.mktmpdir, 'fails.po') expect { GettextSetup::Pot.generate_new_po('ja', Dir.mktmpdir, path, po_path) }.to raise_error(NoConfigFoundError) end it 'complains when no language is supplied' do allow(ENV).to receive(:[]).and_call_original allow(ENV).to receive(:[]).with('LANGUAGE').and_return(nil) result = "You need to specify the language to add. Either 'LANGUAGE=eo rake gettext:po' or 'rake gettext:po[LANGUAGE]'\n" expect do GettextSetup::Pot.generate_new_po(nil, fixture_locales_path, Dir.mktmpdir, Dir.mktmpdir) end.to output(result).to_stdout end it 'generates new PO file', if: msginit_present? do po_path = File.join(Dir.mktmpdir, 'aa', 'tmp.po') pot_path = File.join(locales_path, 'sinatra-i18n.pot') expect do GettextSetup::Pot.generate_new_po('aa', locales_path, pot_path, po_path) end.to output("PO file #{po_path} created\n").to_stdout end it 'merges PO files', if: [msginit_present?, msgmerge_present?] do _('merged-po-file') po_path = File.join(Dir.mktmpdir, 'aa', 'tmp.po') pot_path = GettextSetup::Pot.pot_file_path expect do GettextSetup::Pot.generate_new_po('aa', fixture_locales_path, pot_path, po_path) end.to output("PO file #{po_path} created\n").to_stdout contents = File.read(po_path) expect(contents).to match(/msgid "Hello, world!"/) new_pot_path = File.join(spec_locales_path, 'sinatra-i18n.pot') expect do GettextSetup::Pot.generate_new_po('aa', spec_locales_path, new_pot_path, po_path) end.to output("PO file #{po_path} merged\n").to_stdout new_contents = File.read(po_path) expect(new_contents).to match(/merged-po-file/) end end context 'update_pot' do it "fails when GettextSetup can't find a config.yaml" do path = File.join(Dir.mktmpdir, 'fail-update.pot') expect { GettextSetup::Pot.update_pot(Dir.mktmpdir, path) }.to raise_error(NoConfigFoundError) end it 'creates POT when absent' do _('no-pot-file') path = File.join(Dir.mktmpdir, 'some-pot.pot') expect do GettextSetup::Pot.update_pot(spec_locales_path, path) end.to output("No existing POT file, generating new\nPOT file #{path} has been generated\n").to_stdout contents = File.read(path) expect(contents).to match(/msgid "no-pot-file"/) end it 'updates POT when something changes', if: [msginit_present?, msgmerge_present?] do _('some-spec-only-string') path = File.join(Dir.mktmpdir, 'some-pot.pot') expect do GettextSetup::Pot.update_pot(fixture_locales_path, path) end.to output("No existing POT file, generating new\nPOT file #{path} has been generated\n").to_stdout contents = File.read(path) expect(contents).to match(/Language-Team: LANGUAGE /) expect(contents).not_to match(/some-spec-only-string/) expect do GettextSetup::Pot.update_pot(spec_locales_path, path) end.to output("String changes detected, replacing with updated POT file\n").to_stdout new_contents = File.read(path) expect(new_contents).to match(/some-spec-only-string/) end it "doesn't update the POT when nothing changes", if: [msginit_present?, msgcmp_present?] do _('unchanged-string') path = File.join(Dir.mktmpdir, 'some-pot.pot') expect do GettextSetup::Pot.update_pot(spec_locales_path, path) end.to output("No existing POT file, generating new\nPOT file #{path} has been generated\n").to_stdout contents = File.read(path) expect(contents).to match(/unchanged-string/) expect do GettextSetup::Pot.update_pot(spec_locales_path, path) end.to output("No string changes detected, keeping old POT file\n").to_stdout new_contents = File.read(path) expect(new_contents).to eq(contents) end end context 'Merge pot files' do # setup before :all do { 'ruby' => 'ruby.pot', 'puppet' => 'puppet.pot', 'metadata' => 'metadata.pot' }.each do |pot_type, pot_name| File.open(File.join(merge_locales_path, pot_name), 'w') do |file| file.write <, 2017. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: puppetlabs-mysql 3.11.0-30-g4cc0bbf\\n" "Report-Msgid-Bugs-To: docs@puppet.com\\n" "POT-Creation-Date: 2017-08-26 21:30+0100\\n" "PO-Revision-Date: 2017-08-26 21:30+0100\\n" "Last-Translator: FULL NAME \\n" "Language-Team: LANGUAGE \\n" "MIME-Version: 1.0\\n" "Content-Type: text/plain; charset=UTF-8\\n" "Content-Transfer-Encoding: 8bit\\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n" #: ../lib/puppet/parser/functions/mysql_strip_hash.rb:11 msgid "this is a #{pot_type} string" msgstr "" POT end end end it 'merges pot files' do expect do GettextSetup::Pot.merge(locales_path: merge_locales_path) end.to output(%r{PO files have been successfully merged}).to_stdout contents = File.read(File.join(merge_locales_path, GettextSetup.config['project_name'] + '.pot')) expect(contents).to match(%r{.*\"this is a metadata string\".*}) expect(contents).to match(%r{.*\"this is a puppet string\".*}) expect(contents).to match(%r{.*\"this is a ruby string\".*}) end it 'creates an oldpot file if one already exists' do expect do GettextSetup::Pot.merge(locales_path: merge_locales_path) end.to output("Warning - merge_locales.pot already exists and will be relocated to oldpot/old_merge_locales.pot.\nPO files have been successfully merged, merge_locales.pot has been created.\n").to_stdout file = File.expand_path('oldpot/old_merge_locales.pot', merge_locales_path) expect(File.exist?(file)).to be true end # cleanup after :all do FileUtils.rm(Dir.glob("#{merge_locales_path}/*.pot")) FileUtils.rm_rf("#{merge_locales_path}/oldpot") end end end gettext-setup-0.34/spec/lib/gettext-setup/gettext_setup_spec.rb0000644000004100000410000001152713616243263025113 0ustar www-datawww-data# encoding: utf-8 # frozen_string_literal: true require 'rspec/expectations' require_relative '../../spec_helper' require_relative '../../../lib/gettext-setup' describe GettextSetup do locales_path = File.absolute_path(File.join(File.dirname(__FILE__), '../../fixtures/locales')) fixture_locales_path = File.absolute_path(File.join(File.dirname(__FILE__), '../../fixtures/fixture_locales')) before(:each) do GettextSetup.initialize(locales_path) end after(:each) do FastGettext.default_text_domain = nil FastGettext.default_available_locales = nil FastGettext.default_locale = nil FastGettext.text_domain = nil FastGettext.available_locales = nil FastGettext.locale = nil end let(:config) do GettextSetup.config end context 'initialize' do it 'sets up correctly' do expect(GettextSetup.locales_path).to match(/\/spec\/fixtures/) expect(config['project_name']).to eq('sinatra-i18n') expect(config['package_name']).to eq('Sinatra i18n demo') expect(config['default_locale']).to eq('en') expect(respond_to?(:_)).to eq(true) end end context 'negotiate_locale' do it 'negotiates correctly' do FastGettext.locale = GettextSetup.negotiate_locale('de') expect(FastGettext.locale).to eq('de') expect(_('Hello, world!')).to eq('Hallo, Welt!') end it 'chooses the default locale when no match is found' do expect(GettextSetup.negotiate_locale('no-match')).to eq(config['default_locale']) end it 'chooses the language with the highest q value' do expect(GettextSetup.negotiate_locale('en;q=1, de;q=2')).to eq('de') expect(GettextSetup.negotiate_locale('en;q=1, de;q=0')).to eq('en') end it 'ignores country variant' do expect(GettextSetup.negotiate_locale('en;q=1, de-DE;q=2')).to eq('de') expect(GettextSetup.negotiate_locale('en;q=1, de-DE;q=0')).to eq('en') end it 'chooses the first value when q values are equal' do expect(GettextSetup.negotiate_locale('de;q=1, en;q=1')).to eq('de') end end context 'negotiate_locale!' do it 'sets the locale' do GettextSetup.negotiate_locale!('de') expect(FastGettext.locale).to eq('de') expect(_('Hello, world!')).to eq('Hallo, Welt!') GettextSetup.negotiate_locale!('en') expect(FastGettext.locale).to eq('en') expect(_('Hello, world!')).to eq('Hello, world!') end end context 'setting default_locale' do before :each do GettextSetup.default_locale = 'en' end it 'allows setting the default locale' do expect(GettextSetup.default_locale).to eq('en') GettextSetup.default_locale = 'de' expect(GettextSetup.default_locale).to eq('de') end end context 'clear' do it 'can clear the locale' do expect(GettextSetup.default_locale).to eq('en') expect(GettextSetup.candidate_locales).to include('en') GettextSetup.clear begin old_locale = Locale.current Locale.current = 'de_DE' expect(GettextSetup.candidate_locales).to eq('de_DE,de,en') ensure Locale.current = old_locale end end end context 'multiple locales' do # locales/ loads the de locale and fixture_locales/ loads the jp locale before(:each) do GettextSetup.initialize(fixture_locales_path) end it 'can aggregate locales across projects' do expect(FastGettext.default_available_locales).to include('en') expect(FastGettext.default_available_locales).to include('de') expect(FastGettext.default_available_locales).to include('jp') end it 'can switch to loaded locale' do FastGettext.locale = GettextSetup.negotiate_locale('de,en') expect(FastGettext.locale).to eq('de') FastGettext.locale = GettextSetup.negotiate_locale('jp') expect(FastGettext.locale).to eq('jp') end end context 'translation repository chain' do before(:each) do GettextSetup.initialize(fixture_locales_path) end it 'chain is not nil' do expect(GettextSetup.translation_repositories).not_to be_nil end it 'can translate without switching text domains' do FastGettext.locale = 'de' expect(_('Hello, world!')).to eq('Hallo, Welt!') FastGettext.locale = 'jp' expect(_('Hello, world!')).to eq('こんにちは世界') end it 'does not allow duplicate repositories' do GettextSetup.initialize(fixture_locales_path) repos = GettextSetup.translation_repositories expect(repos.select { |k, _| k == 'fixture_locales' }.size).to eq(1) end it 'does allow multiple unique domains' do GettextSetup.initialize(locales_path) repos = GettextSetup.translation_repositories expect(repos.size == 2) expect(repos.select { |k, _| k == 'fixture_locales' }.size).to eq(1) expect(repos.select { |k, _| k == 'sinatra-i18n' }.size).to eq(1) end end end gettext-setup-0.34/spec/spec_helper.rb0000644000004100000410000000101313616243263020063 0ustar www-datawww-data# frozen_string_literal: true require 'simplecov' require_relative '../lib/gettext-setup' GettextSetup.initialize(File.join(File.dirname(__FILE__), 'fixtures', 'locales')) SimpleCov.start do add_filter '/spec/' end def cmd_present?(cmd) # Try to call out to msgcmp, if it doesn't error, we have the tool `#{cmd} --version` true rescue IOError false end def msgcmp_present? cmd_present?('msgcmp') end def msginit_present? cmd_present?('msginit') end def msgmerge_present? cmd_present?('msgmerge') end gettext-setup-0.34/LICENSE0000644000004100000410000000122713616243263015327 0ustar www-datawww-data gettext-setup gem Copyright (C) 2016 Puppet, Inc. Puppet, Inc. can be contacted at: info@puppet.com Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.gettext-setup-0.34/lib/0000755000004100000410000000000013616243263015066 5ustar www-datawww-datagettext-setup-0.34/lib/tasks/0000755000004100000410000000000013616243263016213 5ustar www-datawww-datagettext-setup-0.34/lib/tasks/gettext.rake0000644000004100000410000000335513616243263020551 0ustar www-datawww-data# frozen_string_literal: true require_relative '../gettext-setup/gettext_setup' require_relative '../gettext-setup/pot' require_relative '../gettext-setup/metadata_pot' namespace :gettext do desc 'Generate a new POT file and replace old if strings changed' task :update_pot do begin result = GettextSetup::Pot.update_pot exit 1 unless result rescue GettextSetup::NoConfigFoundError => e puts e.message exit 1 end end desc 'Generate POT file' task :pot, [:target_path] do |_, args| begin target_path = args.target_path if GettextSetup::Pot.generate_new_pot(target_path: target_path) target_path = GettextSetup::Pot.pot_file_path if target_path.nil? puts "POT file #{target_path} has been generated" else exit 1 end rescue GettextSetup::NoConfigFoundError => e puts e.message end end desc 'Generate POT file for metadata' task :metadata_pot do begin result = GettextSetup::MetadataPot.generate_metadata_pot if result puts "POT metadata file #{GettextSetup::MetadataPot.metadata_path} has been generated" else exit 1 end rescue GettextSetup::NoConfigFoundError => e puts e.message end end desc 'Update PO file for a specific language' task :po, [:language] do |_, args| begin result = GettextSetup::Pot.generate_new_po(args.language) exit 1 unless result rescue GettextSetup::NoConfigFoundError => e puts e.message end end desc 'Merge all Pot files within locales folder' task :merge do begin result = GettextSetup::Pot.merge exit 1 unless result rescue GettextSetup::NoConfigFoundError => e puts e.message end end end gettext-setup-0.34/lib/templates/0000755000004100000410000000000013616243263017064 5ustar www-datawww-datagettext-setup-0.34/lib/templates/metadata.pot.erb0000644000004100000410000000121713616243263022140 0ustar www-datawww-data# #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To:\n" "POT-Creation-Date: <%= DateTime.now %>\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Translate Toolkit 2.0.0\n"<% if metadata.key?('summary') %> #. metadata.json #: .summary msgid "<%= metadata['summary']%>" msgstr ""<% end %><% if metadata.key?('description')%> #. metadata.json #: .description msgid "<%= metadata['description']%>" msgstr "" <% end %> gettext-setup-0.34/lib/gettext-setup/0000755000004100000410000000000013616243263017710 5ustar www-datawww-datagettext-setup-0.34/lib/gettext-setup/pot.rb0000644000004100000410000001574513616243263021053 0ustar www-datawww-data# frozen_string_literal: true require 'open3' require 'English' require 'tempfile' module GettextSetup module Pot def self.text_domain FastGettext.text_domain end def self.files_to_translate files = (GettextSetup.config['source_files'] || []).map do |p| Dir.glob(p) end.flatten # check for optional list of files to exclude from string # extraction exclusions = (GettextSetup.config['exclude_files'] || []).map do |p| Dir.glob(p) end.flatten # if file is a directory, take it out of the array. directories # cause rxgettext to error out. (files - exclusions).reject { |file| File.directory?(file) } end def self.pot_file_path return if GettextSetup.locales_path.nil? return if GettextSetup.config['project_name'].nil? File.join(GettextSetup.locales_path, GettextSetup.config['project_name'] + '.pot') end def self.po_file_path(language) return if GettextSetup.locales_path.nil? return if GettextSetup.config['project_name'].nil? return if language.nil? File.join(GettextSetup.locales_path, language, GettextSetup.config['project_name'] + '.po') end def self.string_changes?(old_pot, new_pot) # Warnings will be in another language if locale is not set to en_US _, stderr, status = Open3.capture3("LANG=en_US msgcmp --use-untranslated '#{old_pot}' '#{new_pot}'") return true if status.exitstatus == 1 || \ /this message is not used/.match(stderr) || \ /this message is used but not defined/.match(stderr) if stderr =~ /msgcmp: command not found/ puts 'Warning - msgcmp is not present on the system' return true end false rescue IOError # probably means msgcmp is not present on the system # so return true to be on the safe side true end # @param [:locales_path] opts # The directory for the locales. # @param [:target_path] opts # The output path for the new POT file. # @param [:header_only] opts # Set to true to create a .pot file with only a header def self.generate_new_pot(opts = {}) locales_path = opts[:locales_path] || GettextSetup.locales_path GettextSetup.initialize_config(locales_path) target_path = opts[:target_path] || pot_file_path input_files = if opts[:header_only] tmpfile = Tempfile.new('gettext-setup.tmp') tmpfile.path else files_to_translate.join(' ') end config = GettextSetup.config package_name = config['package_name'] bugs_address = config['bugs_address'] copyright_holder = config['copyright_holder'] # Done this way to allow the user to enter an empty string in the config. comments_tag = config.key?('comments_tag') ? config['comments_tag'] : 'TRANSLATORS' version = `git describe` system("rxgettext -o #{target_path} --no-wrap --sort-by-file " \ "--add-comments#{comments_tag.to_s == '' ? '' : '=' + comments_tag} --msgid-bugs-address '#{bugs_address}' " \ "--package-name '#{package_name}' " \ "--package-version '#{version}' " \ "--copyright-holder='#{copyright_holder}' --copyright-year=#{Time.now.year} " \ "#{input_files}") tmpfile.unlink if tmpfile $CHILD_STATUS.success? end def self.generate_new_po(language, locales_path = GettextSetup.locales_path, pot_file = nil, po_file = nil) GettextSetup.initialize_config(locales_path) language ||= ENV['LANGUAGE'] pot_file ||= GettextSetup::Pot.pot_file_path po_file ||= GettextSetup::Pot.po_file_path(language) # Let's do some pre-verification of the environment. if language.nil? puts "You need to specify the language to add. Either 'LANGUAGE=eo rake gettext:po' or 'rake gettext:po[LANGUAGE]'" return end language_path = File.dirname(po_file) FileUtils.mkdir_p(language_path) if File.exist?(po_file) cmd = "msgmerge -U #{po_file} #{pot_file}" _, _, _, wait = Open3.popen3(cmd) exitstatus = wait.value if exitstatus.success? puts "PO file #{po_file} merged" true else puts 'PO file merge failed' false end else cmd = "msginit --no-translator -l #{language} -o #{po_file} -i #{pot_file}" _, _, _, wait = Open3.popen3(cmd) exitstatus = wait.value if exitstatus.success? puts "PO file #{po_file} created" true else puts 'PO file creation failed' false end end end def self.update_pot(locales_path = GettextSetup.locales_path, path = nil) GettextSetup.initialize_config(locales_path) path ||= pot_file_path if !File.exist? path puts 'No existing POT file, generating new' result = GettextSetup::Pot.generate_new_pot(locales_path: locales_path, target_path: path) puts "POT file #{path} has been generated" if result result else old_pot = path + '.old' File.rename(path, old_pot) result = GettextSetup::Pot.generate_new_pot(locales_path: locales_path, target_path: path) if !result puts 'POT creation failed' result elsif GettextSetup::Pot.string_changes?(old_pot, path) puts 'String changes detected, replacing with updated POT file' File.delete(old_pot) true else puts 'No string changes detected, keeping old POT file' File.rename(old_pot, path) true end end end # @param [:locales_path] opts # The directory for the locales. def self.merge(opts = {}) locales_path = opts[:locales_path] || GettextSetup.locales_path GettextSetup.initialize_config(locales_path) target_filename = GettextSetup.config['project_name'] + '.pot' target_path = File.expand_path(target_filename, locales_path) oldpot_dir = File.expand_path('oldpot', locales_path) oldpot_path = File.expand_path("oldpot/old_#{target_filename}", locales_path) if File.exist? target_path FileUtils.mkdir_p(oldpot_dir) begin FileUtils.cp(target_path, oldpot_path) rescue Errno::ENOENT => e raise "There was a problem creating .pot backup #{oldpot_path}, merge failed: #{e.message}" end puts "Warning - #{target_filename} already exists and will be relocated to oldpot/old_#{target_filename}." end locales_glob = Dir.glob("#{locales_path}/*.pot") cmd = "msgcat #{locales_glob.join(' ')} -o #{target_path}" _, _, _, wait = Open3.popen3(cmd) exitstatus = wait.value raise 'PO files failed to merge' unless exitstatus.success? puts "PO files have been successfully merged, #{target_filename} has been created." exitstatus end end end gettext-setup-0.34/lib/gettext-setup/metadata_pot.rb0000644000004100000410000000161213616243263022677 0ustar www-datawww-data# frozen_string_literal: true require 'erb' require 'json' require 'date' module GettextSetup module MetadataPot def self.metadata_path File.join(GettextSetup.locales_path, GettextSetup.config['project_name'] + '_metadata.pot') end def self.template_path File.join(File.dirname(__FILE__), '../templates/metadata.pot.erb') end def self.metadata(metadata_file = 'metadata.json') if File.exist?(metadata_file) file = File.open(metadata_file) json = file.read JSON.parse(json) else {} end end def self.pot_string(metadata) b = binding.freeze # Uses `metadata` ERB.new(File.read(template_path)).result(b) end def self.generate_metadata_pot(pot_metadata = metadata, path = metadata_path) File.open(path, 'w') do |f| f << pot_string(pot_metadata) end end end end gettext-setup-0.34/lib/gettext-setup/gettext_setup.rb0000644000004100000410000001216713616243263023150 0ustar www-datawww-data# frozen_string_literal: true require 'fast_gettext' require 'yaml' require 'locale' module GettextSetup class NoConfigFoundError < RuntimeError def initialize(path) super("No config.yaml found! (searching: #{path})") end end @config = nil @translation_repositories = {} # `locales_path` should include: # - config.yaml # - a .pot file for the project # - i18n directories for languages, each with a .po file # - if using .mo files, an LC_MESSAGES dir in each language dir, with the .mo file in it # valid `options` fields: # :file_format - one of the supported backends for fast_gettext (e.g. :po, :mo, :yaml, etc.) def self.initialize(locales_path = 'locales', options = {}) GettextSetup.initialize_config(locales_path) # Make the translation methods available everywhere Object.send(:include, FastGettext::Translation) # Define our text domain, and set the path into our root. I would prefer to # have something smarter, but we really want this up earlier even than our # config loading happens so that errors there can be translated. add_repository_to_chain(config['project_name'], options) # 'chain' is the only available multi-domain type in fast_gettext 1.1.0 We should consider # investigating 'merge' once we can bump our dependency FastGettext.add_text_domain('master_domain', type: :chain, chain: @translation_repositories.values) FastGettext.default_text_domain = 'master_domain' # Likewise, be explicit in our default language choice. Available locales # must be set prior to setting the default_locale since default locale must # available. FastGettext.default_available_locales = (FastGettext.default_available_locales || []) | locales FastGettext.default_locale = default_locale Locale.set_default(default_locale) end # Sets up the config class variables. # # Call this without calling initialize when you only need to deal with the # translation files and you don't need runtime translation. def self.initialize_config(locales_path = 'locales') config_path = File.absolute_path('config.yaml', locales_path) File.exist?(config_path) || raise(NoConfigFoundError, config_path) @config = YAML.load_file(config_path)['gettext'] @locales_path = locales_path end def self.config? raise NoConfigFoundError, File.join(locales_path, 'config.yaml') unless @config @config end def self.add_repository_to_chain(project_name, options) repository = FastGettext::TranslationRepository.build(project_name, path: locales_path, type: options[:file_format] || :po, ignore_fuzzy: false) @translation_repositories[project_name] = repository unless @translation_repositories.key? project_name end def self.locales_path @locales_path ||= File.join(Dir.pwd, 'locales') end def self.config @config ||= {} end def self.translation_repositories @translation_repositories end def self.default_locale config['default_locale'] || 'en' end def self.default_locale=(new_locale) FastGettext.default_locale = new_locale Locale.set_default(new_locale) config['default_locale'] = new_locale end # Returns the locale for the current machine. This is most useful for shell # applications that need an ACCEPT-LANGUAGE header set. def self.candidate_locales Locale.candidates(type: :cldr).join(',') end def self.clear Locale.clear end def self.locales explicit = Dir.glob(File.absolute_path('*/*.po', locales_path)).map do |x| File.basename(File.dirname(x)) end ([default_locale] + explicit).uniq end # Given an HTTP Accept-Language header return the locale with the highest # priority from it for which we have a locale available. If none exists, # return the default locale def self.negotiate_locale(accept_header) unless @config raise ArgumentError, 'No config.yaml found! Use ' \ '`GettextSetup.initialize(locales_path)` to locate your config.yaml' end return FastGettext.default_locale if accept_header.nil? available_locales = accept_header.split(',').map do |locale| pair = locale.strip.split(';q=') pair << '1.0' unless pair.size == 2 # Ignore everything but the language itself; that means that we treat # 'de' and 'de-DE' identical, and would use the 'de' message catalog # for both. pair[0] = pair[0].split('-')[0] pair[0] = FastGettext.default_locale if pair[0] == '*' pair end.sort_by do |(_, qvalue)| -1 * qvalue.to_f end.select do |(locale, _)| FastGettext.available_locales.include?(locale) end if available_locales && available_locales.first available_locales.first.first else # We can't satisfy the request preference. Just use the default locale. default_locale end end # Negotiates and sets the locale based on an accept language header. def self.negotiate_locale!(accept_header) FastGettext.locale = negotiate_locale(accept_header) end end gettext-setup-0.34/lib/gettext-setup.rb0000644000004100000410000000020613616243263020233 0ustar www-datawww-data# frozen_string_literal: true require 'gettext-setup/gettext_setup' require 'gettext-setup/metadata_pot' require 'gettext-setup/pot' gettext-setup-0.34/lib/generate_metadata_pot.rb0000644000004100000410000000010313616243263021721 0ustar www-datawww-data# frozen_string_literal: true require 'metadata_pot/metadata_pot' gettext-setup-0.34/locales/0000755000004100000410000000000013616243263015742 5ustar www-datawww-datagettext-setup-0.34/locales/config-sample.yaml0000644000004100000410000000206313616243263021353 0ustar www-datawww-data--- # This is the project-specific configuration file for setting up # fast_gettext for your project. gettext: # This is used for the name of the .pot and .po files; they will be # called .pot? project_name: 'sinatra-i18n' # This is used in comments in the .pot and .po files to indicate what # project the files belong to and should bea little more descriptive than # package_name: Sinatra i18n demo # The locale that the default messages in the .pot file are in default_locale: en # The email used for sending bug reports. bugs_address: docs@puppetlabs.com # The holder of the copyright. copyright_holder: Puppet Labs, LLC. # This determines which comments in code should be eligible for translation. # Any comments that start with this string will be externalized. (Leave # empty to include all.) comments_tag: TRANSLATORS # Patterns for +Dir.glob+ used to find all files that might contain # translatable content, relative to the project root directory source_files: - 'app.rb' - 'lib/**/*.rb' gettext-setup-0.34/gettext-setup.gemspec0000644000004100000410000001323513616243263020513 0ustar www-datawww-data######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: gettext-setup 0.34 ruby lib Gem::Specification.new do |s| s.name = "gettext-setup".freeze s.version = "0.34" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Puppet".freeze] s.date = "2020-01-10" s.description = "A gem to ease i18n".freeze s.email = ["info@puppet.com".freeze] s.files = ["LICENSE".freeze, "README.md".freeze, "lib/generate_metadata_pot.rb".freeze, "lib/gettext-setup.rb".freeze, "lib/gettext-setup/gettext_setup.rb".freeze, "lib/gettext-setup/metadata_pot.rb".freeze, "lib/gettext-setup/pot.rb".freeze, "lib/tasks/gettext.rake".freeze, "lib/templates/metadata.pot.erb".freeze, "locales/config-sample.yaml".freeze, "spec/fixtures/fixture_locales/config.yaml".freeze, "spec/fixtures/fixture_locales/fixture_locales.pot".freeze, "spec/fixtures/fixture_locales/jp/fixture_locales.po".freeze, "spec/fixtures/fixture_locales/test_strings.rb".freeze, "spec/fixtures/locales/config.yaml".freeze, "spec/fixtures/locales/de/sinatra-i18n.po".freeze, "spec/fixtures/locales/sinatra-i18n.pot".freeze, "spec/fixtures/merge_locales/config.yaml".freeze, "spec/fixtures/spec_locales/config.yaml".freeze, "spec/fixtures/spec_locales/sinatra-i18n.pot".freeze, "spec/fixtures/string_changes/add.pot".freeze, "spec/fixtures/string_changes/change.pot".freeze, "spec/fixtures/string_changes/non_string_changes.pot".freeze, "spec/fixtures/string_changes/old.pot".freeze, "spec/fixtures/string_changes/remove.pot".freeze, "spec/fixtures/tmp_locales/config.yaml".freeze, "spec/fixtures/tmp_locales/de/sinatra-i18n.po".freeze, "spec/fixtures/tmp_locales/sinatra-i18n.pot".freeze, "spec/lib/gettext-setup/gettext_setup_spec.rb".freeze, "spec/lib/gettext-setup/metadata_pot_spec.rb".freeze, "spec/lib/gettext-setup/pot_spec.rb".freeze, "spec/lib/tasks/gettext_rake_spec.rb".freeze, "spec/spec_helper.rb".freeze] s.homepage = "https://github.com/puppetlabs/gettext-setup-gem".freeze s.licenses = ["Apache-2.0".freeze] s.required_ruby_version = Gem::Requirement.new(">= 1.9.2".freeze) s.rubygems_version = "2.5.2.1".freeze s.summary = "A gem to ease internationalization with fast_gettext".freeze s.test_files = ["spec/fixtures/fixture_locales/config.yaml".freeze, "spec/fixtures/fixture_locales/fixture_locales.pot".freeze, "spec/fixtures/fixture_locales/jp/fixture_locales.po".freeze, "spec/fixtures/fixture_locales/test_strings.rb".freeze, "spec/fixtures/locales/config.yaml".freeze, "spec/fixtures/locales/de/sinatra-i18n.po".freeze, "spec/fixtures/locales/sinatra-i18n.pot".freeze, "spec/fixtures/merge_locales/config.yaml".freeze, "spec/fixtures/spec_locales/config.yaml".freeze, "spec/fixtures/spec_locales/sinatra-i18n.pot".freeze, "spec/fixtures/string_changes/add.pot".freeze, "spec/fixtures/string_changes/change.pot".freeze, "spec/fixtures/string_changes/non_string_changes.pot".freeze, "spec/fixtures/string_changes/old.pot".freeze, "spec/fixtures/string_changes/remove.pot".freeze, "spec/fixtures/tmp_locales/config.yaml".freeze, "spec/fixtures/tmp_locales/de/sinatra-i18n.po".freeze, "spec/fixtures/tmp_locales/sinatra-i18n.pot".freeze, "spec/lib/gettext-setup/gettext_setup_spec.rb".freeze, "spec/lib/gettext-setup/metadata_pot_spec.rb".freeze, "spec/lib/gettext-setup/pot_spec.rb".freeze, "spec/lib/tasks/gettext_rake_spec.rb".freeze, "spec/spec_helper.rb".freeze] if s.respond_to? :specification_version then s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q.freeze, [">= 0"]) s.add_runtime_dependency(%q.freeze, ["~> 1.1.0"]) s.add_runtime_dependency(%q.freeze, ["< 3.3.0", ">= 3.0.2"]) s.add_runtime_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, ["~> 3.1"]) s.add_development_dependency(%q.freeze, ["~> 3.1"]) s.add_development_dependency(%q.freeze, ["~> 3.1"]) s.add_development_dependency(%q.freeze, ["~> 3.1"]) s.add_development_dependency(%q.freeze, [">= 0"]) s.add_development_dependency(%q.freeze, [">= 0"]) else s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 1.1.0"]) s.add_dependency(%q.freeze, ["< 3.3.0", ">= 3.0.2"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 3.1"]) s.add_dependency(%q.freeze, ["~> 3.1"]) s.add_dependency(%q.freeze, ["~> 3.1"]) s.add_dependency(%q.freeze, ["~> 3.1"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) end else s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 1.1.0"]) s.add_dependency(%q.freeze, ["< 3.3.0", ">= 3.0.2"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, ["~> 3.1"]) s.add_dependency(%q.freeze, ["~> 3.1"]) s.add_dependency(%q.freeze, ["~> 3.1"]) s.add_dependency(%q.freeze, ["~> 3.1"]) s.add_dependency(%q.freeze, [">= 0"]) s.add_dependency(%q.freeze, [">= 0"]) end end