gettext-setup 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +18 -0
- data/LICENSE +17 -0
- data/README.md +105 -0
- data/Rakefile +5 -0
- data/gettext-setup.gemspec +21 -0
- data/lib/gettext-setup.rb +1 -0
- data/lib/gettext-setup/gettext_setup.rb +82 -0
- data/lib/tasks/gettext.rake +66 -0
- data/locales/config-sample.yaml +22 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8cb929527d234a142edba900b86ff9b1e2a8543d
|
4
|
+
data.tar.gz: cdb14138a6bcc08bad38ac0acf373a3e53f36e97
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: eb0462b300efe1a2e63239f849e4cc3cc228ea03faed2747570dac0cec5107c85999115dd21e6445ca36b39a4bb81069cdb63ac79a578d0757bc143d6fb84299
|
7
|
+
data.tar.gz: 733995b80a2dd5865746ec4e2d48bf8a7b0071b6d04686df69938e753763deed50ce74388c13e793f2acab1c71fa964cdf9e4410842aabcc72ba7d41e724be55
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
fast_gettext (1.0.0)
|
5
|
+
gettext (3.2.1)
|
6
|
+
locale (>= 2.0.5)
|
7
|
+
text (>= 1.3.0)
|
8
|
+
locale (2.1.2)
|
9
|
+
rake (10.4.2)
|
10
|
+
text (1.3.1)
|
11
|
+
|
12
|
+
PLATFORMS
|
13
|
+
ruby
|
14
|
+
|
15
|
+
DEPENDENCIES
|
16
|
+
fast_gettext
|
17
|
+
gettext (>= 3.0.2)
|
18
|
+
rake
|
data/LICENSE
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
gettext-setup gem
|
2
|
+
|
3
|
+
Copyright (C) 2016 Puppet, Inc.
|
4
|
+
|
5
|
+
Puppet, Inc. can be contacted at: info@puppet.com
|
6
|
+
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
you may not use this file except in compliance with the License.
|
9
|
+
You may obtain a copy of the License at
|
10
|
+
|
11
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
See the License for the specific language governing permissions and
|
17
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# gettext-setup gem
|
2
|
+
|
3
|
+
This is a simple gem to set up i18n for Ruby projects (including [Sinatra](www.sinatrarb.com/) web apps) using gettext and fast gettext.
|
4
|
+
|
5
|
+
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.
|
6
|
+
|
7
|
+
## Terminology
|
8
|
+
|
9
|
+
**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)).
|
10
|
+
|
11
|
+
**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.
|
12
|
+
|
13
|
+
**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.
|
14
|
+
|
15
|
+
**Transifex** - A translation management system. When PO files are updated, the updates are pulled into Transifex and translated there.
|
16
|
+
|
17
|
+
## Setup for your project
|
18
|
+
|
19
|
+
These are the poingant bits of this example that you need to replicate in
|
20
|
+
your project:
|
21
|
+
|
22
|
+
1. Add `gem 'gettext-setup'` to your `Gemfile`.
|
23
|
+
1. Add `gem 'fast_gettext'` to your `Gemfile`. This line is only needed
|
24
|
+
for running the bundled `rake` task.
|
25
|
+
1. Copy `locales/config-sample.yaml` to your project and put it into a
|
26
|
+
`locales` directory as `config.yaml`.
|
27
|
+
1. Edit `locales/config.yaml` and make the necessary changes for your
|
28
|
+
project
|
29
|
+
1. Add these three lines to your `Rakefile`:
|
30
|
+
```
|
31
|
+
spec = Gem::Specification.find_by_name 'gettext-setup'
|
32
|
+
load "#{spec.gem_dir}/lib/tasks/gettext.rake"
|
33
|
+
GettextSetup.initialize(File.absolute_path('locales', File.dirname(__FILE__)))
|
34
|
+
```
|
35
|
+
1. Add this line to the top of your `app.rb`:
|
36
|
+
`require 'gettext-setup'`
|
37
|
+
1. Add these lines inside the class declared in your `app.rb`:
|
38
|
+
```
|
39
|
+
include FastGettext::Translation
|
40
|
+
GettextSetup.initialize(File.absolute_path('locales', File.dirname(__FILE__)))
|
41
|
+
before do
|
42
|
+
FastGettext.locale = GettextSetup.negotiate_locale(env["HTTP_ACCEPT_LANGUAGE"])
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
## Writing translatable code
|
47
|
+
|
48
|
+
### Use full sentences
|
49
|
+
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).
|
50
|
+
|
51
|
+
### Use the translation function _()
|
52
|
+
Wrap user-facing strings in the `_()` function so they can be externalized to a POT file.
|
53
|
+
|
54
|
+
E.g. `_("Hello, world!")`
|
55
|
+
|
56
|
+
### Interpolation
|
57
|
+
To add dynamic data to a string, use the following string formatting and translation function:
|
58
|
+
|
59
|
+
`_("We negotiated a locale of %{locale}") % {locale: FastGettext.locale}`
|
60
|
+
|
61
|
+
### Pluralize with the n_() function
|
62
|
+
|
63
|
+
Wrap strings that include pluralization with the `n_()` function.
|
64
|
+
|
65
|
+
E.g. `n_("There is %{count} bicycle in %{city}", "There are %{count} bicycles in %{city}", num_bikes) % {count: num_bikes, city: "Beijing"},`
|
66
|
+
|
67
|
+
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.
|
68
|
+
|
69
|
+
Plurals are selected from the PO file by index. Here's an example of how a
|
70
|
+
pluralized string is handled in a PO file:
|
71
|
+
|
72
|
+
`msgid "%{count} file"
|
73
|
+
`msgid_plural "%{count} files"
|
74
|
+
`msgstr[0] "%{count} Dateien"
|
75
|
+
`msgstr[1] "%{count} Datei"
|
76
|
+
|
77
|
+
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.
|
78
|
+
|
79
|
+
The `msgid_plural` is the plural version of the English source string.
|
80
|
+
|
81
|
+
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).
|
82
|
+
|
83
|
+
### Comments
|
84
|
+
To provide translators with some contextual information or instructions about a string, precede the string with a comment. Start the comment with "TRANSLATOR: " to make it obvious that you are providing instructions for the translator. The comment gets pulled in to the POT file and will show up as a comment in Transifex.
|
85
|
+
|
86
|
+
E.g. `# TRANSLATOR: The placeholder in this string represents the name of a parameter.`
|
87
|
+
|
88
|
+
## Translation workflow
|
89
|
+
|
90
|
+
1. Wrap the translation function around translatable strings in code files
|
91
|
+
|
92
|
+
2. Run `rake gettext:pot` to parse the source code and extract translatable strings into the message catalog in `locales/<project_name>.pot`. If a POT file already exists, this rake task will update the POT file. Do this before making a pull request that includes changes to user-facing strings.
|
93
|
+
|
94
|
+
3. Run `rake gettext:po[<lang>]` to create/update language-specific PO files. This step will be managed by the localization team, and will usually happen prior to a new release.
|
95
|
+
|
96
|
+
4. When a PO file is updated, a git hook is used to automatically pull the new/updated strings into Transifex ready for translation.
|
97
|
+
|
98
|
+
5. When the PO file reaches 100% translated and reviewed in Transifex, it is pulled back into the locale folder.
|
99
|
+
|
100
|
+
6. PE checks the user's locale settings (the browser settings for web apps, or the system settings for the CLI). If we support the preferred locale, PE uses the PO file for that locale. Otherwise, it uses the default locale (en_US).
|
101
|
+
|
102
|
+
## TODO
|
103
|
+
|
104
|
+
1. Locale-specific formatting of numbers, dates, etc.
|
105
|
+
2. Separate out the locales that are supported for testing/dev and production so we can add test translations without shipping them.
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "gettext-setup"
|
7
|
+
spec.version = "0.1"
|
8
|
+
spec.authors = ["Puppet"]
|
9
|
+
spec.email = ["info@puppet.com"]
|
10
|
+
spec.description = "A gem to ease i18n"
|
11
|
+
spec.summary = "A gem to ease internationalization with fast_gettext"
|
12
|
+
spec.homepage = "https://github.com/puppetlabs/gettext-setup-gem"
|
13
|
+
spec.license = "ASL2"
|
14
|
+
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.test_files = spec.files.grep(%r{^spec/})
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
20
|
+
spec.add_development_dependency "rake"
|
21
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'gettext-setup/gettext_setup'
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'fast_gettext'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module GettextSetup
|
6
|
+
@@config = nil
|
7
|
+
|
8
|
+
# `locales_path` should include:
|
9
|
+
# - config.yaml
|
10
|
+
# - a .pot file for the project
|
11
|
+
# - i18n directories for languages, each with a .po file
|
12
|
+
def self.initialize(locales_path)
|
13
|
+
config_path = File.absolute_path('config.yaml', locales_path)
|
14
|
+
@@config = YAML.load_file(config_path)['gettext']
|
15
|
+
@@locales_path = locales_path
|
16
|
+
|
17
|
+
# Make the translation methods available everywhere
|
18
|
+
Object.send(:include, FastGettext::Translation)
|
19
|
+
|
20
|
+
# Define our text domain, and set the path into our root. I would prefer to
|
21
|
+
# have something smarter, but we really want this up earlier even than our
|
22
|
+
# config loading happens so that errors there can be translated.
|
23
|
+
#
|
24
|
+
# We use the PO files directly, since it works about as efficiently with
|
25
|
+
# fast_gettext, and avoids all the extra overhead of compilation down to
|
26
|
+
# machine format, etc.
|
27
|
+
FastGettext.add_text_domain(config['project_name'],
|
28
|
+
:path => locales_path,
|
29
|
+
:type => :po,
|
30
|
+
:ignore_fuzzy => false)
|
31
|
+
FastGettext.default_text_domain = config['project_name']
|
32
|
+
|
33
|
+
# Likewise, be explicit in our default language choice.
|
34
|
+
FastGettext.default_locale = default_locale
|
35
|
+
FastGettext.default_available_locales = locales
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.locales_path
|
39
|
+
@@locales_path
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.config
|
43
|
+
@@config
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.default_locale
|
47
|
+
config['default_locale'] || "en"
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.locales
|
51
|
+
explicit = Dir.glob(File::absolute_path('*/*.po', locales_path)).map do |x|
|
52
|
+
File::basename(File::dirname(x))
|
53
|
+
end
|
54
|
+
(explicit + [ default_locale]).uniq
|
55
|
+
end
|
56
|
+
|
57
|
+
# Given an HTTP Accept-Language header return the locale with the highest
|
58
|
+
# priority from it for which we have a locale available. If none exists,
|
59
|
+
# return the default locale
|
60
|
+
def self.negotiate_locale(accept_header)
|
61
|
+
unless @@config
|
62
|
+
raise ArgumentError, "No config.yaml found! Use `GettextSetup.initialize(locales_path)` to locate your config.yaml"
|
63
|
+
end
|
64
|
+
return FastGettext.default_locale if accept_header.nil?
|
65
|
+
available_locales = accept_header.split(",").map do |locale|
|
66
|
+
pair = locale.strip.split(';q=')
|
67
|
+
pair << '1.0' unless pair.size == 2
|
68
|
+
pair[0] = FastGettext.default_locale if pair[0] == '*'
|
69
|
+
pair
|
70
|
+
end.sort_by do |(locale,qvalue)|
|
71
|
+
qvalue.to_f
|
72
|
+
end.select do |(locale,_)|
|
73
|
+
FastGettext.available_locales.include?(locale)
|
74
|
+
end
|
75
|
+
if available_locales and available_locales.last
|
76
|
+
available_locales.last.first
|
77
|
+
else
|
78
|
+
# We can't satisfy the request preference. Just use the default locale.
|
79
|
+
default_locale
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# require 'bundler'
|
2
|
+
# puts File.absolute_path('Gemfile', Dir.pwd)
|
3
|
+
# Bundler.read_file(File.absolute_path('Gemfile', Dir.pwd))
|
4
|
+
#
|
5
|
+
require_relative '../gettext-setup/gettext_setup'
|
6
|
+
#
|
7
|
+
# GettextSetup.initialize(File.absolute_path('locales', Dir.pwd))
|
8
|
+
|
9
|
+
namespace :gettext do
|
10
|
+
|
11
|
+
def locale_path
|
12
|
+
GettextSetup.locales_path
|
13
|
+
end
|
14
|
+
|
15
|
+
def text_domain
|
16
|
+
FastGettext.text_domain
|
17
|
+
end
|
18
|
+
|
19
|
+
def files_to_translate
|
20
|
+
GettextSetup.config['source_files'].map do |p|
|
21
|
+
Dir.glob(p)
|
22
|
+
end.flatten
|
23
|
+
end
|
24
|
+
|
25
|
+
def pot_file_path
|
26
|
+
File.join(locale_path, GettextSetup.config['project_name'] + ".pot")
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Update pot files"
|
30
|
+
task :pot do
|
31
|
+
package_name = GettextSetup.config['package_name']
|
32
|
+
project_name = GettextSetup.config['project_name']
|
33
|
+
bugs_address = GettextSetup.config['bugs_address']
|
34
|
+
copyright_holder = GettextSetup.config['copyright_holder']
|
35
|
+
version=`git describe`
|
36
|
+
system("rxgettext -o locales/#{project_name}.pot --no-wrap --sort-by-file " +
|
37
|
+
"--add-comments --msgid-bugs-address '#{bugs_address}' " +
|
38
|
+
"--package-name '#{package_name}' " +
|
39
|
+
"--package-version '#{version}' " +
|
40
|
+
"--copyright-holder='#{copyright_holder}' --copyright-year=#{Time.now.year} " +
|
41
|
+
"#{files_to_translate.join(" ")}")
|
42
|
+
puts "POT file locales/#{project_name}.pot has been updated"
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Update po file for a specific language"
|
46
|
+
task :po, [:language] do |_, args|
|
47
|
+
language = args.language || ENV["LANGUAGE"]
|
48
|
+
|
49
|
+
# Let's do some pre-verification of the environment.
|
50
|
+
if language.nil?
|
51
|
+
puts "You need to specify the language to add. Either 'LANGUAGE=eo rake gettext:po' or 'rake gettext:po[LANGUAGE]'"
|
52
|
+
next
|
53
|
+
end
|
54
|
+
|
55
|
+
language_path = File.join(locale_path, language)
|
56
|
+
mkdir_p(language_path)
|
57
|
+
|
58
|
+
po_file_path = File.join(language_path,
|
59
|
+
GettextSetup.config['project_name'] + ".po")
|
60
|
+
if File.exists?(po_file_path)
|
61
|
+
system("msgmerge -U #{po_file_path} #{pot_file_path}")
|
62
|
+
else
|
63
|
+
system("msginit --no-translator -l #{language} -o #{po_file_path} -i #{pot_file_path}")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
---
|
2
|
+
# This is the project-specific configuration file for setting up
|
3
|
+
# fast_gettext for your project.
|
4
|
+
gettext:
|
5
|
+
# This is used for the name of the .pot and .po files; they will be
|
6
|
+
# called <project_name>.pot?
|
7
|
+
project_name: 'sinatra-i18n'
|
8
|
+
# This is used in comments in the .pot and .po files to indicate what
|
9
|
+
# project the files belong to and should bea little more desctiptive than
|
10
|
+
# <project_name>
|
11
|
+
package_name: Sinatra i18n demo
|
12
|
+
# The locale that the default messages in the .pot file are in
|
13
|
+
default_locale: en
|
14
|
+
# The email used for sending bug reports.
|
15
|
+
bugs_address: docs@puppetlabs.com
|
16
|
+
# The holder of the copyright.
|
17
|
+
copyright_holder: Puppet Labs, LLC.
|
18
|
+
# Patterns for +Dir.glob+ used to find all files that might contain
|
19
|
+
# translatable content, relative to the project root directory
|
20
|
+
source_files:
|
21
|
+
- 'app.rb'
|
22
|
+
- 'lib/**/*.rb'
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gettext-setup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Puppet
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-06-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ~>
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '1.3'
|
19
|
+
name: bundler
|
20
|
+
prerelease: false
|
21
|
+
type: :development
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
name: rake
|
34
|
+
prerelease: false
|
35
|
+
type: :development
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: A gem to ease i18n
|
42
|
+
email:
|
43
|
+
- info@puppet.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- Gemfile
|
49
|
+
- Gemfile.lock
|
50
|
+
- LICENSE
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- gettext-setup.gemspec
|
54
|
+
- lib/gettext-setup.rb
|
55
|
+
- lib/gettext-setup/gettext_setup.rb
|
56
|
+
- lib/tasks/gettext.rake
|
57
|
+
- locales/config-sample.yaml
|
58
|
+
homepage: https://github.com/puppetlabs/gettext-setup-gem
|
59
|
+
licenses:
|
60
|
+
- ASL2
|
61
|
+
metadata: {}
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 2.4.4
|
79
|
+
signing_key:
|
80
|
+
specification_version: 4
|
81
|
+
summary: A gem to ease internationalization with fast_gettext
|
82
|
+
test_files: []
|
83
|
+
has_rdoc:
|